// Written in the D programming language. /**This is a submodule of $(MREF std, math). It contains several functions for introspection on numerical values. Copyright: Copyright The D Language Foundation 2000 - 2011.License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston, Conversion of CEPHES math library to D by Iain Buclaw and David NadlingerSource: $(PHOBOSSRC std/math/traits.d) Macros: NAN = $(RED NAN) PLUSMN = ± INFIN = ∞ */ module std.math.traits; import std.traits : isFloatingPoint, isIntegral, isNumeric, isSigned; /********************************* * Determines if $(D_PARAM x) is NaN. * Params: * x = a floating point number. * Returns: * `true` if $(D_PARAM x) is Nan. */bool isNaN(X)(X x) @nogc @trusted pure nothrowif (isFloatingPoint!(X)){ version (all) { return x != x; } else { /* Code kept for historical context. At least on Intel, the simple test x != x uses one dedicated instruction (ucomiss/ucomisd) that runs in one cycle. Code for 80- and 128-bits is larger but still smaller than the integrals-based solutions below. Future revisions may enable the code below conditionally depending on hardware. */ alias F = floatTraits!(X); static if (F.realFormat == RealFormat.ieeeSingle) { const uint p = *cast(uint *)&x; // Sign bit (MSB) is irrelevant so mask it out. // Next 8 bits should be all set. // At least one bit among the least significant 23 bits should be set. return (p & 0x7FFF_FFFF) > 0x7F80_0000; } else static if (F.realFormat == RealFormat.ieeeDouble) { const ulong p = *cast(ulong *)&x; // Sign bit (MSB) is irrelevant so mask it out. // Next 11 bits should be all set. // At least one bit among the least significant 52 bits should be set. return (p & 0x7FFF_FFFF_FFFF_FFFF) > 0x7FF0_0000_0000_0000; } else static if (F.realFormat == RealFormat.ieeeExtended || F.realFormat == RealFormat.ieeeExtended53) { const ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; const ulong ps = *cast(ulong *)&x; return e == F.EXPMASK && ps & 0x7FFF_FFFF_FFFF_FFFF; // not infinity } else static if (F.realFormat == RealFormat.ieeeQuadruple) { const ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; const ulong psLsb = (cast(ulong *)&x)[MANTISSA_LSB]; const ulong psMsb = (cast(ulong *)&x)[MANTISSA_MSB]; return e == F.EXPMASK && (psLsb | (psMsb& 0x0000_FFFF_FFFF_FFFF)) != 0; } else { return x != x; } }} ///@safe pure nothrow @nogc unittest{ assert( isNaN(float.init)); assert( isNaN(-double.init)); assert( isNaN(real.nan)); assert( isNaN(-real.nan)); assert(!isNaN(cast(float) 53.6)); assert(!isNaN(cast(real)-53.6));} @safe pure nothrow @nogc unittest{ import std.meta : AliasSeq; static foreach (T; AliasSeq!(float, double, real)) {{ // CTFE-able tests assert(isNaN(T.init)); assert(isNaN(-T.init)); assert(isNaN(T.nan)); assert(isNaN(-T.nan)); assert(!isNaN(T.infinity)); assert(!isNaN(-T.infinity)); assert(!isNaN(cast(T) 53.6)); assert(!isNaN(cast(T)-53.6)); // Runtime tests shared T f; f = T.init; assert(isNaN(f)); assert(isNaN(-f)); f = T.nan; assert(isNaN(f)); assert(isNaN(-f)); f = T.infinity; assert(!isNaN(f)); assert(!isNaN(-f)); f = cast(T) 53.6; assert(!isNaN(f)); assert(!isNaN(-f)); }}} /********************************* * Determines if $(D_PARAM x) is finite. * Params: * x = a floating point number. * Returns: * `true` if $(D_PARAM x) is finite. */bool isFinite(X)(X x) @trusted pure nothrow @nogc{ import std.math.traits : floatTraits, RealFormat; static if (__traits(isFloating, X)) if (__ctfe) return x == x && x != X.infinity && x != -X.infinity; alias F = floatTraits!(X); ushort* pe = cast(ushort *)&x; return (pe[F.EXPPOS_SHORT] & F.EXPMASK) != F.EXPMASK;} ///@safe pure nothrow @nogc unittest{ assert( isFinite(1.23f)); assert( isFinite(float.max)); assert( isFinite(float.min_normal)); assert(!isFinite(float.nan)); assert(!isFinite(float.infinity));} @safe pure nothrow @nogc unittest{ assert(isFinite(1.23)); assert(isFinite(double.max)); assert(isFinite(double.min_normal)); assert(!isFinite(double.nan)); assert(!isFinite(double.infinity)); assert(isFinite(1.23L)); assert(isFinite(real.max)); assert(isFinite(real.min_normal)); assert(!isFinite(real.nan)); assert(!isFinite(real.infinity)); //CTFE static assert(isFinite(1.23)); static assert(isFinite(double.max)); static assert(isFinite(double.min_normal)); static assert(!isFinite(double.nan)); static assert(!isFinite(double.infinity)); static assert(isFinite(1.23L)); static assert(isFinite(real.max)); static assert(isFinite(real.min_normal)); static assert(!isFinite(real.nan)); static assert(!isFinite(real.infinity));} /********************************* * Determines if $(D_PARAM x) is normalized. * * A normalized number must not be zero, subnormal, infinite nor $(NAN). * * Params: * x = a floating point number. * Returns: * `true` if $(D_PARAM x) is normalized. */ /* Need one for each format because subnormal floats might * be converted to normal reals. */bool isNormal(X)(X x) @trusted pure nothrow @nogc{ import std.math.traits : floatTraits, RealFormat; static if (__traits(isFloating, X)) if (__ctfe) return (x <= -X.min_normal && x != -X.infinity) || (x >= X.min_normal && x != X.infinity); alias F = floatTraits!(X); ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; return (e != F.EXPMASK && e != 0);} ///@safe pure nothrow @nogc unittest{ float f = 3; double d = 500; real e = 10e+48; assert(isNormal(f)); assert(isNormal(d)); assert(isNormal(e)); f = d = e = 0; assert(!isNormal(f)); assert(!isNormal(d)); assert(!isNormal(e)); assert(!isNormal(real.infinity)); assert(isNormal(-real.max)); assert(!isNormal(real.min_normal/4)); } @safe pure nothrow @nogc unittest{ // CTFE enum float f = 3; enum double d = 500; enum real e = 10e+48; static assert(isNormal(f)); static assert(isNormal(d)); static assert(isNormal(e)); static assert(!isNormal(0.0f)); static assert(!isNormal(0.0)); static assert(!isNormal(0.0L)); static assert(!isNormal(real.infinity)); static assert(isNormal(-real.max)); static assert(!isNormal(real.min_normal/4));} /********************************* * Determines if $(D_PARAM x) is subnormal. * * Subnormals (also known as "denormal number"), have a 0 exponent * and a 0 most significant mantissa bit. * * Params: * x = a floating point number. * Returns: * `true` if $(D_PARAM x) is a denormal number. */bool isSubnormal(X)(X x) @trusted pure nothrow @nogc{ import std.math.traits : floatTraits, RealFormat, MANTISSA_MSB, MANTISSA_LSB; static if (__traits(isFloating, X)) if (__ctfe) return -X.min_normal < x && x < X.min_normal; /* Need one for each format because subnormal floats might be converted to normal reals. */ alias F = floatTraits!(X); static if (F.realFormat == RealFormat.ieeeSingle) { uint *p = cast(uint *)&x; return (*p & F.EXPMASK_INT) == 0 && *p & F.MANTISSAMASK_INT; } else static if (F.realFormat == RealFormat.ieeeDouble) { uint *p = cast(uint *)&x; return (p[MANTISSA_MSB] & F.EXPMASK_INT) == 0 && (p[MANTISSA_LSB] || p[MANTISSA_MSB] & F.MANTISSAMASK_INT); } else static if (F.realFormat == RealFormat.ieeeQuadruple) { ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; long* ps = cast(long *)&x; return (e == 0 && ((ps[MANTISSA_LSB]|(ps[MANTISSA_MSB]& 0x0000_FFFF_FFFF_FFFF)) != 0)); } else static if (F.realFormat == RealFormat.ieeeExtended || F.realFormat == RealFormat.ieeeExtended53) { ushort* pe = cast(ushort *)&x; long* ps = cast(long *)&x; return (pe[F.EXPPOS_SHORT] & F.EXPMASK) == 0 && *ps > 0; } else { static assert(false, "Not implemented for this architecture"); }} ///@safe pure nothrow @nogc unittest{ import std.meta : AliasSeq; static foreach (T; AliasSeq!(float, double, real)) {{ T f; for (f = 1.0; !isSubnormal(f); f /= 2) assert(f != 0); }}} @safe pure nothrow @nogc unittest{ static bool subnormalTest(T)() { T f; for (f = 1.0; !isSubnormal(f); f /= 2) if (f == 0) return false; return true; } static assert(subnormalTest!float()); static assert(subnormalTest!double()); static assert(subnormalTest!real());} /********************************* * Determines if $(D_PARAM x) is $(PLUSMN)$(INFIN). * Params: * x = a floating point number. * Returns: * `true` if $(D_PARAM x) is $(PLUSMN)$(INFIN). */bool isInfinity(X)(X x) @nogc @trusted pure nothrowif (isFloatingPoint!(X)){ import std.math.traits : floatTraits, RealFormat, MANTISSA_MSB, MANTISSA_LSB; alias F = floatTraits!(X); static if (F.realFormat == RealFormat.ieeeSingle) { return ((*cast(uint *)&x) & 0x7FFF_FFFF) == 0x7F80_0000; } else static if (F.realFormat == RealFormat.ieeeDouble) { return ((*cast(ulong *)&x) & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FF0_0000_0000_0000; } else static if (F.realFormat == RealFormat.ieeeExtended || F.realFormat == RealFormat.ieeeExtended53) { const ushort e = cast(ushort)(F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]); const ulong ps = *cast(ulong *)&x; // On Motorola 68K, infinity can have hidden bit = 1 or 0. On x86, it is always 1. return e == F.EXPMASK && (ps & 0x7FFF_FFFF_FFFF_FFFF) == 0; } else static if (F.realFormat == RealFormat.ieeeQuadruple) { const long psLsb = (cast(long *)&x)[MANTISSA_LSB]; const long psMsb = (cast(long *)&x)[MANTISSA_MSB]; return (psLsb == 0) && (psMsb & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FFF_0000_0000_0000; } else { return (x < -X.max) || (X.max < x); }} ///@nogc @safe pure nothrow unittest{ assert(!isInfinity(float.init)); assert(!isInfinity(-float.init)); assert(!isInfinity(float.nan)); assert(!isInfinity(-float.nan)); assert(isInfinity(float.infinity)); assert(isInfinity(-float.infinity)); assert(isInfinity(-1.0f / 0.0f));} @safe pure nothrow @nogc unittest{ // CTFE-able tests assert(!isInfinity(double.init)); assert(!isInfinity(-double.init)); assert(!isInfinity(double.nan)); assert(!isInfinity(-double.nan)); assert(isInfinity(double.infinity)); assert(isInfinity(-double.infinity)); assert(isInfinity(-1.0 / 0.0)); assert(!isInfinity(real.init)); assert(!isInfinity(-real.init)); assert(!isInfinity(real.nan)); assert(!isInfinity(-real.nan)); assert(isInfinity(real.infinity)); assert(isInfinity(-real.infinity)); assert(isInfinity(-1.0L / 0.0L)); // Runtime tests shared float f; f = float.init; assert(!isInfinity(f)); assert(!isInfinity(-f)); f = float.nan; assert(!isInfinity(f)); assert(!isInfinity(-f)); f = float.infinity; assert(isInfinity(f)); assert(isInfinity(-f)); f = (-1.0f / 0.0f); assert(isInfinity(f)); shared double d; d = double.init; assert(!isInfinity(d)); assert(!isInfinity(-d)); d = double.nan; assert(!isInfinity(d)); assert(!isInfinity(-d)); d = double.infinity; assert(isInfinity(d)); assert(isInfinity(-d)); d = (-1.0 / 0.0); assert(isInfinity(d)); shared real e; e = real.init; assert(!isInfinity(e)); assert(!isInfinity(-e)); e = real.nan; assert(!isInfinity(e)); assert(!isInfinity(-e)); e = real.infinity; assert(isInfinity(e)); assert(isInfinity(-e)); e = (-1.0L / 0.0L); assert(isInfinity(e));} @nogc @safe pure nothrow unittest{ import std.meta : AliasSeq; static bool foo(T)(inout T x) { return isInfinity(x); } foreach (T; AliasSeq!(float, double, real)) { assert(!foo(T(3.14f))); assert(foo(T.infinity)); }} /********************************* * Is the binary representation of x identical to y? */bool isIdentical(real x, real y) @trusted pure nothrow @nogc{ if (__ctfe) { if (x !is y) return false; if (x == x) return true; // If not NaN `is` implies identical representation. static if (double.mant_dig != real.mant_dig) { // Works because we are in CTFE and there is no way in CTFE to set more // bits of NaN payload than can fit in a double, and since 2.087 // changed real.init to be non-signaling I *think* there is no way in // CTFE for a real to be a signaling NaN unless real and double have // the same representation so real's bits can be manipulated directly. double d1 = x, d2 = y; } else { // Alias to avoid converting signaling to quiet. alias d1 = x; alias d2 = y; } return *cast(long*) &d1 == *cast(long*) &d2; } // We're doing a bitwise comparison so the endianness is irrelevant. long* pxs = cast(long *)&x; long* pys = cast(long *)&y; alias F = floatTraits!(real); static if (F.realFormat == RealFormat.ieeeDouble) { return pxs[0] == pys[0]; } else static if (F.realFormat == RealFormat.ieeeQuadruple) {Lines 1–500 of 1062. Load more
// Written in the D programming language. /** This is a submodule of $(MREF std, math). It contains several functions for introspection on numerical values. Copyright: Copyright The D Language Foundation 2000 - 2011. License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston, Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger Source: $(PHOBOSSRC std/math/traits.d) Macros: NAN = $(RED NAN) PLUSMN = ± INFIN = ∞ */ module std.math.traits; import std.traits : isFloatingPoint, isIntegral, isNumeric, isSigned; /********************************* * Determines if $(D_PARAM x) is NaN. * Params: * x = a floating point number. * Returns: * `true` if $(D_PARAM x) is Nan. */ bool isNaN(X)(X x) @nogc @trusted pure nothrow if (isFloatingPoint!(X)) { version (all) { return x != x; } else { /* Code kept for historical context. At least on Intel, the simple test x != x uses one dedicated instruction (ucomiss/ucomisd) that runs in one cycle. Code for 80- and 128-bits is larger but still smaller than the integrals-based solutions below. Future revisions may enable the code below conditionally depending on hardware. */ alias F = floatTraits!(X); static if (F.realFormat == RealFormat.ieeeSingle) { const uint p = *cast(uint *)&x; // Sign bit (MSB) is irrelevant so mask it out. // Next 8 bits should be all set. // At least one bit among the least significant 23 bits should be set. return (p & 0x7FFF_FFFF) > 0x7F80_0000; } else static if (F.realFormat == RealFormat.ieeeDouble) { const ulong p = *cast(ulong *)&x; // Sign bit (MSB) is irrelevant so mask it out. // Next 11 bits should be all set. // At least one bit among the least significant 52 bits should be set. return (p & 0x7FFF_FFFF_FFFF_FFFF) > 0x7FF0_0000_0000_0000; } else static if (F.realFormat == RealFormat.ieeeExtended || F.realFormat == RealFormat.ieeeExtended53) { const ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; const ulong ps = *cast(ulong *)&x; return e == F.EXPMASK && ps & 0x7FFF_FFFF_FFFF_FFFF; // not infinity } else static if (F.realFormat == RealFormat.ieeeQuadruple) { const ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; const ulong psLsb = (cast(ulong *)&x)[MANTISSA_LSB]; const ulong psMsb = (cast(ulong *)&x)[MANTISSA_MSB]; return e == F.EXPMASK && (psLsb | (psMsb& 0x0000_FFFF_FFFF_FFFF)) != 0; } else { return x != x; } } } /// @safe pure nothrow @nogc unittest { assert( isNaN(float.init)); assert( isNaN(-double.init)); assert( isNaN(real.nan)); assert( isNaN(-real.nan)); assert(!isNaN(cast(float) 53.6)); assert(!isNaN(cast(real)-53.6)); } @safe pure nothrow @nogc unittest { import std.meta : AliasSeq; static foreach (T; AliasSeq!(float, double, real)) {{ // CTFE-able tests assert(isNaN(T.init)); assert(isNaN(-T.init)); assert(isNaN(T.nan)); assert(isNaN(-T.nan)); assert(!isNaN(T.infinity)); assert(!isNaN(-T.infinity)); assert(!isNaN(cast(T) 53.6)); assert(!isNaN(cast(T)-53.6)); // Runtime tests shared T f; f = T.init; assert(isNaN(f)); assert(isNaN(-f)); f = T.nan; assert(isNaN(f)); assert(isNaN(-f)); f = T.infinity; assert(!isNaN(f)); assert(!isNaN(-f)); f = cast(T) 53.6; assert(!isNaN(f)); assert(!isNaN(-f)); }} } /********************************* * Determines if $(D_PARAM x) is finite. * Params: * x = a floating point number. * Returns: * `true` if $(D_PARAM x) is finite. */ bool isFinite(X)(X x) @trusted pure nothrow @nogc { import std.math.traits : floatTraits, RealFormat; static if (__traits(isFloating, X)) if (__ctfe) return x == x && x != X.infinity && x != -X.infinity; alias F = floatTraits!(X); ushort* pe = cast(ushort *)&x; return (pe[F.EXPPOS_SHORT] & F.EXPMASK) != F.EXPMASK; } /// @safe pure nothrow @nogc unittest { assert( isFinite(1.23f)); assert( isFinite(float.max)); assert( isFinite(float.min_normal)); assert(!isFinite(float.nan)); assert(!isFinite(float.infinity)); } @safe pure nothrow @nogc unittest { assert(isFinite(1.23)); assert(isFinite(double.max)); assert(isFinite(double.min_normal)); assert(!isFinite(double.nan)); assert(!isFinite(double.infinity)); assert(isFinite(1.23L)); assert(isFinite(real.max)); assert(isFinite(real.min_normal)); assert(!isFinite(real.nan)); assert(!isFinite(real.infinity)); //CTFE static assert(isFinite(1.23)); static assert(isFinite(double.max)); static assert(isFinite(double.min_normal)); static assert(!isFinite(double.nan)); static assert(!isFinite(double.infinity)); static assert(isFinite(1.23L)); static assert(isFinite(real.max)); static assert(isFinite(real.min_normal)); static assert(!isFinite(real.nan)); static assert(!isFinite(real.infinity)); } /********************************* * Determines if $(D_PARAM x) is normalized. * * A normalized number must not be zero, subnormal, infinite nor $(NAN). * * Params: * x = a floating point number. * Returns: * `true` if $(D_PARAM x) is normalized. */ /* Need one for each format because subnormal floats might * be converted to normal reals. */ bool isNormal(X)(X x) @trusted pure nothrow @nogc { import std.math.traits : floatTraits, RealFormat; static if (__traits(isFloating, X)) if (__ctfe) return (x <= -X.min_normal && x != -X.infinity) || (x >= X.min_normal && x != X.infinity); alias F = floatTraits!(X); ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; return (e != F.EXPMASK && e != 0); } /// @safe pure nothrow @nogc unittest { float f = 3; double d = 500; real e = 10e+48; assert(isNormal(f)); assert(isNormal(d)); assert(isNormal(e)); f = d = e = 0; assert(!isNormal(f)); assert(!isNormal(d)); assert(!isNormal(e)); assert(!isNormal(real.infinity)); assert(isNormal(-real.max)); assert(!isNormal(real.min_normal/4)); } @safe pure nothrow @nogc unittest { // CTFE enum float f = 3; enum double d = 500; enum real e = 10e+48; static assert(isNormal(f)); static assert(isNormal(d)); static assert(isNormal(e)); static assert(!isNormal(0.0f)); static assert(!isNormal(0.0)); static assert(!isNormal(0.0L)); static assert(!isNormal(real.infinity)); static assert(isNormal(-real.max)); static assert(!isNormal(real.min_normal/4)); } /********************************* * Determines if $(D_PARAM x) is subnormal. * * Subnormals (also known as "denormal number"), have a 0 exponent * and a 0 most significant mantissa bit. * * Params: * x = a floating point number. * Returns: * `true` if $(D_PARAM x) is a denormal number. */ bool isSubnormal(X)(X x) @trusted pure nothrow @nogc { import std.math.traits : floatTraits, RealFormat, MANTISSA_MSB, MANTISSA_LSB; static if (__traits(isFloating, X)) if (__ctfe) return -X.min_normal < x && x < X.min_normal; /* Need one for each format because subnormal floats might be converted to normal reals. */ alias F = floatTraits!(X); static if (F.realFormat == RealFormat.ieeeSingle) { uint *p = cast(uint *)&x; return (*p & F.EXPMASK_INT) == 0 && *p & F.MANTISSAMASK_INT; } else static if (F.realFormat == RealFormat.ieeeDouble) { uint *p = cast(uint *)&x; return (p[MANTISSA_MSB] & F.EXPMASK_INT) == 0 && (p[MANTISSA_LSB] || p[MANTISSA_MSB] & F.MANTISSAMASK_INT); } else static if (F.realFormat == RealFormat.ieeeQuadruple) { ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; long* ps = cast(long *)&x; return (e == 0 && ((ps[MANTISSA_LSB]|(ps[MANTISSA_MSB]& 0x0000_FFFF_FFFF_FFFF)) != 0)); } else static if (F.realFormat == RealFormat.ieeeExtended || F.realFormat == RealFormat.ieeeExtended53) { ushort* pe = cast(ushort *)&x; long* ps = cast(long *)&x; return (pe[F.EXPPOS_SHORT] & F.EXPMASK) == 0 && *ps > 0; } else { static assert(false, "Not implemented for this architecture"); } } /// @safe pure nothrow @nogc unittest { import std.meta : AliasSeq; static foreach (T; AliasSeq!(float, double, real)) {{ T f; for (f = 1.0; !isSubnormal(f); f /= 2) assert(f != 0); }} } @safe pure nothrow @nogc unittest { static bool subnormalTest(T)() { T f; for (f = 1.0; !isSubnormal(f); f /= 2) if (f == 0) return false; return true; } static assert(subnormalTest!float()); static assert(subnormalTest!double()); static assert(subnormalTest!real()); } /********************************* * Determines if $(D_PARAM x) is $(PLUSMN)$(INFIN). * Params: * x = a floating point number. * Returns: * `true` if $(D_PARAM x) is $(PLUSMN)$(INFIN). */ bool isInfinity(X)(X x) @nogc @trusted pure nothrow if (isFloatingPoint!(X)) { import std.math.traits : floatTraits, RealFormat, MANTISSA_MSB, MANTISSA_LSB; alias F = floatTraits!(X); static if (F.realFormat == RealFormat.ieeeSingle) { return ((*cast(uint *)&x) & 0x7FFF_FFFF) == 0x7F80_0000; } else static if (F.realFormat == RealFormat.ieeeDouble) { return ((*cast(ulong *)&x) & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FF0_0000_0000_0000; } else static if (F.realFormat == RealFormat.ieeeExtended || F.realFormat == RealFormat.ieeeExtended53) { const ushort e = cast(ushort)(F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]); const ulong ps = *cast(ulong *)&x; // On Motorola 68K, infinity can have hidden bit = 1 or 0. On x86, it is always 1. return e == F.EXPMASK && (ps & 0x7FFF_FFFF_FFFF_FFFF) == 0; } else static if (F.realFormat == RealFormat.ieeeQuadruple) { const long psLsb = (cast(long *)&x)[MANTISSA_LSB]; const long psMsb = (cast(long *)&x)[MANTISSA_MSB]; return (psLsb == 0) && (psMsb & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FFF_0000_0000_0000; } else { return (x < -X.max) || (X.max < x); } } /// @nogc @safe pure nothrow unittest { assert(!isInfinity(float.init)); assert(!isInfinity(-float.init)); assert(!isInfinity(float.nan)); assert(!isInfinity(-float.nan)); assert(isInfinity(float.infinity)); assert(isInfinity(-float.infinity)); assert(isInfinity(-1.0f / 0.0f)); } @safe pure nothrow @nogc unittest { // CTFE-able tests assert(!isInfinity(double.init)); assert(!isInfinity(-double.init)); assert(!isInfinity(double.nan)); assert(!isInfinity(-double.nan)); assert(isInfinity(double.infinity)); assert(isInfinity(-double.infinity)); assert(isInfinity(-1.0 / 0.0)); assert(!isInfinity(real.init)); assert(!isInfinity(-real.init)); assert(!isInfinity(real.nan)); assert(!isInfinity(-real.nan)); assert(isInfinity(real.infinity)); assert(isInfinity(-real.infinity)); assert(isInfinity(-1.0L / 0.0L)); // Runtime tests shared float f; f = float.init; assert(!isInfinity(f)); assert(!isInfinity(-f)); f = float.nan; assert(!isInfinity(f)); assert(!isInfinity(-f)); f = float.infinity; assert(isInfinity(f)); assert(isInfinity(-f)); f = (-1.0f / 0.0f); assert(isInfinity(f)); shared double d; d = double.init; assert(!isInfinity(d)); assert(!isInfinity(-d)); d = double.nan; assert(!isInfinity(d)); assert(!isInfinity(-d)); d = double.infinity; assert(isInfinity(d)); assert(isInfinity(-d)); d = (-1.0 / 0.0); assert(isInfinity(d)); shared real e; e = real.init; assert(!isInfinity(e)); assert(!isInfinity(-e)); e = real.nan; assert(!isInfinity(e)); assert(!isInfinity(-e)); e = real.infinity; assert(isInfinity(e)); assert(isInfinity(-e)); e = (-1.0L / 0.0L); assert(isInfinity(e)); } @nogc @safe pure nothrow unittest { import std.meta : AliasSeq; static bool foo(T)(inout T x) { return isInfinity(x); } foreach (T; AliasSeq!(float, double, real)) { assert(!foo(T(3.14f))); assert(foo(T.infinity)); } } /********************************* * Is the binary representation of x identical to y? */ bool isIdentical(real x, real y) @trusted pure nothrow @nogc { if (__ctfe) { if (x !is y) return false; if (x == x) return true; // If not NaN `is` implies identical representation. static if (double.mant_dig != real.mant_dig) { // Works because we are in CTFE and there is no way in CTFE to set more // bits of NaN payload than can fit in a double, and since 2.087 // changed real.init to be non-signaling I *think* there is no way in // CTFE for a real to be a signaling NaN unless real and double have // the same representation so real's bits can be manipulated directly. double d1 = x, d2 = y; } else { // Alias to avoid converting signaling to quiet. alias d1 = x; alias d2 = y; } return *cast(long*) &d1 == *cast(long*) &d2; } // We're doing a bitwise comparison so the endianness is irrelevant. long* pxs = cast(long *)&x; long* pys = cast(long *)&y; alias F = floatTraits!(real); static if (F.realFormat == RealFormat.ieeeDouble) { return pxs[0] == pys[0]; } else static if (F.realFormat == RealFormat.ieeeQuadruple) {