I thought I had written about this a long time ago, as Math.SetRoundMode
(and now System.Math.SetRoundMode
) has been introduced since at least Delphi 2007. There are also related GetRoundMode
and TFPURoundingMode
.
Delphi 2009 also introduced TFPUPrecisionMode
, GetPrecisionMode
and SetPrecisionMode
.
Delphi 2010 also introduced TFPUException
, TFPUExceptionMask
, TFPUPrecisionMode
, TFPURoundingMode
, ClearExceptions
, GetExceptionMask
, and SetExceptionMask
.
Delphi XE2 introduced namespaces, so prepended the unit Math
with the namespace System.
to become unit System.Math
. It also introduced $EXCESSPRECISION
(for x64), GetMXCSR
, SetMXCSR
, ResetMXCSR
, ClearFPUExceptions
, TSSEException
, TSSEExceptionMask
, GetSSEExceptionMask
, SetSSEExceptionMask
, ClearSSEExceptions
, TSSERoundingMode
, GetSSERoundMode
, SetSSERoundMode
.
The documentation basically only had formatting changes. This is the most important part of SetRoundMode
:
function SetRoundMode(const RoundMode: TFPURoundingMode): TFPURoundingMode;…
Call
SetRoundingMode
to specify how the FPU handles rounding issues. The rounding mode can be any of the following values:
Value MeaningrmNearest
Rounds to the closest value.rmDown
Rounds toward negative infinity.rmUp
Rounds toward positive infinity.rmTruncate
Truncates the value, rounding positive numbers down and negative numbers up.
This is the most important bit of SetPrecisionMode
:
function SetPrecisionMode(const Precision: TFPUPrecisionMode): TFPUPrecisionMode;…
Call
SetPrecisionMode
to specify the level of precision that the FPU (floating-point unit) uses for floating-point calculations. The precision control mode can be any of the following values:
Value MeaningpmSingle
single precisionpmReserved
not usedpmDouble
double precisionpmExtended
extended precision
SetPrecisionMode
returns the previous precision control mode.
Both are functions, so so they return the previous value which you can use to set it back later: this is important as it is a process wide. SetPrecisionMode
documents this, but SetRoundMode
does not!
Global side effects and avoiding them
Since SetRoundMode
and SetPrecisionMode
set the 8087 CW (and on XE2 and up SetMXCSR
), it means all usual Set8087CW
caveats apply: changing the global 8087 CW setting impacts all running code, which means that threads, DLLs (especially ones written in other languages), etc might malfunction. You can find some of the caveat effects on my bog by searching for [Wayback] Set8087CW site:wiert.me – Google Search.
If you want to avoid global side effects, you might try the solution in [Wayback] c# – What is the equivalent of Math.Round() with MidpointRounding.AwayFromZero in Delphi? – Stack Overflow by [Wayback] Andreas Rejbrand.
On MXCSR
[Wayback/Archive.is] SSE Instruction Set (not sure why Embarcadero used this as reference material from their docwiki, but hey, Intel documentation is likely outdated):
SSE — MXCSR
The
MXCSR
register is a 32-bit register containing flags for control and status information regarding SSE instructions. As of SSE3, only bits 0-15 have been defined.
Pnemonic Bit Location Description FZ bit 15 Flush To Zero R+ bit 14 Round Positive R- bit 13 Round Negative RZ bits 13 and 14 Round To Zero RN bits 13 and 14 are 0 Round To Nearest PM bit 12 Precision Mask UM bit 11 Underflow Mask OM bit 10 Overflow Mask ZM bit 9 Divide By Zero Mask DM bit 8 Denormal Mask IM bit 7 Invalid Operation Mask DAZ bit 6 Denormals Are Zero PE bit 5 Precision Flag UE bit 4 Underflow Flag OE bit 3 Overflow Flag ZE bit 2 Divide By Zero Flag DE bit 1 Denormal Flag IE bit 0 Invalid Operation Flag
FZ
mode causes all underflowing operations to simply go to zero. This saves some processing time, but loses precision.The
R+
,R-
,RN
, andRZ
rounding modes determine how the lowest bit is generated. Normally,RN
is used.
PM
,UM
,MM
,ZM
,DM
, andIM
are masks that tell the processor to ignore the exceptions that happen, if they do. This keeps the program from having to deal with problems, but might cause invalid results.
DAZ
tells the CPU to force all Denormals to zero. A Denormal is a number that is so small that FPU can’t renormalize it due to limited exponent ranges. They’re just like normal numbers, but they take considerably longer to process. Note that not all processors supportDAZ
.
PE
,UE
,ME
,ZE
,DE
, andIE
are the exception flags that are set if they happen, and aren’t unmasked. Programs can check these to see if something interesting happened. These bits are “sticky”, which means that once they’re set, they stay set forever until the program clears them. This means that the indicated exception could have happened several operations ago, but nobody bothered to clear it.
DAZ
wasn’t available in the first version of SSE. Since setting a reserved bit inMXCSR
causes a general protection fault, we need to be able to check the availability of this feature without causing problems. To do this, one needs to set up a 512-byte area of memory to save the SSE state to, usingfxsave
, and then one needs to inspect bytes 28 through 31 for theMXCSR_MASK
value. If bit 6 is set,DAZ
is supported, otherwise, it isn’t.
Architectures Software Developer’s Manual: Intel® 64 and IA-32 includes supporting processors programming environment and architecture.
[Wayback] Intel® 64 and IA-32 Architectures Developer’s Manual: Vol. 1
[Wayback]
Documentation links
Some of the documentation links (that regrettably do not explain what happens behind the scenes with the 8087 CW) are these:
- Delphi 2007:
- Delphi 2009:
- Delphi 2010:
- [Wayback] Math.TFPUException – RAD Studio VCL Reference
- [Wayback] Math.TFPUExceptionMask – RAD Studio VCL Reference
- [Wayback] Math.TFPUPrecisionMode – RAD Studio VCL Reference
- [Wayback] Math.TFPURoundingMode – RAD Studio VCL Reference
- [Wayback] Math.ClearExceptions – RAD Studio VCL Reference
- [Wayback] Math.GetExceptionMask – RAD Studio VCL Reference
- [Wayback] Math.SetExceptionMask – RAD Studio VCL Reference
- [Wayback] Math.GetPrecisionMode – RAD Studio VCL Reference
- [Wayback] Math.SetPrecisionMode – RAD Studio VCL Reference
- [Wayback] Math.GetRoundMode – RAD Studio VCL Reference
- [Wayback] Math.SetRoundMode – RAD Studio VCL Reference
- Delphi XE:
- [Wayback] Math.TFPUException – XE API Documentation
- [Wayback] Math.TFPUExceptionMask – XE API Documentation
- [Wayback] Math.TFPUPrecisionMode – XE API Documentation
- [Wayback] Math.TFPURoundingMode – XE API Documentation
- [Wayback] Math.ClearExceptions – XE API Documentation
- [Wayback] Math.GetExceptionMask – XE API Documentation
- [Wayback] Math.SetExceptionMask – XE API Documentation
- [Wayback] Math.GetPrecisionMode – XE API Documentation
- [Wayback] Math.SetPrecisionMode – XE API Documentation
- [Wayback] Math.GetRoundMode – XE API Documentation
- [Wayback] Math.SetRoundMode – XE API Documentation
- Delphi XE2: the
System
namespace got prepended, x64 support added and a rudimentary page [Wayback] Floating-Point Rounding Issues – RAD Studio XE2 added- [Wayback] Floating point precision control (Delphi for x64) – RAD Studio XE2:
$EXCESSPRECISION
- [Wayback] System.GetMXCSR – XE2 API Documentation
- [Wayback] System.SetMXCSR – XE2 API Documentation
- [Wayback] System.ResetMXCSR – XE2 API Documentation
- [Wayback] System.Math.TFPUException – XE2 API Documentation
- [Wayback] System.Math.TFPUExceptionMask – XE2 API Documentation
- [Wayback] System.Math.TFPUPrecisionMode – XE2 API Documentation
- [Wayback] System.Math.TFPURoundingMode – XE2 API Documentation
- [Wayback] System.Math.TSSEException – XE2 API Documentation
- [Wayback] System.Math.TSSEExceptionMask – XE2 API Documentation
- There is no
TSSEPrecisionMode
- [Wayback] System.Math.TSSERoundingMode – XE2 API Documentation
- [Wayback] System.Math.ClearExceptions – XE2 API Documentation
- [Wayback] System.Math.GetExceptionMask – XE2 API Documentation
- [Wayback] System.Math.SetExceptionMask – XE2 API Documentation
- [Wayback] System.Math.ClearFPUExceptions – XE2 API Documentation
- [Wayback] System.Math.GetFPUExceptionMask – XE2 API Documentation
- [Wayback] System.Math.SetFPUExceptionMask – XE2 API Documentation
- [Wayback] System.Math.ClearSSEExceptions – XE2 API Documentation
- [Wayback] System.Math.GetSSEExceptionMask – XE2 API Documentation
- [Wayback] System.Math.SetSSEExceptionMask – XE2 API Documentation
- There is no
SetSSEPrecisionMode
- There is no
GetSSEPrecisionMode
- [Wayback] System.Math.GetSSERoundMode – XE2 API Documentation
- [Wayback] System.Math.SetSSERoundMode – XE2 API Documentation
- [Wayback] System.Math.GetPrecisionMode – XE2 API Documentation
- [Wayback] System.Math.SetPrecisionMode – XE2 API Documentation
- [Wayback] System.Math.GetRoundMode – XE2 API Documentation
- [Wayback] System.Math.SetRoundMode – XE2 API Documentation
- [Wayback] Floating point precision control (Delphi for x64) – RAD Studio XE2:
–jeroen