module std.math.traits; import std.traits; enum RealFormat { ieeeHalf, ieeeSingle, ieeeDouble, ieeeExtended, // x87 80-bit real ieeeExtended53, // x87 real rounded to precision of double. ibmExtended, // IBM 128-bit extended ieeeQuadruple, } template floatTraits(T) { import std.traits : Unqual; // EXPMASK is a ushort mask to select the exponent portion (without sign) // EXPSHIFT is the number of bits the exponent is left-shifted by in its ushort // EXPBIAS is the exponent bias - 1 (exp == EXPBIAS yields ×2^-1). // EXPPOS_SHORT is the index of the exponent when represented as a ushort array. // SIGNPOS_BYTE is the index of the sign when represented as a ubyte array. // RECIP_EPSILON is the value such that (smallest_subnormal) * RECIP_EPSILON == T.min_normal enum Unqual!T RECIP_EPSILON = (1/T.epsilon); static if (T.mant_dig == 24) { // Single precision float enum ushort EXPMASK = 0x7F80; enum ushort EXPSHIFT = 7; enum ushort EXPBIAS = 0x3F00; enum uint EXPMASK_INT = 0x7F80_0000; enum uint MANTISSAMASK_INT = 0x007F_FFFF; enum realFormat = RealFormat.ieeeSingle; version (LittleEndian) { enum EXPPOS_SHORT = 1; enum SIGNPOS_BYTE = 3; } else { enum EXPPOS_SHORT = 0; enum SIGNPOS_BYTE = 0; } } else static if (T.mant_dig == 53) { static if (T.sizeof == 8) { // Double precision float, or real == double enum ushort EXPMASK = 0x7FF0; enum ushort EXPSHIFT = 4; enum ushort EXPBIAS = 0x3FE0; enum uint EXPMASK_INT = 0x7FF0_0000; enum uint MANTISSAMASK_INT = 0x000F_FFFF; // for the MSB only enum ulong MANTISSAMASK_LONG = 0x000F_FFFF_FFFF_FFFF; enum realFormat = RealFormat.ieeeDouble; version (LittleEndian) { enum EXPPOS_SHORT = 3; enum SIGNPOS_BYTE = 7; } else { enum EXPPOS_SHORT = 0; enum SIGNPOS_BYTE = 0; } } else static if (T.sizeof == 12) { // Intel extended real80 rounded to double enum ushort EXPMASK = 0x7FFF; enum ushort EXPSHIFT = 0; enum ushort EXPBIAS = 0x3FFE; enum realFormat = RealFormat.ieeeExtended53; version (LittleEndian) { enum EXPPOS_SHORT = 4; enum SIGNPOS_BYTE = 9; } else { enum EXPPOS_SHORT = 0; enum SIGNPOS_BYTE = 0; } } else static assert(false, "No traits support for " ~ T.stringof); } else static if (T.mant_dig == 64) { // Intel extended real80 enum ushort EXPMASK = 0x7FFF; enum ushort EXPSHIFT = 0; enum ushort EXPBIAS = 0x3FFE; enum realFormat = RealFormat.ieeeExtended; version (LittleEndian) { enum EXPPOS_SHORT = 4; enum SIGNPOS_BYTE = 9; } else { enum EXPPOS_SHORT = 0; enum SIGNPOS_BYTE = 0; } } else static if (T.mant_dig == 113) { // Quadruple precision float enum ushort EXPMASK = 0x7FFF; enum ushort EXPSHIFT = 0; enum ushort EXPBIAS = 0x3FFE; enum realFormat = RealFormat.ieeeQuadruple; version (LittleEndian) { enum EXPPOS_SHORT = 7; enum SIGNPOS_BYTE = 15; } else { enum EXPPOS_SHORT = 0; enum SIGNPOS_BYTE = 0; } } else static if (T.mant_dig == 106) { // IBM Extended doubledouble enum ushort EXPMASK = 0x7FF0; enum ushort EXPSHIFT = 4; enum realFormat = RealFormat.ibmExtended; // For IBM doubledouble the larger magnitude double comes first. // It's really a double[2] and arrays don't index differently // between little and big-endian targets. enum DOUBLEPAIR_MSB = 0; enum DOUBLEPAIR_LSB = 1; // The exponent/sign byte is for most significant part. version (LittleEndian) { enum EXPPOS_SHORT = 3; enum SIGNPOS_BYTE = 7; } else { enum EXPPOS_SHORT = 0; enum SIGNPOS_BYTE = 0; } } else static assert(false, "No traits support for " ~ T.stringof); } 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; } } } 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); } } version (LittleEndian) { enum MANTISSA_LSB = 0; enum MANTISSA_MSB = 1; } else { enum MANTISSA_LSB = 1; enum MANTISSA_MSB = 0; }