private alias haveSSE = core.cpuid.sse;
}
+version (D_SoftFloat)
+{
+ // Some soft float implementations may support IEEE floating flags.
+ // The implementation here supports hardware flags only and is so currently
+ // only available for supported targets.
+}
+else version (X86_Any) version = IeeeFlagsSupport;
+else version (PPC_Any) version = IeeeFlagsSupport;
+else version (RISCV_Any) version = IeeeFlagsSupport;
+else version (MIPS_Any) version = IeeeFlagsSupport;
+else version (ARM_Any) version = IeeeFlagsSupport;
+
+// Struct FloatingPointControl is only available if hardware FP units are available.
+version (D_HardFloat)
+{
+ // FloatingPointControl.clearExceptions() depends on version IeeeFlagsSupport
+ version (IeeeFlagsSupport) version = FloatingPointControlSupport;
+}
+
+version (GNU)
+{
+ // The compiler can unexpectedly rearrange floating point operations and
+ // access to the floating point status flags when optimizing. This means
+ // ieeeFlags tests cannot be reliably checked in optimized code.
+ // See https://github.com/ldc-developers/ldc/issues/888
+}
+else
+{
+ version = IeeeFlagsUnittest;
+ version = FloatingPointControlUnittest;
+}
+
version (unittest)
{
import core.stdc.stdio; // : sprintf;
if (isNaN(x))
return x;
if (x > OF)
- {
- if (__ctfe)
- return real.infinity;
- else
- return real.max * copysign(real.max, real.infinity);
- }
+ return real.infinity;
if (x < UF)
- {
- if (__ctfe)
- return 0.0;
- else
- return real.min_normal * copysign(real.min_normal, 0.0);
- }
+ return 0.0;
// Express: e^^x = e^^g * 2^^n
// = e^^g * e^^(n * LOG2E)
// Special cases. Raises an overflow flag, except in the case
// for CTFE, where there are no hardware controls.
if (x > OF)
- {
- if (__ctfe)
- return real.infinity;
- else
- return real.max * copysign(real.max, real.infinity);
- }
+ return real.infinity;
if (x == 0.0)
return x;
if (x < UF)
if (isNaN(x))
return x;
if (x > OF)
- {
- if (__ctfe)
- return real.infinity;
- else
- return real.max * copysign(real.max, real.infinity);
- }
+ return real.infinity;
if (x < UF)
- {
- if (__ctfe)
- return 0.0;
- else
- return real.min_normal * copysign(real.min_normal, 0.0);
- }
+ return 0.0;
// Separate into integer and fractional parts.
int n = cast(int) floor(x + 0.5);
@system unittest
{
- FloatingPointControl ctrl;
- if (FloatingPointControl.hasExceptionTraps)
- ctrl.disableExceptions(FloatingPointControl.allExceptions);
- ctrl.rounding = FloatingPointControl.roundToNearest;
+ version (FloatingPointControlSupport)
+ {
+ FloatingPointControl ctrl;
+ if (FloatingPointControl.hasExceptionTraps)
+ ctrl.disableExceptions(FloatingPointControl.allExceptions);
+ ctrl.rounding = FloatingPointControl.roundToNearest;
+ }
static if (real.mant_dig == 113)
{
const minEqualDecimalDigits = real.dig - 3;
real x;
- IeeeFlags f;
+ version (IeeeFlagsSupport) IeeeFlags f;
foreach (ref pair; exptestpoints)
{
- resetIeeeFlags();
+ version (IeeeFlagsSupport) resetIeeeFlags();
x = exp(pair[0]);
- f = ieeeFlags;
assert(equalsDigit(x, pair[1], minEqualDecimalDigits));
-
- version (IeeeFlagsSupport)
- {
- // Check the overflow bit
- if (x == real.infinity)
- {
- // don't care about the overflow bit if input was inf
- // (e.g., the LLVM intrinsic doesn't set it on Linux x86_64)
- assert(pair[0] == real.infinity || f.overflow);
- }
- else
- assert(!f.overflow);
- // Check the underflow bit
- assert(f.underflow == (fabs(x) < real.min_normal));
- // Invalid and div by zero shouldn't be affected.
- assert(!f.invalid);
- assert(!f.divByZero);
- }
}
+
// Ideally, exp(0) would not set the inexact flag.
// Unfortunately, fldl2e sets it!
// So it's not realistic to avoid setting it.
assert(exp(0.0L) == 1.0);
// NaN propagation. Doesn't set flags, bcos was already NaN.
- resetIeeeFlags();
- x = exp(real.nan);
- f = ieeeFlags;
- assert(isIdentical(abs(x), real.nan));
- assert(f.flags == 0);
+ version (IeeeFlagsSupport)
+ {
+ resetIeeeFlags();
+ x = exp(real.nan);
+ f = ieeeFlags;
+ assert(isIdentical(abs(x), real.nan));
+ assert(f.flags == 0);
- resetIeeeFlags();
- x = exp(-real.nan);
- f = ieeeFlags;
- assert(isIdentical(abs(x), real.nan));
- assert(f.flags == 0);
+ resetIeeeFlags();
+ x = exp(-real.nan);
+ f = ieeeFlags;
+ assert(isIdentical(abs(x), real.nan));
+ assert(f.flags == 0);
+ }
+ else
+ {
+ x = exp(real.nan);
+ assert(isIdentical(abs(x), real.nan));
+
+ x = exp(-real.nan);
+ assert(isIdentical(abs(x), real.nan));
+ }
x = exp(NaN(0x123));
assert(isIdentical(x, NaN(0x123)));
assert(0, "remquo not implemented");
}
+
+version (IeeeFlagsSupport)
+{
+
/** IEEE exception status flags ('sticky bits')
These flags indicate that an exceptional floating-point condition has occurred.
else
assert(0, "Not yet supported");
}
+
static void resetIeeeFlags() @nogc
{
version (GNU)
{
version (X86_Any)
{
- asm pure nothrow @nogc
+ asm nothrow @nogc
{
"fnclex";
}
if (haveSSE)
{
uint mxcsr;
- asm pure nothrow @nogc
+ asm nothrow @nogc
{
"stmxcsr %0" : "=m" (mxcsr);
}
mxcsr &= ~EXCEPTIONS_MASK;
- asm pure nothrow @nogc
+ asm nothrow @nogc
{
"ldmxcsr %0" : : "m" (mxcsr);
}
{
uint old = FloatingPointControl.getControlState();
old &= ~0b11111; // http://infocenter.arm.com/help/topic/com.arm.doc.ddi0408i/Chdfifdc.html
- asm pure nothrow @nogc
+ asm nothrow @nogc
{
"vmsr FPSCR, %0" : : "r" (old);
}
else
{
uint newValues = 0x0;
- asm pure nothrow @nogc
+ asm nothrow @nogc
{
"fsflags %0" : : "r" (newValues);
}
else
version (InlineAsm_X86_Any)
{
- asm pure nothrow @nogc
+ asm nothrow @nogc
{
fnclex;
}
}
///
-version (GNU)
-{
- // ieeeFlags test disabled, see LDC Issue #888.
-}
-else
+version (IeeeFlagsUnittest)
@system unittest
{
static void func() {
int a = 10 * 10;
}
-
- real a=3.5;
+ pragma(inline, false) static void blockopt(ref real x) {}
+ real a = 3.5;
// Set all the flags to zero
resetIeeeFlags();
assert(!ieeeFlags.divByZero);
+ blockopt(a); // avoid constant propagation by the optimizer
// Perform a division by zero.
- a/=0.0L;
+ a /= 0.0L;
assert(a == real.infinity);
assert(ieeeFlags.divByZero);
+ blockopt(a); // avoid constant propagation by the optimizer
// Create a NaN
- a*=0.0L;
+ a *= 0.0L;
assert(ieeeFlags.invalid);
assert(isNaN(a));
assert(ieeeFlags == f);
}
-version (GNU)
-{
- // ieeeFlags test disabled, see LDC Issue #888.
-}
-else
+version (IeeeFlagsUnittest)
@system unittest
{
import std.meta : AliasSeq;
}
}
-version (X86_Any)
-{
- version = IeeeFlagsSupport;
-}
-else version (PPC_Any)
-{
- version = IeeeFlagsSupport;
-}
-else version (RISCV_Any)
-{
- version = IeeeFlagsSupport;
-}
-else version (MIPS_Any)
-{
- version = IeeeFlagsSupport;
-}
-else version (ARM_Any)
-{
- version = IeeeFlagsSupport;
-}
-
/// Set all of the floating-point status flags to false.
void resetIeeeFlags() @nogc { IeeeFlags.resetIeeeFlags(); }
return IeeeFlags(IeeeFlags.getIeeeFlags());
}
+} // IeeeFlagsSupport
+
+
+version (FloatingPointControlSupport)
+{
+
/** Control the Floating point hardware
Change the IEEE754 floating-point rounding mode and the floating-point
// Clear all pending exceptions
static void clearExceptions() @nogc
{
- resetIeeeFlags();
+ version (IeeeFlagsSupport)
+ resetIeeeFlags();
+ else
+ static assert(false, "Not implemented for this architecture");
}
// Read from the control register
version (D_InlineAsm_X86)
{
short cont;
- asm nothrow @nogc
+ asm pure nothrow @nogc
{
xor EAX, EAX;
fstcw cont;
version (D_InlineAsm_X86_64)
{
short cont;
- asm nothrow @nogc
+ asm pure nothrow @nogc
{
xor RAX, RAX;
fstcw cont;
{
version (X86_Any)
{
- asm pure nothrow @nogc
+ asm nothrow @nogc
{
"fclex; fldcw %0" : : "m" (newState);
}
if (haveSSE)
{
uint mxcsr;
- asm pure nothrow @nogc
+ asm nothrow @nogc
{
"stmxcsr %0" : "=m" (mxcsr);
}
mxcsr &= ~(allExceptions << 7); // delete old masks
mxcsr |= (newState & allExceptions) << 7; // write new exception masks
- asm pure nothrow @nogc
+ asm nothrow @nogc
{
"ldmxcsr %0" : : "m" (mxcsr);
}
}
else version (AArch64)
{
- asm pure nothrow @nogc
+ asm nothrow @nogc
{
"msr FPCR, %0;" : : "r" (newState);
}
return;
else
{
- asm pure nothrow @nogc
+ asm nothrow @nogc
{
"vmsr FPSCR, %0" : : "r" (newState);
}
return;
else
{
- asm pure nothrow @nogc
+ asm nothrow @nogc
{
"fscsr %0" : : "r" (newState);
}
}
}
-version (D_HardFloat) @system unittest
+@system unittest
{
void ensureDefaults()
{
ensureDefaults();
}
-version (D_HardFloat) @system unittest // rounding
+version (FloatingPointControlUnittest)
+@system unittest // rounding
{
import std.meta : AliasSeq;
foreach (T; AliasSeq!(float, double, real))
{
- FloatingPointControl fpctrl;
-
- fpctrl.rounding = FloatingPointControl.roundUp;
- T u = 1;
- u += 0.1;
-
- fpctrl.rounding = FloatingPointControl.roundDown;
- T d = 1;
- d += 0.1;
+ /* Be careful with changing the rounding mode, it interferes
+ * with common subexpressions. Changing rounding modes should
+ * be done with separate functions that are not inlined.
+ */
- fpctrl.rounding = FloatingPointControl.roundToZero;
- T z = 1;
- z += 0.1;
+ {
+ static T addRound(T)(uint rm)
+ {
+ pragma(inline, false) static void blockopt(ref T x) {}
+ pragma(inline, false);
+ FloatingPointControl fpctrl;
+ fpctrl.rounding = rm;
+ T x = 1;
+ blockopt(x); // avoid constant propagation by the optimizer
+ x += 0.1;
+ return x;
+ }
- assert(u > d);
- assert(z == d);
+ T u = addRound!(T)(FloatingPointControl.roundUp);
+ T d = addRound!(T)(FloatingPointControl.roundDown);
+ T z = addRound!(T)(FloatingPointControl.roundToZero);
- fpctrl.rounding = FloatingPointControl.roundUp;
- u = -1;
- u -= 0.1;
+ assert(u > d);
+ assert(z == d);
+ }
- fpctrl.rounding = FloatingPointControl.roundDown;
- d = -1;
- d -= 0.1;
+ {
+ static T subRound(T)(uint rm)
+ {
+ pragma(inline, false) static void blockopt(ref T x) {}
+ pragma(inline, false);
+ FloatingPointControl fpctrl;
+ fpctrl.rounding = rm;
+ T x = -1;
+ blockopt(x); // avoid constant propagation by the optimizer
+ x -= 0.1;
+ return x;
+ }
- fpctrl.rounding = FloatingPointControl.roundToZero;
- z = -1;
- z -= 0.1;
+ T u = subRound!(T)(FloatingPointControl.roundUp);
+ T d = subRound!(T)(FloatingPointControl.roundDown);
+ T z = subRound!(T)(FloatingPointControl.roundToZero);
- assert(u > d);
- assert(z == u);
+ assert(u > d);
+ assert(z == u);
+ }
}
}
+} // FloatingPointControlSupport
+
/*********************************
* Determines if $(D_PARAM x) is NaN.