d: Merge upstream dmd ff57fec515, druntime ff57fec515, phobos 17bafda79.
[official-gcc.git] / libphobos / src / std / math / hardware.d
blobcb6cb87845c8c7dbb1469e985e93af90f8ee0c41
1 // Written in the D programming language.
3 /**
4 This is a submodule of $(MREF std, math).
6 It contains hardware support for floating point numbers.
8 Copyright: Copyright The D Language Foundation 2000 - 2011.
9 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
10 Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston,
11 Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger
12 Source: $(PHOBOSSRC std/math/hardware.d)
15 /* NOTE: This file has been patched from the original DMD distribution to
16 * work with the GDC compiler.
18 module std.math.hardware;
20 static import core.stdc.fenv;
22 version (X86) version = X86_Any;
23 version (X86_64) version = X86_Any;
24 version (PPC) version = PPC_Any;
25 version (PPC64) version = PPC_Any;
26 version (MIPS32) version = MIPS_Any;
27 version (MIPS64) version = MIPS_Any;
28 version (AArch64) version = ARM_Any;
29 version (ARM) version = ARM_Any;
30 version (S390) version = IBMZ_Any;
31 version (SPARC) version = SPARC_Any;
32 version (SPARC64) version = SPARC_Any;
33 version (SystemZ) version = IBMZ_Any;
34 version (RISCV32) version = RISCV_Any;
35 version (RISCV64) version = RISCV_Any;
36 version (LoongArch64) version = LoongArch_Any;
38 version (D_InlineAsm_X86) version = InlineAsm_X86_Any;
39 version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any;
41 version (X86_64) version = StaticallyHaveSSE;
42 version (X86) version (OSX) version = StaticallyHaveSSE;
44 version (StaticallyHaveSSE)
46 private enum bool haveSSE = true;
48 else version (X86)
50 static import core.cpuid;
51 private alias haveSSE = core.cpuid.sse;
54 version (D_SoftFloat)
56 // Some soft float implementations may support IEEE floating flags.
57 // The implementation here supports hardware flags only and is so currently
58 // only available for supported targets.
60 else version (X86_Any) version = IeeeFlagsSupport;
61 else version (PPC_Any) version = IeeeFlagsSupport;
62 else version (RISCV_Any) version = IeeeFlagsSupport;
63 else version (MIPS_Any) version = IeeeFlagsSupport;
64 else version (LoongArch_Any) version = IeeeFlagsSupport;
65 else version (ARM_Any) version = IeeeFlagsSupport;
67 // Struct FloatingPointControl is only available if hardware FP units are available.
68 version (D_HardFloat)
70 // FloatingPointControl.clearExceptions() depends on version IeeeFlagsSupport
71 version (IeeeFlagsSupport) version = FloatingPointControlSupport;
74 version (IeeeFlagsSupport)
77 /** IEEE exception status flags ('sticky bits')
79 These flags indicate that an exceptional floating-point condition has occurred.
80 They indicate that a NaN or an infinity has been generated, that a result
81 is inexact, or that a signalling NaN has been encountered. If floating-point
82 exceptions are enabled (unmasked), a hardware exception will be generated
83 instead of setting these flags.
85 struct IeeeFlags
87 nothrow @nogc:
89 private:
90 // The x87 FPU status register is 16 bits.
91 // The Pentium SSE2 status register is 32 bits.
92 // The ARM and PowerPC FPSCR is a 32-bit register.
93 // The SPARC FSR is a 32bit register (64 bits for SPARC 7 & 8, but high bits are uninteresting).
94 // The RISC-V (32 & 64 bit) fcsr is 32-bit register.
95 // THe LoongArch fcsr (fcsr0) is a 32-bit register.
96 uint flags;
98 version (CRuntime_Microsoft)
100 // Microsoft uses hardware-incompatible custom constants in fenv.h (core.stdc.fenv).
101 // Applies to both x87 status word (16 bits) and SSE2 status word(32 bits).
102 enum : int
104 INEXACT_MASK = 0x20,
105 UNDERFLOW_MASK = 0x10,
106 OVERFLOW_MASK = 0x08,
107 DIVBYZERO_MASK = 0x04,
108 INVALID_MASK = 0x01,
110 EXCEPTIONS_MASK = 0b11_1111
112 // Don't bother about subnormals, they are not supported on most CPUs.
113 // SUBNORMAL_MASK = 0x02;
115 else
117 enum : int
119 INEXACT_MASK = core.stdc.fenv.FE_INEXACT,
120 UNDERFLOW_MASK = core.stdc.fenv.FE_UNDERFLOW,
121 OVERFLOW_MASK = core.stdc.fenv.FE_OVERFLOW,
122 DIVBYZERO_MASK = core.stdc.fenv.FE_DIVBYZERO,
123 INVALID_MASK = core.stdc.fenv.FE_INVALID,
124 EXCEPTIONS_MASK = core.stdc.fenv.FE_ALL_EXCEPT,
128 static uint getIeeeFlags() @trusted pure
130 version (GNU)
132 version (X86_Any)
134 ushort sw;
135 asm pure nothrow @nogc
137 "fstsw %0" : "=a" (sw);
139 // OR the result with the SSE2 status register (MXCSR).
140 if (haveSSE)
142 uint mxcsr;
143 asm pure nothrow @nogc
145 "stmxcsr %0" : "=m" (mxcsr);
147 return (sw | mxcsr) & EXCEPTIONS_MASK;
149 else
150 return sw & EXCEPTIONS_MASK;
152 else version (ARM)
154 version (ARM_SoftFloat)
155 return 0;
156 else
158 uint result = void;
159 asm pure nothrow @nogc
161 "vmrs %0, FPSCR; and %0, %0, #0x1F;" : "=r" (result);
163 return result;
166 else version (RISCV_Any)
168 version (D_SoftFloat)
169 return 0;
170 else
172 uint result = void;
173 asm pure nothrow @nogc
175 "frflags %0" : "=r" (result);
177 return result;
180 else
181 assert(0, "Not yet supported");
183 else
184 version (InlineAsm_X86_Any)
186 ushort sw;
187 asm pure nothrow @nogc { fstsw sw; }
189 // OR the result with the SSE2 status register (MXCSR).
190 if (haveSSE)
192 uint mxcsr;
193 asm pure nothrow @nogc { stmxcsr mxcsr; }
194 return (sw | mxcsr) & EXCEPTIONS_MASK;
196 else return sw & EXCEPTIONS_MASK;
198 else version (SPARC)
201 int retval;
202 asm pure nothrow @nogc { st %fsr, retval; }
203 return retval;
205 assert(0, "Not yet supported");
207 else version (ARM)
209 assert(false, "Not yet supported.");
211 else version (RISCV_Any)
213 mixin(`
214 uint result = void;
215 asm pure nothrow @nogc
217 "frflags %0" : "=r" (result);
219 return result;
222 else version (LoongArch_Any)
224 uint result = void;
225 asm pure nothrow @nogc
227 "movfcsr2gr %0,$r2" : "=r" (result);
229 return result & EXCEPTIONS_MASK;
231 else
232 assert(0, "Not yet supported");
235 static void resetIeeeFlags() @trusted
237 version (GNU)
239 version (X86_Any)
241 asm nothrow @nogc
243 "fnclex";
246 // Also clear exception flags in MXCSR, SSE's control register.
247 if (haveSSE)
249 uint mxcsr;
250 asm nothrow @nogc
252 "stmxcsr %0" : "=m" (mxcsr);
254 mxcsr &= ~EXCEPTIONS_MASK;
255 asm nothrow @nogc
257 "ldmxcsr %0" : : "m" (mxcsr);
261 else version (ARM)
263 version (ARM_SoftFloat)
264 return;
265 else
267 uint old = FloatingPointControl.getControlState();
268 old &= ~0b11111; // http://infocenter.arm.com/help/topic/com.arm.doc.ddi0408i/Chdfifdc.html
269 asm nothrow @nogc
271 "vmsr FPSCR, %0" : : "r" (old);
275 else version (RISCV_Any)
277 version (D_SoftFloat)
278 return;
279 else
281 uint newValues = 0x0;
282 asm nothrow @nogc
284 "fsflags %0" : : "r" (newValues);
288 else
289 assert(0, "Not yet supported");
291 else
292 version (InlineAsm_X86_Any)
294 asm nothrow @nogc
296 fnclex;
299 // Also clear exception flags in MXCSR, SSE's control register.
300 if (haveSSE)
302 uint mxcsr;
303 asm nothrow @nogc { stmxcsr mxcsr; }
304 mxcsr &= ~EXCEPTIONS_MASK;
305 asm nothrow @nogc { ldmxcsr mxcsr; }
308 else version (RISCV_Any)
310 mixin(`
311 uint newValues = 0x0;
312 asm pure nothrow @nogc
314 "fsflags %0" : : "r" (newValues);
318 else version (LoongArch_Any)
320 asm nothrow @nogc
322 "movgr2fcsr $r2,$r0";
325 else
327 /* SPARC:
328 int tmpval;
329 asm pure nothrow @nogc { st %fsr, tmpval; }
330 tmpval &=0xFFFF_FC00;
331 asm pure nothrow @nogc { ld tmpval, %fsr; }
333 assert(0, "Not yet supported");
337 public:
339 * The result cannot be represented exactly, so rounding occurred.
340 * Example: `x = sin(0.1);`
342 @property bool inexact() @safe const { return (flags & INEXACT_MASK) != 0; }
345 * A zero was generated by underflow
346 * Example: `x = real.min*real.epsilon/2;`
348 @property bool underflow() @safe const { return (flags & UNDERFLOW_MASK) != 0; }
351 * An infinity was generated by overflow
352 * Example: `x = real.max*2;`
354 @property bool overflow() @safe const { return (flags & OVERFLOW_MASK) != 0; }
357 * An infinity was generated by division by zero
358 * Example: `x = 3/0.0;`
360 @property bool divByZero() @safe const { return (flags & DIVBYZERO_MASK) != 0; }
363 * A machine NaN was generated.
364 * Example: `x = real.infinity * 0.0;`
366 @property bool invalid() @safe const { return (flags & INVALID_MASK) != 0; }
370 version (StdDdoc)
371 @safe unittest
373 import std.math.traits : isNaN;
375 static void func() {
376 int a = 10 * 10;
378 real a = 3.5;
379 // Set all the flags to zero
380 resetIeeeFlags();
381 assert(!ieeeFlags.divByZero);
382 // Perform a division by zero.
383 a /= 0.0L;
384 assert(a == real.infinity);
385 assert(ieeeFlags.divByZero);
386 // Create a NaN
387 a *= 0.0L;
388 assert(ieeeFlags.invalid);
389 assert(isNaN(a));
391 // Check that calling func() has no effect on the
392 // status flags.
393 IeeeFlags f = ieeeFlags;
394 func();
395 assert(ieeeFlags == f);
398 @safe unittest
400 import std.math.traits : isNaN;
402 static void func() {
403 int a = 10 * 10;
405 real a = 3.5;
406 // Set all the flags to zero
407 resetIeeeFlags();
408 assert(!ieeeFlags.divByZero);
409 // Perform a division by zero.
410 a = forceDivOp(a, 0.0L);
411 assert(a == real.infinity);
412 assert(ieeeFlags.divByZero);
413 // Create a NaN
414 a = forceMulOp(a, 0.0L);
415 assert(ieeeFlags.invalid);
416 assert(isNaN(a));
418 // Check that calling func() has no effect on the
419 // status flags.
420 IeeeFlags f = ieeeFlags;
421 func();
422 assert(ieeeFlags == f);
425 @safe unittest
427 import std.meta : AliasSeq;
429 static struct Test
431 void delegate() @trusted action;
432 bool function() @trusted ieeeCheck;
435 static foreach (T; AliasSeq!(float, double, real))
437 T x; // Needs to be here to avoid `call without side effects` warning.
438 auto tests = [
439 Test(
440 () { x = forceAddOp!T(1, 0.1L); },
441 () => ieeeFlags.inexact
443 Test(
444 () { x = forceDivOp!T(T.min_normal, T.max); },
445 () => ieeeFlags.underflow
447 Test(
448 () { x = forceAddOp!T(T.max, T.max); },
449 () => ieeeFlags.overflow
451 Test(
452 () { x = forceDivOp!T(1, 0); },
453 () => ieeeFlags.divByZero
455 Test(
456 () { x = forceDivOp!T(0, 0); },
457 () => ieeeFlags.invalid
460 foreach (test; tests)
462 resetIeeeFlags();
463 assert(!test.ieeeCheck());
464 test.action();
465 assert(test.ieeeCheck());
470 /// Set all of the floating-point status flags to false.
471 void resetIeeeFlags() @trusted nothrow @nogc
473 IeeeFlags.resetIeeeFlags();
477 version (StdDdoc)
478 @safe unittest
480 resetIeeeFlags();
481 real a = 3.5;
482 a /= 0.0L;
483 assert(a == real.infinity);
484 assert(ieeeFlags.divByZero);
486 resetIeeeFlags();
487 assert(!ieeeFlags.divByZero);
490 @safe unittest
492 resetIeeeFlags();
493 real a = 3.5;
494 a = forceDivOp(a, 0.0L);
495 assert(a == real.infinity);
496 assert(ieeeFlags.divByZero);
498 resetIeeeFlags();
499 assert(!ieeeFlags.divByZero);
502 /// Returns: snapshot of the current state of the floating-point status flags
503 @property IeeeFlags ieeeFlags() @trusted pure nothrow @nogc
505 return IeeeFlags(IeeeFlags.getIeeeFlags());
509 version (StdDdoc)
510 @safe nothrow unittest
512 import std.math.traits : isNaN;
514 resetIeeeFlags();
515 real a = 3.5;
517 a /= 0.0L;
518 assert(a == real.infinity);
519 assert(ieeeFlags.divByZero);
521 a *= 0.0L;
522 assert(isNaN(a));
523 assert(ieeeFlags.invalid);
526 @safe nothrow unittest
528 import std.math.traits : isNaN;
530 resetIeeeFlags();
531 real a = 3.5;
533 a = forceDivOp(a, 0.0L);
534 assert(a == real.infinity);
535 assert(ieeeFlags.divByZero);
537 a = forceMulOp(a, 0.0L);
538 assert(isNaN(a));
539 assert(ieeeFlags.invalid);
542 } // IeeeFlagsSupport
545 version (FloatingPointControlSupport)
548 /** Control the Floating point hardware
550 Change the IEEE754 floating-point rounding mode and the floating-point
551 hardware exceptions.
553 By default, the rounding mode is roundToNearest and all hardware exceptions
554 are disabled. For most applications, debugging is easier if the $(I division
555 by zero), $(I overflow), and $(I invalid operation) exceptions are enabled.
556 These three are combined into a $(I severeExceptions) value for convenience.
557 Note in particular that if $(I invalidException) is enabled, a hardware trap
558 will be generated whenever an uninitialized floating-point variable is used.
560 All changes are temporary. The previous state is restored at the
561 end of the scope.
564 Example:
565 ----
567 FloatingPointControl fpctrl;
569 // Enable hardware exceptions for division by zero, overflow to infinity,
570 // invalid operations, and uninitialized floating-point variables.
571 fpctrl.enableExceptions(FloatingPointControl.severeExceptions);
573 // This will generate a hardware exception, if x is a
574 // default-initialized floating point variable:
575 real x; // Add `= 0` or even `= real.nan` to not throw the exception.
576 real y = x * 3.0;
578 // The exception is only thrown for default-uninitialized NaN-s.
579 // NaN-s with other payload are valid:
580 real z = y * real.nan; // ok
582 // The set hardware exceptions and rounding modes will be disabled when
583 // leaving this scope.
585 ----
588 struct FloatingPointControl
590 nothrow @nogc:
592 alias RoundingMode = uint; ///
594 version (StdDdoc)
596 enum : RoundingMode
598 /** IEEE rounding modes.
599 * The default mode is roundToNearest.
601 * roundingMask = A mask of all rounding modes.
603 roundToNearest,
604 roundDown, /// ditto
605 roundUp, /// ditto
606 roundToZero, /// ditto
607 roundingMask, /// ditto
610 else version (CRuntime_Microsoft)
612 // Microsoft uses hardware-incompatible custom constants in fenv.h (core.stdc.fenv).
613 enum : RoundingMode
615 roundToNearest = 0x0000,
616 roundDown = 0x0400,
617 roundUp = 0x0800,
618 roundToZero = 0x0C00,
619 roundingMask = roundToNearest | roundDown
620 | roundUp | roundToZero,
623 else
625 enum : RoundingMode
627 roundToNearest = core.stdc.fenv.FE_TONEAREST,
628 roundDown = core.stdc.fenv.FE_DOWNWARD,
629 roundUp = core.stdc.fenv.FE_UPWARD,
630 roundToZero = core.stdc.fenv.FE_TOWARDZERO,
631 roundingMask = roundToNearest | roundDown
632 | roundUp | roundToZero,
636 /***
637 * Change the floating-point hardware rounding mode
639 * Changing the rounding mode in the middle of a function can interfere
640 * with optimizations of floating point expressions, as the optimizer assumes
641 * that the rounding mode does not change.
642 * It is best to change the rounding mode only at the
643 * beginning of the function, and keep it until the function returns.
644 * It is also best to add the line:
645 * ---
646 * pragma(inline, false);
647 * ---
648 * as the first line of the function so it will not get inlined.
649 * Params:
650 * newMode = the new rounding mode
652 @property void rounding(RoundingMode newMode) @trusted
654 initialize();
655 setControlState((getControlState() & (-1 - roundingMask)) | (newMode & roundingMask));
658 /// Returns: the currently active rounding mode
659 @property static RoundingMode rounding() @trusted pure
661 return cast(RoundingMode)(getControlState() & roundingMask);
664 alias ExceptionMask = uint; ///
666 version (StdDdoc)
668 enum : ExceptionMask
670 /** IEEE hardware exceptions.
671 * By default, all exceptions are masked (disabled).
673 * severeExceptions = The overflow, division by zero, and invalid
674 * exceptions.
676 subnormalException,
677 inexactException, /// ditto
678 underflowException, /// ditto
679 overflowException, /// ditto
680 divByZeroException, /// ditto
681 invalidException, /// ditto
682 severeExceptions, /// ditto
683 allExceptions, /// ditto
686 else version (ARM_Any)
688 enum : ExceptionMask
690 subnormalException = 0x8000,
691 inexactException = 0x1000,
692 underflowException = 0x0800,
693 overflowException = 0x0400,
694 divByZeroException = 0x0200,
695 invalidException = 0x0100,
696 severeExceptions = overflowException | divByZeroException
697 | invalidException,
698 allExceptions = severeExceptions | underflowException
699 | inexactException | subnormalException,
702 else version (PPC_Any)
704 enum : ExceptionMask
706 inexactException = 0x0008,
707 divByZeroException = 0x0010,
708 underflowException = 0x0020,
709 overflowException = 0x0040,
710 invalidException = 0x0080,
711 severeExceptions = overflowException | divByZeroException
712 | invalidException,
713 allExceptions = severeExceptions | underflowException
714 | inexactException,
717 else version (RISCV_Any)
719 enum : ExceptionMask
721 inexactException = 0x01,
722 divByZeroException = 0x08,
723 underflowException = 0x02,
724 overflowException = 0x04,
725 invalidException = 0x10,
726 severeExceptions = overflowException | divByZeroException
727 | invalidException,
728 allExceptions = severeExceptions | underflowException
729 | inexactException,
732 else version (HPPA)
734 enum : ExceptionMask
736 inexactException = 0x01,
737 underflowException = 0x02,
738 overflowException = 0x04,
739 divByZeroException = 0x08,
740 invalidException = 0x10,
741 severeExceptions = overflowException | divByZeroException
742 | invalidException,
743 allExceptions = severeExceptions | underflowException
744 | inexactException,
747 else version (LoongArch_Any)
749 enum : ExceptionMask
751 inexactException = 0x00,
752 divByZeroException = 0x01,
753 overflowException = 0x02,
754 underflowException = 0x04,
755 invalidException = 0x08,
756 severeExceptions = overflowException | divByZeroException
757 | invalidException,
758 allExceptions = severeExceptions | underflowException
759 | inexactException,
762 else version (MIPS_Any)
764 enum : ExceptionMask
766 inexactException = 0x0080,
767 divByZeroException = 0x0400,
768 overflowException = 0x0200,
769 underflowException = 0x0100,
770 invalidException = 0x0800,
771 severeExceptions = overflowException | divByZeroException
772 | invalidException,
773 allExceptions = severeExceptions | underflowException
774 | inexactException,
777 else version (SPARC_Any)
779 enum : ExceptionMask
781 inexactException = 0x0800000,
782 divByZeroException = 0x1000000,
783 overflowException = 0x4000000,
784 underflowException = 0x2000000,
785 invalidException = 0x8000000,
786 severeExceptions = overflowException | divByZeroException
787 | invalidException,
788 allExceptions = severeExceptions | underflowException
789 | inexactException,
792 else version (IBMZ_Any)
794 enum : ExceptionMask
796 inexactException = 0x08000000,
797 divByZeroException = 0x40000000,
798 overflowException = 0x20000000,
799 underflowException = 0x10000000,
800 invalidException = 0x80000000,
801 severeExceptions = overflowException | divByZeroException
802 | invalidException,
803 allExceptions = severeExceptions | underflowException
804 | inexactException,
807 else version (X86_Any)
809 enum : ExceptionMask
811 inexactException = 0x20,
812 underflowException = 0x10,
813 overflowException = 0x08,
814 divByZeroException = 0x04,
815 subnormalException = 0x02,
816 invalidException = 0x01,
817 severeExceptions = overflowException | divByZeroException
818 | invalidException,
819 allExceptions = severeExceptions | underflowException
820 | inexactException | subnormalException,
823 else
824 static assert(false, "Not implemented for this architecture");
826 version (ARM_Any)
828 static bool hasExceptionTraps_impl() @safe
830 auto oldState = getControlState();
831 // If exceptions are not supported, we set the bit but read it back as zero
832 // https://sourceware.org/ml/libc-ports/2012-06/msg00091.html
833 setControlState(oldState | divByZeroException);
834 immutable result = (getControlState() & allExceptions) != 0;
835 setControlState(oldState);
836 return result;
840 /// Returns: true if the current FPU supports exception trapping
841 @property static bool hasExceptionTraps() @safe pure
843 version (X86_Any)
844 return true;
845 else version (PPC_Any)
846 return true;
847 else version (MIPS_Any)
848 return true;
849 else version (LoongArch_Any)
850 return true;
851 else version (ARM_Any)
853 // The hasExceptionTraps_impl function is basically pure,
854 // as it restores all global state
855 auto fptr = ( () @trusted => cast(bool function() @safe
856 pure nothrow @nogc)&hasExceptionTraps_impl)();
857 return fptr();
859 else
860 assert(0, "Not yet supported");
863 /// Enable (unmask) specific hardware exceptions. Multiple exceptions may be ORed together.
864 void enableExceptions(ExceptionMask exceptions) @trusted
866 assert(hasExceptionTraps);
867 initialize();
868 version (X86_Any)
869 setControlState(getControlState() & ~(exceptions & allExceptions));
870 else
871 setControlState(getControlState() | (exceptions & allExceptions));
874 /// Disable (mask) specific hardware exceptions. Multiple exceptions may be ORed together.
875 void disableExceptions(ExceptionMask exceptions) @trusted
877 assert(hasExceptionTraps);
878 initialize();
879 version (X86_Any)
880 setControlState(getControlState() | (exceptions & allExceptions));
881 else
882 setControlState(getControlState() & ~(exceptions & allExceptions));
885 /// Returns: the exceptions which are currently enabled (unmasked)
886 @property static ExceptionMask enabledExceptions() @trusted pure
888 assert(hasExceptionTraps);
889 version (X86_Any)
890 return (getControlState() & allExceptions) ^ allExceptions;
891 else
892 return (getControlState() & allExceptions);
895 /// Clear all pending exceptions, then restore the original exception state and rounding mode.
896 ~this() @trusted
898 clearExceptions();
899 if (initialized)
900 setControlState(savedState);
903 private:
904 ControlState savedState;
906 bool initialized = false;
908 version (ARM_Any)
910 alias ControlState = uint;
912 else version (HPPA)
914 alias ControlState = uint;
916 else version (PPC_Any)
918 alias ControlState = uint;
920 else version (RISCV_Any)
922 alias ControlState = uint;
924 else version (LoongArch_Any)
926 alias ControlState = uint;
928 else version (MIPS_Any)
930 alias ControlState = uint;
932 else version (SPARC_Any)
934 alias ControlState = ulong;
936 else version (IBMZ_Any)
938 alias ControlState = uint;
940 else version (X86_Any)
942 alias ControlState = ushort;
944 else
945 static assert(false, "Not implemented for this architecture");
947 void initialize() @safe
949 // BUG: This works around the absence of this() constructors.
950 if (initialized) return;
951 clearExceptions();
952 savedState = getControlState();
953 initialized = true;
956 // Clear all pending exceptions
957 static void clearExceptions() @safe
959 version (IeeeFlagsSupport)
960 resetIeeeFlags();
961 else
962 static assert(false, "Not implemented for this architecture");
965 // Read from the control register
966 package(std.math) static ControlState getControlState() @trusted pure
968 version (GNU)
970 version (X86_Any)
972 ControlState cont;
973 asm pure nothrow @nogc
975 "fstcw %0" : "=m" (cont);
977 return cont;
979 else version (AArch64)
981 ControlState cont;
982 asm pure nothrow @nogc
984 "mrs %0, FPCR;" : "=r" (cont);
986 return cont;
988 else version (ARM)
990 ControlState cont;
991 version (ARM_SoftFloat)
992 cont = 0;
993 else
995 asm pure nothrow @nogc
997 "vmrs %0, FPSCR" : "=r" (cont);
1000 return cont;
1002 else version (RISCV_Any)
1004 version (D_SoftFloat)
1005 return 0;
1006 else
1008 ControlState cont;
1009 asm pure nothrow @nogc
1011 "frcsr %0" : "=r" (cont);
1013 return cont;
1016 else
1017 assert(0, "Not yet supported");
1019 else
1020 version (D_InlineAsm_X86)
1022 short cont;
1023 asm pure nothrow @nogc
1025 xor EAX, EAX;
1026 fstcw cont;
1028 return cont;
1030 else version (D_InlineAsm_X86_64)
1032 short cont;
1033 asm pure nothrow @nogc
1035 xor RAX, RAX;
1036 fstcw cont;
1038 return cont;
1040 else version (RISCV_Any)
1042 mixin(`
1043 ControlState cont;
1044 asm pure nothrow @nogc
1046 "frcsr %0" : "=r" (cont);
1048 return cont;
1051 else version (LoongArch_Any)
1053 ControlState cont;
1054 asm pure nothrow @nogc
1056 "movfcsr2gr %0,$r0" : "=r" (cont);
1058 cont &= (roundingMask | allExceptions);
1059 return cont;
1061 else
1062 assert(0, "Not yet supported");
1065 // Set the control register
1066 package(std.math) static void setControlState(ControlState newState) @trusted
1068 version (GNU)
1070 version (X86_Any)
1072 asm nothrow @nogc
1074 "fclex; fldcw %0" : : "m" (newState);
1077 // Also update MXCSR, SSE's control register.
1078 if (haveSSE)
1080 uint mxcsr;
1081 asm nothrow @nogc
1083 "stmxcsr %0" : "=m" (mxcsr);
1086 /* In the FPU control register, rounding mode is in bits 10 and
1087 11. In MXCSR it's in bits 13 and 14. */
1088 mxcsr &= ~(roundingMask << 3); // delete old rounding mode
1089 mxcsr |= (newState & roundingMask) << 3; // write new rounding mode
1091 /* In the FPU control register, masks are bits 0 through 5.
1092 In MXCSR they're 7 through 12. */
1093 mxcsr &= ~(allExceptions << 7); // delete old masks
1094 mxcsr |= (newState & allExceptions) << 7; // write new exception masks
1096 asm nothrow @nogc
1098 "ldmxcsr %0" : : "m" (mxcsr);
1102 else version (AArch64)
1104 asm nothrow @nogc
1106 "msr FPCR, %0;" : : "r" (newState);
1109 else version (ARM)
1111 version (ARM_SoftFloat)
1112 return;
1113 else
1115 asm nothrow @nogc
1117 "vmsr FPSCR, %0" : : "r" (newState);
1121 else version (RISCV_Any)
1123 version (D_SoftFloat)
1124 return;
1125 else
1127 asm nothrow @nogc
1129 "fscsr %0" : : "r" (newState);
1133 else
1134 assert(0, "Not yet supported");
1136 else
1137 version (InlineAsm_X86_Any)
1139 asm nothrow @nogc
1141 fclex;
1142 fldcw newState;
1145 // Also update MXCSR, SSE's control register.
1146 if (haveSSE)
1148 uint mxcsr;
1149 asm nothrow @nogc { stmxcsr mxcsr; }
1151 /* In the FPU control register, rounding mode is in bits 10 and
1152 11. In MXCSR it's in bits 13 and 14. */
1153 mxcsr &= ~(roundingMask << 3); // delete old rounding mode
1154 mxcsr |= (newState & roundingMask) << 3; // write new rounding mode
1156 /* In the FPU control register, masks are bits 0 through 5.
1157 In MXCSR they're 7 through 12. */
1158 mxcsr &= ~(allExceptions << 7); // delete old masks
1159 mxcsr |= (newState & allExceptions) << 7; // write new exception masks
1161 asm nothrow @nogc { ldmxcsr mxcsr; }
1164 else version (RISCV_Any)
1166 mixin(`
1167 asm pure nothrow @nogc
1169 "fscsr %0" : : "r" (newState);
1173 else version (LoongArch_Any)
1175 asm nothrow @nogc
1177 "movgr2fcsr $r0,%0" :
1178 : "r" (newState & (roundingMask | allExceptions));
1181 else
1182 assert(0, "Not yet supported");
1187 @safe unittest
1189 import std.math.rounding : lrint;
1191 FloatingPointControl fpctrl;
1193 fpctrl.rounding = FloatingPointControl.roundDown;
1194 assert(lrint(1.5) == 1.0);
1196 fpctrl.rounding = FloatingPointControl.roundUp;
1197 assert(lrint(1.4) == 2.0);
1199 fpctrl.rounding = FloatingPointControl.roundToNearest;
1200 assert(lrint(1.5) == 2.0);
1203 @safe unittest
1205 void ensureDefaults()
1207 assert(FloatingPointControl.rounding
1208 == FloatingPointControl.roundToNearest);
1209 if (FloatingPointControl.hasExceptionTraps)
1210 assert(FloatingPointControl.enabledExceptions == 0);
1214 FloatingPointControl ctrl;
1216 ensureDefaults();
1219 FloatingPointControl ctrl;
1220 ctrl.rounding = FloatingPointControl.roundDown;
1221 assert(FloatingPointControl.rounding == FloatingPointControl.roundDown);
1223 ensureDefaults();
1225 if (FloatingPointControl.hasExceptionTraps)
1227 FloatingPointControl ctrl;
1228 ctrl.enableExceptions(FloatingPointControl.divByZeroException
1229 | FloatingPointControl.overflowException);
1230 assert(ctrl.enabledExceptions ==
1231 (FloatingPointControl.divByZeroException
1232 | FloatingPointControl.overflowException));
1234 ctrl.rounding = FloatingPointControl.roundUp;
1235 assert(FloatingPointControl.rounding == FloatingPointControl.roundUp);
1237 ensureDefaults();
1240 @safe unittest // rounding
1242 import std.meta : AliasSeq;
1244 static T addRound(T)(uint rm)
1246 pragma(inline, false);
1247 FloatingPointControl fpctrl;
1248 fpctrl.rounding = rm;
1249 T x = 1;
1250 x = forceAddOp(x, 0.1L);
1251 return x;
1254 static T subRound(T)(uint rm)
1256 pragma(inline, false);
1257 FloatingPointControl fpctrl;
1258 fpctrl.rounding = rm;
1259 T x = -1;
1260 x = forceSubOp(x, 0.1L);
1261 return x;
1264 static foreach (T; AliasSeq!(float, double, real))
1266 /* Be careful with changing the rounding mode, it interferes
1267 * with common subexpressions. Changing rounding modes should
1268 * be done with separate functions that are not inlined.
1272 T u = addRound!(T)(FloatingPointControl.roundUp);
1273 T d = addRound!(T)(FloatingPointControl.roundDown);
1274 T z = addRound!(T)(FloatingPointControl.roundToZero);
1276 assert(u > d);
1277 assert(z == d);
1281 T u = subRound!(T)(FloatingPointControl.roundUp);
1282 T d = subRound!(T)(FloatingPointControl.roundDown);
1283 T z = subRound!(T)(FloatingPointControl.roundToZero);
1285 assert(u > d);
1286 assert(z == u);
1291 } // FloatingPointControlSupport
1293 version (StdUnittest)
1295 // These helpers are intended to avoid constant propagation by the optimizer.
1296 pragma(inline, false) private @safe
1298 T forceAddOp(T)(T x, T y) { return x + y; }
1299 T forceSubOp(T)(T x, T y) { return x - y; }
1300 T forceMulOp(T)(T x, T y) { return x * y; }
1301 T forceDivOp(T)(T x, T y) { return x / y; }