// David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2020 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.09.14 #pragma once #include namespace WwiseGTE { template class IEEEBinary { public: // For generic access of the template types. using FloatType = Float; using UIntType = UInt; // Construction from an encoding. Copy constructor, destructor, and // assignment operator are implicitly generated. For the 3-parameter // constructor, see the comments for SetEncoding(...). ~IEEEBinary() = default; IEEEBinary() = default; IEEEBinary(IEEEBinary const& object) : encoding(object.encoding) { } IEEEBinary(UInt inEncoding) : encoding(inEncoding) { } IEEEBinary(UInt inSign, UInt inBiased, UInt inTrailing) { SetEncoding(inSign, inBiased, inTrailing); } IEEEBinary(Float inNumber) : number(inNumber) { } // Implicit conversion to floating-point type. inline operator UInt () const { return encoding; } inline operator Float () const { return number; } // Assignment. IEEEBinary& operator= (IEEEBinary const& object) { encoding = object.encoding; return *this; } // Special constants. static int const NUM_ENCODING_BITS = NumBits; static int const NUM_EXPONENT_BITS = NumBits - Precision; static int const NUM_SIGNIFICAND_BITS = Precision; static int const NUM_TRAILING_BITS = Precision - 1; static int const EXPONENT_BIAS = (1 << (NUM_EXPONENT_BITS - 1)) - 1; static int const MAX_BIASED_EXPONENT = (1 << NUM_EXPONENT_BITS) - 1; static int const MIN_SUB_EXPONENT = 1 - EXPONENT_BIAS; static int const MIN_EXPONENT = MIN_SUB_EXPONENT - NUM_TRAILING_BITS; static int const SIGN_SHIFT = NumBits - 1; static UInt const SIGN_MASK = (UInt(1) << (NumBits - 1)); static UInt const NOT_SIGN_MASK = UInt(~SIGN_MASK); static UInt const TRAILING_MASK = (UInt(1) << NUM_TRAILING_BITS) - 1; static UInt const EXPONENT_MASK = NOT_SIGN_MASK & ~TRAILING_MASK; static UInt const NAN_QUIET_MASK = (UInt(1) << (NUM_TRAILING_BITS - 1)); static UInt const NAN_PAYLOAD_MASK = (TRAILING_MASK >> 1); static UInt const MAX_TRAILING = TRAILING_MASK; static UInt const SUP_TRAILING = (UInt(1) << NUM_TRAILING_BITS); static UInt const POS_ZERO = UInt(0); static UInt const NEG_ZERO = SIGN_MASK; static UInt const MIN_SUBNORMAL = UInt(1); static UInt const MAX_SUBNORMAL = TRAILING_MASK; static UInt const MIN_NORMAL = SUP_TRAILING; static UInt const MAX_NORMAL = NOT_SIGN_MASK & ~SUP_TRAILING; static UInt const POS_INFINITY = EXPONENT_MASK; static UInt const NEG_INFINITY = SIGN_MASK | EXPONENT_MASK; // The types of numbers. enum Classification { CLASS_NEG_INFINITY, CLASS_NEG_SUBNORMAL, CLASS_NEG_NORMAL, CLASS_NEG_ZERO, CLASS_POS_ZERO, CLASS_POS_SUBNORMAL, CLASS_POS_NORMAL, CLASS_POS_INFINITY, CLASS_QUIET_NAN, CLASS_SIGNALING_NAN }; Classification GetClassification() const { UInt sign, biased, trailing; GetEncoding(sign, biased, trailing); if (biased == 0) { if (trailing == 0) { return (sign != 0 ? CLASS_NEG_ZERO : CLASS_POS_ZERO); } else { return (sign != 0 ? CLASS_NEG_SUBNORMAL : CLASS_POS_SUBNORMAL); } } else if (biased < MAX_BIASED_EXPONENT) { return (sign != 0 ? CLASS_NEG_NORMAL : CLASS_POS_NORMAL); } else if (trailing == 0) { return (sign != 0 ? CLASS_NEG_INFINITY : CLASS_POS_INFINITY); } else if (trailing & NAN_QUIET_MASK) { return CLASS_QUIET_NAN; } else { return CLASS_SIGNALING_NAN; } } bool IsZero() const { return encoding == POS_ZERO || encoding == NEG_ZERO; } bool IsSignMinus() const { return (encoding & SIGN_MASK) != 0; } bool IsSubnormal() const { return GetBiased() == 0 && GetTrailing() > 0; } bool IsNormal() const { UInt biased = GetBiased(); return 0 < biased&& biased < MAX_BIASED_EXPONENT; } bool IsFinite() const { return GetBiased() < MAX_BIASED_EXPONENT; } bool IsInfinite() const { return GetBiased() == MAX_BIASED_EXPONENT && GetTrailing() == 0; } bool IsNaN() const { return GetBiased() == MAX_BIASED_EXPONENT && GetTrailing() != 0; } bool IsSignalingNaN() const { UInt trailing = GetTrailing(); return GetBiased() == MAX_BIASED_EXPONENT && (trailing & NAN_QUIET_MASK) == 0 && (trailing & NAN_PAYLOAD_MASK) != 0; } // Get neighboring numbers. UInt GetNextUp() const { UInt sign, biased, trailing; GetEncoding(sign, biased, trailing); if (biased == 0) { if (trailing == 0) { // The next-up for both -0 and +0 is MIN_SUBNORMAL. return MIN_SUBNORMAL; } else { if (sign != 0) { // When trailing is 1, 'this' is -MIN_SUBNORMAL and next-up // is -0. --trailing; return SIGN_MASK | trailing; } else { // When trailing is MAX_TRAILING, 'this' is MAX_SUBNORMAL // and next-up is MIN_NORMAL. ++trailing; return trailing; } } } else if (biased < MAX_BIASED_EXPONENT) { UInt nonnegative = (encoding & NOT_SIGN_MASK); if (sign != 0) { --nonnegative; return SIGN_MASK | nonnegative; } else { ++nonnegative; return nonnegative; } } else if (trailing == 0) { if (sign != 0) { // The next-up of -INFINITY is -MAX_NORMAL. return SIGN_MASK | MAX_NORMAL; } else { // The next-up of +INFINITY is +INFINITY. return POS_INFINITY; } } else if (trailing & NAN_QUIET_MASK) { // TODO. The IEEE standard is not clear what to do here. Figure // out what it means. return 0; } else { // TODO. The IEEE standard is not clear what to do here. Figure // out what it means. return 0; } } UInt GetNextDown() const { UInt sign, biased, trailing; GetEncoding(sign, biased, trailing); if (biased == 0) { if (trailing == 0) { // The next-down for both -0 and +0 is -MIN_SUBNORMAL. return SIGN_MASK | MIN_SUBNORMAL; } else { if (sign == 0) { // When trailing is 1, 'this' is MIN_SUBNORMAL and next-down // is +0. --trailing; return trailing; } else { // When trailing is MAX_TRAILING, 'this' is -MAX_SUBNORMAL // and next-down is -MIN_NORMAL. ++trailing; return SIGN_MASK | trailing; } } } else if (biased < MAX_BIASED_EXPONENT) { UInt nonnegative = (encoding & NOT_SIGN_MASK); if (sign == 0) { --nonnegative; return nonnegative; } else { ++nonnegative; return SIGN_MASK | nonnegative; } } else if (trailing == 0) { if (sign == 0) { // The next-down of +INFINITY is +MAX_NORMAL. return MAX_NORMAL; } else { // The next-down of -INFINITY is -INFINITY. return NEG_INFINITY; } } else if (trailing & NAN_QUIET_MASK) { // TODO. The IEEE standard is not clear what to do here. Figure // out what it means. return 0; } else { // TODO. The IEEE standard is not clear what to do here. Figure // out what it means. return 0; } } // Encode and decode the binary representation. The sign is 0 (number // is nonnegative) or 1 (number is negative). The biased exponent is // in the range [0,MAX_BIASED_EXPONENT]. The trailing significand is // in the range [0,MAX_TRAILING]. UInt GetSign() const { return (encoding & SIGN_MASK) >> SIGN_SHIFT; } UInt GetBiased() const { return (encoding & EXPONENT_MASK) >> NUM_TRAILING_BITS; } UInt GetTrailing() const { return encoding & TRAILING_MASK; } void SetEncoding(UInt sign, UInt biased, UInt trailing) { encoding = (sign << SIGN_SHIFT) | (biased << NUM_TRAILING_BITS) | trailing; } void GetEncoding(UInt& sign, UInt& biased, UInt& trailing) const { sign = GetSign(); biased = GetBiased(); trailing = GetTrailing(); } // Access for direct manipulation of the object. union { UInt encoding; Float number; }; }; using IEEEBinary32 = IEEEBinary; using IEEEBinary64 = IEEEBinary; }