1 // Written in the D programming language.
2 
3 /**
4 This is a submodule of $(MREF std, math).
5 
6 It contains hardware support for floating point numbers.
7 
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)
13  */
14 
15 module std.math.hardware;
16 
17 static import core.stdc.fenv;
18 
19 version (X86)       version = X86_Any;
20 version (X86_64)    version = X86_Any;
21 version (PPC)       version = PPC_Any;
22 version (PPC64)     version = PPC_Any;
23 version (MIPS32)    version = MIPS_Any;
24 version (MIPS64)    version = MIPS_Any;
25 version (AArch64)   version = ARM_Any;
26 version (ARM)       version = ARM_Any;
27 version (S390)      version = IBMZ_Any;
28 version (SPARC)     version = SPARC_Any;
29 version (SPARC64)   version = SPARC_Any;
30 version (SystemZ)   version = IBMZ_Any;
31 version (RISCV32)   version = RISCV_Any;
32 version (RISCV64)   version = RISCV_Any;
33 
34 version (D_InlineAsm_X86)    version = InlineAsm_X86_Any;
35 version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any;
36 
37 version (X86_64) version = StaticallyHaveSSE;
38 version (X86) version (OSX) version = StaticallyHaveSSE;
39 
40 version (StaticallyHaveSSE)
41 {
42     private enum bool haveSSE = true;
43 }
44 else version (X86)
45 {
46     static import core.cpuid;
47     private alias haveSSE = core.cpuid.sse;
48 }
49 
50 version (D_SoftFloat)
51 {
52     // Some soft float implementations may support IEEE floating flags.
53     // The implementation here supports hardware flags only and is so currently
54     // only available for supported targets.
55 }
56 else version (X86_Any)   version = IeeeFlagsSupport;
57 else version (PPC_Any)   version = IeeeFlagsSupport;
58 else version (RISCV_Any) version = IeeeFlagsSupport;
59 else version (MIPS_Any)  version = IeeeFlagsSupport;
60 else version (ARM_Any)   version = IeeeFlagsSupport;
61 
62 // Struct FloatingPointControl is only available if hardware FP units are available.
63 version (D_HardFloat)
64 {
65     // FloatingPointControl.clearExceptions() depends on version IeeeFlagsSupport
66     version (IeeeFlagsSupport) version = FloatingPointControlSupport;
67 }
68 
69 version (IeeeFlagsSupport)
70 {
71 
72 /** IEEE exception status flags ('sticky bits')
73 
74  These flags indicate that an exceptional floating-point condition has occurred.
75  They indicate that a NaN or an infinity has been generated, that a result
76  is inexact, or that a signalling NaN has been encountered. If floating-point
77  exceptions are enabled (unmasked), a hardware exception will be generated
78  instead of setting these flags.
79  */
80 struct IeeeFlags
81 {
82 nothrow @nogc:
83 
84 private:
85     // The x87 FPU status register is 16 bits.
86     // The Pentium SSE2 status register is 32 bits.
87     // The ARM and PowerPC FPSCR is a 32-bit register.
88     // The SPARC FSR is a 32bit register (64 bits for SPARC 7 & 8, but high bits are uninteresting).
89     // The RISC-V (32 & 64 bit) fcsr is 32-bit register.
90     uint flags;
91 
92     version (CRuntime_Microsoft)
93     {
94         // Microsoft uses hardware-incompatible custom constants in fenv.h (core.stdc.fenv).
95         // Applies to both x87 status word (16 bits) and SSE2 status word(32 bits).
96         enum : int
97         {
98             INEXACT_MASK   = 0x20,
99             UNDERFLOW_MASK = 0x10,
100             OVERFLOW_MASK  = 0x08,
101             DIVBYZERO_MASK = 0x04,
102             INVALID_MASK   = 0x01,
103 
104             EXCEPTIONS_MASK = 0b11_1111
105         }
106         // Don't bother about subnormals, they are not supported on most CPUs.
107         //  SUBNORMAL_MASK = 0x02;
108     }
109     else
110     {
111         enum : int
112         {
113             INEXACT_MASK    = core.stdc.fenv.FE_INEXACT,
114             UNDERFLOW_MASK  = core.stdc.fenv.FE_UNDERFLOW,
115             OVERFLOW_MASK   = core.stdc.fenv.FE_OVERFLOW,
116             DIVBYZERO_MASK  = core.stdc.fenv.FE_DIVBYZERO,
117             INVALID_MASK    = core.stdc.fenv.FE_INVALID,
118             EXCEPTIONS_MASK = core.stdc.fenv.FE_ALL_EXCEPT,
119         }
120     }
121 
122     static uint getIeeeFlags() @trusted pure
123     {
124         version (InlineAsm_X86_Any)
125         {
126             ushort sw;
127             asm pure nothrow @nogc { fstsw sw; }
128 
129             // OR the result with the SSE2 status register (MXCSR).
130             if (haveSSE)
131             {
132                 uint mxcsr;
133                 asm pure nothrow @nogc { stmxcsr mxcsr; }
134                 return (sw | mxcsr) & EXCEPTIONS_MASK;
135             }
136             else return sw & EXCEPTIONS_MASK;
137         }
138         else version (SPARC)
139         {
140             /*
141                int retval;
142                asm pure nothrow @nogc { st %fsr, retval; }
143                return retval;
144             */
145             assert(0, "Not yet supported");
146         }
147         else version (ARM)
148         {
149             assert(false, "Not yet supported.");
150         }
151         else version (RISCV_Any)
152         {
153             mixin(`
154             uint result = void;
155             asm pure nothrow @nogc
156             {
157                 "frflags %0" : "=r" (result);
158             }
159             return result;
160             `);
161         }
162         else
163             assert(0, "Not yet supported");
164     }
165 
166     static void resetIeeeFlags() @trusted
167     {
168         version (InlineAsm_X86_Any)
169         {
170             asm nothrow @nogc
171             {
172                 fnclex;
173             }
174 
175             // Also clear exception flags in MXCSR, SSE's control register.
176             if (haveSSE)
177             {
178                 uint mxcsr;
179                 asm nothrow @nogc { stmxcsr mxcsr; }
180                 mxcsr &= ~EXCEPTIONS_MASK;
181                 asm nothrow @nogc { ldmxcsr mxcsr; }
182             }
183         }
184         else version (RISCV_Any)
185         {
186             mixin(`
187             uint newValues = 0x0;
188             asm pure nothrow @nogc
189             {
190                 "fsflags %0" : : "r" (newValues);
191             }
192             `);
193         }
194         else
195         {
196             /* SPARC:
197               int tmpval;
198               asm pure nothrow @nogc { st %fsr, tmpval; }
199               tmpval &=0xFFFF_FC00;
200               asm pure nothrow @nogc { ld tmpval, %fsr; }
201             */
202            assert(0, "Not yet supported");
203         }
204     }
205 
206 public:
207     /**
208      * The result cannot be represented exactly, so rounding occurred.
209      * Example: `x = sin(0.1);`
210      */
211     @property bool inexact() @safe const { return (flags & INEXACT_MASK) != 0; }
212 
213     /**
214      * A zero was generated by underflow
215      * Example: `x = real.min*real.epsilon/2;`
216      */
217     @property bool underflow() @safe const { return (flags & UNDERFLOW_MASK) != 0; }
218 
219     /**
220      * An infinity was generated by overflow
221      * Example: `x = real.max*2;`
222      */
223     @property bool overflow() @safe const { return (flags & OVERFLOW_MASK) != 0; }
224 
225     /**
226      * An infinity was generated by division by zero
227      * Example: `x = 3/0.0;`
228      */
229     @property bool divByZero() @safe const { return (flags & DIVBYZERO_MASK) != 0; }
230 
231     /**
232      * A machine NaN was generated.
233      * Example: `x = real.infinity * 0.0;`
234      */
235     @property bool invalid() @safe const { return (flags & INVALID_MASK) != 0; }
236 }
237 
238 ///
239 version (StdDdoc)
240 @safe unittest
241 {
242     import std.math.traits : isNaN;
243 
244     static void func() {
245         int a = 10 * 10;
246     }
247     real a = 3.5;
248     // Set all the flags to zero
249     resetIeeeFlags();
250     assert(!ieeeFlags.divByZero);
251     // Perform a division by zero.
252     a /= 0.0L;
253     assert(a == real.infinity);
254     assert(ieeeFlags.divByZero);
255     // Create a NaN
256     a *= 0.0L;
257     assert(ieeeFlags.invalid);
258     assert(isNaN(a));
259 
260     // Check that calling func() has no effect on the
261     // status flags.
262     IeeeFlags f = ieeeFlags;
263     func();
264     assert(ieeeFlags == f);
265 }
266 
267 @safe unittest
268 {
269     import std.math.traits : isNaN;
270 
271     static void func() {
272         int a = 10 * 10;
273     }
274     real a = 3.5;
275     // Set all the flags to zero
276     resetIeeeFlags();
277     assert(!ieeeFlags.divByZero);
278     // Perform a division by zero.
279     a = forceDivOp(a, 0.0L);
280     assert(a == real.infinity);
281     assert(ieeeFlags.divByZero);
282     // Create a NaN
283     a = forceMulOp(a, 0.0L);
284     assert(ieeeFlags.invalid);
285     assert(isNaN(a));
286 
287     // Check that calling func() has no effect on the
288     // status flags.
289     IeeeFlags f = ieeeFlags;
290     func();
291     assert(ieeeFlags == f);
292 }
293 
294 @safe unittest
295 {
296     import std.meta : AliasSeq;
297 
298     static struct Test
299     {
300         void delegate() @trusted action;
301         bool function() @trusted ieeeCheck;
302     }
303 
304     static foreach (T; AliasSeq!(float, double, real))
305     {{
306         T x; // Needs to be here to avoid `call without side effects` warning.
307         auto tests = [
308             Test(
309                 () { x = forceAddOp!T(1, 0.1L); },
310                 () => ieeeFlags.inexact
311             ),
312             Test(
313                 () { x = forceDivOp!T(T.min_normal, T.max); },
314                 () => ieeeFlags.underflow
315             ),
316             Test(
317                 () { x = forceAddOp!T(T.max, T.max); },
318                 () => ieeeFlags.overflow
319             ),
320             Test(
321                 () { x = forceDivOp!T(1, 0); },
322                 () => ieeeFlags.divByZero
323             ),
324             Test(
325                 () { x = forceDivOp!T(0, 0); },
326                 () => ieeeFlags.invalid
327             )
328         ];
329         foreach (test; tests)
330         {
331             resetIeeeFlags();
332             assert(!test.ieeeCheck());
333             test.action();
334             assert(test.ieeeCheck());
335         }
336     }}
337 }
338 
339 /// Set all of the floating-point status flags to false.
340 void resetIeeeFlags() @trusted nothrow @nogc
341 {
342     IeeeFlags.resetIeeeFlags();
343 }
344 
345 ///
346 version (StdDdoc)
347 @safe unittest
348 {
349     resetIeeeFlags();
350     real a = 3.5;
351     a /= 0.0L;
352     assert(a == real.infinity);
353     assert(ieeeFlags.divByZero);
354 
355     resetIeeeFlags();
356     assert(!ieeeFlags.divByZero);
357 }
358 
359 @safe unittest
360 {
361     resetIeeeFlags();
362     real a = 3.5;
363     a = forceDivOp(a, 0.0L);
364     assert(a == real.infinity);
365     assert(ieeeFlags.divByZero);
366 
367     resetIeeeFlags();
368     assert(!ieeeFlags.divByZero);
369 }
370 
371 /// Returns: snapshot of the current state of the floating-point status flags
372 @property IeeeFlags ieeeFlags() @trusted pure nothrow @nogc
373 {
374    return IeeeFlags(IeeeFlags.getIeeeFlags());
375 }
376 
377 ///
378 version (StdDdoc)
379 @safe nothrow unittest
380 {
381     import std.math.traits : isNaN;
382 
383     resetIeeeFlags();
384     real a = 3.5;
385 
386     a /= 0.0L;
387     assert(a == real.infinity);
388     assert(ieeeFlags.divByZero);
389 
390     a *= 0.0L;
391     assert(isNaN(a));
392     assert(ieeeFlags.invalid);
393 }
394 
395 @safe nothrow unittest
396 {
397     import std.math.traits : isNaN;
398 
399     resetIeeeFlags();
400     real a = 3.5;
401 
402     a = forceDivOp(a, 0.0L);
403     assert(a == real.infinity);
404     assert(ieeeFlags.divByZero);
405 
406     a = forceMulOp(a, 0.0L);
407     assert(isNaN(a));
408     assert(ieeeFlags.invalid);
409 }
410 
411 } // IeeeFlagsSupport
412 
413 
414 version (FloatingPointControlSupport)
415 {
416 
417 /** Control the Floating point hardware
418 
419   Change the IEEE754 floating-point rounding mode and the floating-point
420   hardware exceptions.
421 
422   By default, the rounding mode is roundToNearest and all hardware exceptions
423   are disabled. For most applications, debugging is easier if the $(I division
424   by zero), $(I overflow), and $(I invalid operation) exceptions are enabled.
425   These three are combined into a $(I severeExceptions) value for convenience.
426   Note in particular that if $(I invalidException) is enabled, a hardware trap
427   will be generated whenever an uninitialized floating-point variable is used.
428 
429   All changes are temporary. The previous state is restored at the
430   end of the scope.
431 
432 
433 Example:
434 ----
435 {
436     FloatingPointControl fpctrl;
437 
438     // Enable hardware exceptions for division by zero, overflow to infinity,
439     // invalid operations, and uninitialized floating-point variables.
440     fpctrl.enableExceptions(FloatingPointControl.severeExceptions);
441 
442     // This will generate a hardware exception, if x is a
443     // default-initialized floating point variable:
444     real x; // Add `= 0` or even `= real.nan` to not throw the exception.
445     real y = x * 3.0;
446 
447     // The exception is only thrown for default-uninitialized NaN-s.
448     // NaN-s with other payload are valid:
449     real z = y * real.nan; // ok
450 
451     // The set hardware exceptions and rounding modes will be disabled when
452     // leaving this scope.
453 }
454 ----
455 
456  */
457 struct FloatingPointControl
458 {
459 nothrow @nogc:
460 
461     alias RoundingMode = uint; ///
462 
463     version (StdDdoc)
464     {
465         enum : RoundingMode
466         {
467             /** IEEE rounding modes.
468              * The default mode is roundToNearest.
469              *
470              *  roundingMask = A mask of all rounding modes.
471              */
472             roundToNearest,
473             roundDown, /// ditto
474             roundUp, /// ditto
475             roundToZero, /// ditto
476             roundingMask, /// ditto
477         }
478     }
479     else version (CRuntime_Microsoft)
480     {
481         // Microsoft uses hardware-incompatible custom constants in fenv.h (core.stdc.fenv).
482         enum : RoundingMode
483         {
484             roundToNearest = 0x0000,
485             roundDown      = 0x0400,
486             roundUp        = 0x0800,
487             roundToZero    = 0x0C00,
488             roundingMask   = roundToNearest | roundDown
489                              | roundUp | roundToZero,
490         }
491     }
492     else
493     {
494         enum : RoundingMode
495         {
496             roundToNearest = core.stdc.fenv.FE_TONEAREST,
497             roundDown      = core.stdc.fenv.FE_DOWNWARD,
498             roundUp        = core.stdc.fenv.FE_UPWARD,
499             roundToZero    = core.stdc.fenv.FE_TOWARDZERO,
500             roundingMask   = roundToNearest | roundDown
501                              | roundUp | roundToZero,
502         }
503     }
504 
505     /***
506      * Change the floating-point hardware rounding mode
507      *
508      * Changing the rounding mode in the middle of a function can interfere
509      * with optimizations of floating point expressions, as the optimizer assumes
510      * that the rounding mode does not change.
511      * It is best to change the rounding mode only at the
512      * beginning of the function, and keep it until the function returns.
513      * It is also best to add the line:
514      * ---
515      * pragma(inline, false);
516      * ---
517      * as the first line of the function so it will not get inlined.
518      * Params:
519      *    newMode = the new rounding mode
520      */
521     @property void rounding(RoundingMode newMode) @trusted
522     {
523         initialize();
524         setControlState((getControlState() & (-1 - roundingMask)) | (newMode & roundingMask));
525     }
526 
527     /// Returns: the currently active rounding mode
528     @property static RoundingMode rounding() @trusted pure
529     {
530         return cast(RoundingMode)(getControlState() & roundingMask);
531     }
532 
533     alias ExceptionMask = uint; ///
534 
535     version (StdDdoc)
536     {
537         enum : ExceptionMask
538         {
539             /** IEEE hardware exceptions.
540              *  By default, all exceptions are masked (disabled).
541              *
542              *  severeExceptions = The overflow, division by zero, and invalid
543              *  exceptions.
544              */
545             subnormalException,
546             inexactException, /// ditto
547             underflowException, /// ditto
548             overflowException, /// ditto
549             divByZeroException, /// ditto
550             invalidException, /// ditto
551             severeExceptions, /// ditto
552             allExceptions, /// ditto
553         }
554     }
555     else version (ARM_Any)
556     {
557         enum : ExceptionMask
558         {
559             subnormalException    = 0x8000,
560             inexactException      = 0x1000,
561             underflowException    = 0x0800,
562             overflowException     = 0x0400,
563             divByZeroException    = 0x0200,
564             invalidException      = 0x0100,
565             severeExceptions   = overflowException | divByZeroException
566                                  | invalidException,
567             allExceptions      = severeExceptions | underflowException
568                                  | inexactException | subnormalException,
569         }
570     }
571     else version (PPC_Any)
572     {
573         enum : ExceptionMask
574         {
575             inexactException      = 0x0008,
576             divByZeroException    = 0x0010,
577             underflowException    = 0x0020,
578             overflowException     = 0x0040,
579             invalidException      = 0x0080,
580             severeExceptions   = overflowException | divByZeroException
581                                  | invalidException,
582             allExceptions      = severeExceptions | underflowException
583                                  | inexactException,
584         }
585     }
586     else version (RISCV_Any)
587     {
588         enum : ExceptionMask
589         {
590             inexactException      = 0x01,
591             divByZeroException    = 0x08,
592             underflowException    = 0x02,
593             overflowException     = 0x04,
594             invalidException      = 0x10,
595             severeExceptions   = overflowException | divByZeroException
596                                  | invalidException,
597             allExceptions      = severeExceptions | underflowException
598                                  | inexactException,
599         }
600     }
601     else version (HPPA)
602     {
603         enum : ExceptionMask
604         {
605             inexactException      = 0x01,
606             underflowException    = 0x02,
607             overflowException     = 0x04,
608             divByZeroException    = 0x08,
609             invalidException      = 0x10,
610             severeExceptions   = overflowException | divByZeroException
611                                  | invalidException,
612             allExceptions      = severeExceptions | underflowException
613                                  | inexactException,
614         }
615     }
616     else version (MIPS_Any)
617     {
618         enum : ExceptionMask
619         {
620             inexactException      = 0x0080,
621             divByZeroException    = 0x0400,
622             overflowException     = 0x0200,
623             underflowException    = 0x0100,
624             invalidException      = 0x0800,
625             severeExceptions   = overflowException | divByZeroException
626                                  | invalidException,
627             allExceptions      = severeExceptions | underflowException
628                                  | inexactException,
629         }
630     }
631     else version (SPARC_Any)
632     {
633         enum : ExceptionMask
634         {
635             inexactException      = 0x0800000,
636             divByZeroException    = 0x1000000,
637             overflowException     = 0x4000000,
638             underflowException    = 0x2000000,
639             invalidException      = 0x8000000,
640             severeExceptions   = overflowException | divByZeroException
641                                  | invalidException,
642             allExceptions      = severeExceptions | underflowException
643                                  | inexactException,
644         }
645     }
646     else version (IBMZ_Any)
647     {
648         enum : ExceptionMask
649         {
650             inexactException      = 0x08000000,
651             divByZeroException    = 0x40000000,
652             overflowException     = 0x20000000,
653             underflowException    = 0x10000000,
654             invalidException      = 0x80000000,
655             severeExceptions   = overflowException | divByZeroException
656                                  | invalidException,
657             allExceptions      = severeExceptions | underflowException
658                                  | inexactException,
659         }
660     }
661     else version (X86_Any)
662     {
663         enum : ExceptionMask
664         {
665             inexactException      = 0x20,
666             underflowException    = 0x10,
667             overflowException     = 0x08,
668             divByZeroException    = 0x04,
669             subnormalException    = 0x02,
670             invalidException      = 0x01,
671             severeExceptions   = overflowException | divByZeroException
672                                  | invalidException,
673             allExceptions      = severeExceptions | underflowException
674                                  | inexactException | subnormalException,
675         }
676     }
677     else
678         static assert(false, "Not implemented for this architecture");
679 
680     version (ARM_Any)
681     {
682         static bool hasExceptionTraps_impl() @safe
683         {
684             auto oldState = getControlState();
685             // If exceptions are not supported, we set the bit but read it back as zero
686             // https://sourceware.org/ml/libc-ports/2012-06/msg00091.html
687             setControlState(oldState | divByZeroException);
688             immutable result = (getControlState() & allExceptions) != 0;
689             setControlState(oldState);
690             return result;
691         }
692     }
693 
694     /// Returns: true if the current FPU supports exception trapping
695     @property static bool hasExceptionTraps() @safe pure
696     {
697         version (X86_Any)
698             return true;
699         else version (PPC_Any)
700             return true;
701         else version (MIPS_Any)
702             return true;
703         else version (ARM_Any)
704         {
705             // The hasExceptionTraps_impl function is basically pure,
706             // as it restores all global state
707             auto fptr = ( () @trusted => cast(bool function() @safe
708                 pure nothrow @nogc)&hasExceptionTraps_impl)();
709             return fptr();
710         }
711         else
712             assert(0, "Not yet supported");
713     }
714 
715     /// Enable (unmask) specific hardware exceptions. Multiple exceptions may be ORed together.
716     void enableExceptions(ExceptionMask exceptions) @trusted
717     {
718         assert(hasExceptionTraps);
719         initialize();
720         version (X86_Any)
721             setControlState(getControlState() & ~(exceptions & allExceptions));
722         else
723             setControlState(getControlState() | (exceptions & allExceptions));
724     }
725 
726     /// Disable (mask) specific hardware exceptions. Multiple exceptions may be ORed together.
727     void disableExceptions(ExceptionMask exceptions) @trusted
728     {
729         assert(hasExceptionTraps);
730         initialize();
731         version (X86_Any)
732             setControlState(getControlState() | (exceptions & allExceptions));
733         else
734             setControlState(getControlState() & ~(exceptions & allExceptions));
735     }
736 
737     /// Returns: the exceptions which are currently enabled (unmasked)
738     @property static ExceptionMask enabledExceptions() @trusted pure
739     {
740         assert(hasExceptionTraps);
741         version (X86_Any)
742             return (getControlState() & allExceptions) ^ allExceptions;
743         else
744             return (getControlState() & allExceptions);
745     }
746 
747     ///  Clear all pending exceptions, then restore the original exception state and rounding mode.
748     ~this() @trusted
749     {
750         clearExceptions();
751         if (initialized)
752             setControlState(savedState);
753     }
754 
755 private:
756     ControlState savedState;
757 
758     bool initialized = false;
759 
760     version (ARM_Any)
761     {
762         alias ControlState = uint;
763     }
764     else version (HPPA)
765     {
766         alias ControlState = uint;
767     }
768     else version (PPC_Any)
769     {
770         alias ControlState = uint;
771     }
772     else version (RISCV_Any)
773     {
774         alias ControlState = uint;
775     }
776     else version (MIPS_Any)
777     {
778         alias ControlState = uint;
779     }
780     else version (SPARC_Any)
781     {
782         alias ControlState = ulong;
783     }
784     else version (IBMZ_Any)
785     {
786         alias ControlState = uint;
787     }
788     else version (X86_Any)
789     {
790         alias ControlState = ushort;
791     }
792     else
793         static assert(false, "Not implemented for this architecture");
794 
795     void initialize() @safe
796     {
797         // BUG: This works around the absence of this() constructors.
798         if (initialized) return;
799         clearExceptions();
800         savedState = getControlState();
801         initialized = true;
802     }
803 
804     // Clear all pending exceptions
805     static void clearExceptions() @safe
806     {
807         version (IeeeFlagsSupport)
808             resetIeeeFlags();
809         else
810             static assert(false, "Not implemented for this architecture");
811     }
812 
813     // Read from the control register
814     package(std.math) static ControlState getControlState() @trusted pure
815     {
816         version (D_InlineAsm_X86)
817         {
818             short cont;
819             asm pure nothrow @nogc
820             {
821                 xor EAX, EAX;
822                 fstcw cont;
823             }
824             return cont;
825         }
826         else version (D_InlineAsm_X86_64)
827         {
828             short cont;
829             asm pure nothrow @nogc
830             {
831                 xor RAX, RAX;
832                 fstcw cont;
833             }
834             return cont;
835         }
836         else version (RISCV_Any)
837         {
838             mixin(`
839             ControlState cont;
840             asm pure nothrow @nogc
841             {
842                 "frcsr %0" : "=r" (cont);
843             }
844             return cont;
845             `);
846         }
847         else
848             assert(0, "Not yet supported");
849     }
850 
851     // Set the control register
852     package(std.math) static void setControlState(ControlState newState) @trusted
853     {
854         version (InlineAsm_X86_Any)
855         {
856             asm nothrow @nogc
857             {
858                 fclex;
859                 fldcw newState;
860             }
861 
862             // Also update MXCSR, SSE's control register.
863             if (haveSSE)
864             {
865                 uint mxcsr;
866                 asm nothrow @nogc { stmxcsr mxcsr; }
867 
868                 /* In the FPU control register, rounding mode is in bits 10 and
869                 11. In MXCSR it's in bits 13 and 14. */
870                 mxcsr &= ~(roundingMask << 3);             // delete old rounding mode
871                 mxcsr |= (newState & roundingMask) << 3;   // write new rounding mode
872 
873                 /* In the FPU control register, masks are bits 0 through 5.
874                 In MXCSR they're 7 through 12. */
875                 mxcsr &= ~(allExceptions << 7);            // delete old masks
876                 mxcsr |= (newState & allExceptions) << 7;  // write new exception masks
877 
878                 asm nothrow @nogc { ldmxcsr mxcsr; }
879             }
880         }
881         else version (RISCV_Any)
882         {
883             mixin(`
884             asm pure nothrow @nogc
885             {
886                 "fscsr %0" : : "r" (newState);
887             }
888             `);
889         }
890         else
891             assert(0, "Not yet supported");
892     }
893 }
894 
895 ///
896 @safe unittest
897 {
898     import std.math.rounding : lrint;
899 
900     FloatingPointControl fpctrl;
901 
902     fpctrl.rounding = FloatingPointControl.roundDown;
903     assert(lrint(1.5) == 1.0);
904 
905     fpctrl.rounding = FloatingPointControl.roundUp;
906     assert(lrint(1.4) == 2.0);
907 
908     fpctrl.rounding = FloatingPointControl.roundToNearest;
909     assert(lrint(1.5) == 2.0);
910 }
911 
912 @safe unittest
913 {
914     void ensureDefaults()
915     {
916         assert(FloatingPointControl.rounding
917                == FloatingPointControl.roundToNearest);
918         if (FloatingPointControl.hasExceptionTraps)
919             assert(FloatingPointControl.enabledExceptions == 0);
920     }
921 
922     {
923         FloatingPointControl ctrl;
924     }
925     ensureDefaults();
926 
927     {
928         FloatingPointControl ctrl;
929         ctrl.rounding = FloatingPointControl.roundDown;
930         assert(FloatingPointControl.rounding == FloatingPointControl.roundDown);
931     }
932     ensureDefaults();
933 
934     if (FloatingPointControl.hasExceptionTraps)
935     {
936         FloatingPointControl ctrl;
937         ctrl.enableExceptions(FloatingPointControl.divByZeroException
938                               | FloatingPointControl.overflowException);
939         assert(ctrl.enabledExceptions ==
940                (FloatingPointControl.divByZeroException
941                 | FloatingPointControl.overflowException));
942 
943         ctrl.rounding = FloatingPointControl.roundUp;
944         assert(FloatingPointControl.rounding == FloatingPointControl.roundUp);
945     }
946     ensureDefaults();
947 }
948 
949 @safe unittest // rounding
950 {
951     import std.meta : AliasSeq;
952 
953     static T addRound(T)(uint rm)
954     {
955         pragma(inline, false);
956         FloatingPointControl fpctrl;
957         fpctrl.rounding = rm;
958         T x = 1;
959         x = forceAddOp(x, 0.1L);
960         return x;
961     }
962 
963     static T subRound(T)(uint rm)
964     {
965         pragma(inline, false);
966         FloatingPointControl fpctrl;
967         fpctrl.rounding = rm;
968         T x = -1;
969         x = forceSubOp(x, 0.1L);
970         return x;
971     }
972 
973     static foreach (T; AliasSeq!(float, double, real))
974     {{
975         /* Be careful with changing the rounding mode, it interferes
976          * with common subexpressions. Changing rounding modes should
977          * be done with separate functions that are not inlined.
978          */
979 
980         {
981             T u = addRound!(T)(FloatingPointControl.roundUp);
982             T d = addRound!(T)(FloatingPointControl.roundDown);
983             T z = addRound!(T)(FloatingPointControl.roundToZero);
984 
985             assert(u > d);
986             assert(z == d);
987         }
988 
989         {
990             T u = subRound!(T)(FloatingPointControl.roundUp);
991             T d = subRound!(T)(FloatingPointControl.roundDown);
992             T z = subRound!(T)(FloatingPointControl.roundToZero);
993 
994             assert(u > d);
995             assert(z == u);
996         }
997     }}
998 }
999 
1000 } // FloatingPointControlSupport
1001 
1002 version (StdUnittest)
1003 {
1004     // These helpers are intended to avoid constant propagation by the optimizer.
1005     pragma(inline, false) private @safe
1006     {
1007         T forceAddOp(T)(T x, T y) { return x + y; }
1008         T forceSubOp(T)(T x, T y) { return x - y; }
1009         T forceMulOp(T)(T x, T y) { return x * y; }
1010         T forceDivOp(T)(T x, T y) { return x / y; }
1011     }
1012 }