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 }