Coming in Delphi 12: Disabled Floating-Point Exceptions
Delphi 12 is in the works. One of its small features that will have a huge impact is a change in the default handling of floating-point exceptions, which will now be masked on all platforms.
Previously, floating-point exception handling was different across platforms, and most notably on the Windows platform: In VCL and console applications, floating-point exceptions were not masked and would raise an exception. On the other hand, FireMonkey applications had all floating-exceptions masked by default, regardless of which platform they were running on.
Basically if you haven't explicitly changed the exception mask in your code to
some value other than the default, and you had code that attempted to divide
some number with zero, you would get an
EZeroDivide exception in previous
Delphi versions on the Windows VCL application.
In Delphi 12, there is no exception, and the result of such a division will be
So the following code will no longer raise an exception. If you were relying on exceptions being raised, you will have to either set the exception mask to the old value at application start, or adapt your code.
var d, z: Double; begin z := 0; d := 5 / z; end;
Division by zero is just one of the floating-point operations that is affected by this change. This also affects the behavior of various mathematical functions like: square root, trigonometric, and transcendental functions.
Why this change?
One of the reasons for this change is unifying floating-point exception handling across different platforms.
But the real reason behind this change is that the Delphi way of handling floating-point exceptions and having exceptions unmasked (which has been like that since initial Delphi version) is itself—forgive the pun—an exception and not the norm. This created a problem when interacting with various OSes and other APIs, as well as integrating libraries written in other languages and constantly switching between masked and unmasked exceptions depending on which code you are calling.
Having masked exceptions allows more seamless integration with 3rd-party code, and fewer headaches during the development process.
FPCR thread safety
There is another extremely important side-effect of this change. The methods for handling FPCR in Delphi are not thread-safe. This would cause issues in multi-threaded applications while switching between masked and unmasked exceptions.
Unfortunately, those FPCR methods are still not thread-safe in Delphi 12. But, because floating-point exceptions are now masked by default and this was the single most common reason for calling those thread-unsafe FPCR methods, the main source of threading issues has now been resolved.
Even the code that does call
SetExceptionMask to change default value to and
from masked exceptions will now work without issues, as switching from masked
exceptions to masked exceptions (in other words not changing anything) will no longer cause thread-safety issues.
If you do that in your own code, you will be able to safely remove such calls, as they will no longer do anything meaningful and will be just wasting CPU cycles from now on.
Of course, if you want to change the new default value and unmask floating-point exceptions, you will still need to pay more attention to FPCR thread safety. This is also valid for any other usages of thread-unsafe FPCR methods that may exist in your code.
Note: This blog post is based on a pre-release version of the RAD Studio software and it has been written with specific permission by Embarcadero. No feature is committed until the product GA release.