You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

453 lines
14 KiB

// David Eberly, Geometric Tools, Redmond WA 98052
// Copyright (c) 1998-2021
// 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.02.12
#pragma once
#include <cstdint>
namespace gte
{
template <typename Float, typename UInt, int NumBits, int Precision>
class IEEEBinary
{
public:
// For generic access of the template types.
using FloatType = Float;
using UIntType = UInt;
// The standard constructors, copy semantics and move semantics are
// all default behavior. The default constructor does not initialize
// its union member.
IEEEBinary() = default;
~IEEEBinary() = default;
IEEEBinary(IEEEBinary const&) = default;
IEEEBinary(IEEEBinary&&) = default;
IEEEBinary& operator=(IEEEBinary const&) = default;
IEEEBinary& operator=(IEEEBinary&&) = default;
// Construction from primitive elements.
IEEEBinary(UInt inEncoding)
:
encoding(inEncoding)
{
}
IEEEBinary(UInt inSign, UInt inBiased, UInt inTrailing)
{
SetEncoding(inSign, inBiased, inTrailing);
}
IEEEBinary(Float inNumber)
:
number(inNumber)
{
}
// Implicit conversions to integer type or floating-point type.
inline operator UInt () const
{
return encoding;
}
inline operator Float () const
{
return number;
}
// 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 class Classification
{
NEG_INFINITY,
NEG_SUBNORMAL,
NEG_NORMAL,
NEG_ZERO,
POS_ZERO,
POS_SUBNORMAL,
POS_NORMAL,
POS_INFINITY,
QUIET_NAN,
SIGNALING_NAN
};
Classification GetClassification() const
{
UInt sign, biased, trailing;
GetEncoding(sign, biased, trailing);
if (biased == 0)
{
if (trailing == 0)
{
if (sign != 0)
{
return Classification::NEG_ZERO;
}
else
{
return Classification::POS_ZERO;
}
}
else
{
if (sign != 0)
{
return Classification::NEG_SUBNORMAL;
}
else
{
return Classification::POS_SUBNORMAL;
}
}
}
else if (biased < MAX_BIASED_EXPONENT)
{
if (sign != 0)
{
return Classification::NEG_NORMAL;
}
else
{
return Classification::POS_NORMAL;
}
}
else if (trailing == 0)
{
if (sign != 0)
{
return Classification::NEG_INFINITY;
}
else
{
return Classification::POS_INFINITY;
}
}
else if (trailing & NAN_QUIET_MASK)
{
return Classification::QUIET_NAN;
}
else
{
return Classification::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 IsQuietNaN() const
{
UInt trailing = GetTrailing();
return GetBiased() == MAX_BIASED_EXPONENT
&& (trailing & NAN_QUIET_MASK) != 0
&& (trailing & NAN_PAYLOAD_MASK) != 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)
{
// The number is a quiet NAN, possibly with payload.
// Just return the number itself.
return encoding;
}
else
{
// The number is a signaling NAN, possibly with payload.
// Just return the number itself.
return encoding;
}
}
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)
{
// The number is a quiet NAN, possibly with payload.
// Just return the number itself.
return encoding;
}
else
{
// The number is a signaling NAN, possibly with payload.
// Just return the number itself.
return encoding;
}
}
// 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<float, uint32_t, 32, 24>;
// NUM_ENCODING_BITS = 32
// NUM_EXPONENT_BITS = 8
// NUM_SIGNIFICAND_BITS = 24
// NUM_TRAILING_BITS = 23
// EXPONENT_BIAS = 127
// MAX_BIASED_EXPONENT = 255
// MIN_SUB_EXPONENT = -126
// MIN_EXPONENT = -149
// SIGN_SHIFT = 31
// SIGN_MASK = 0x80000000
// NOT_SIGN_MASK = 0x7FFFFFFF
// TRAILING_MASK = 0x007FFFFF
// EXPONENT_MASK = 0x7F800000
// NAN_QUIET_MASK = 0x00400000
// NAN_PAYLOAD_MASK = 0x003FFFFF
// MAX_TRAILING = 0x007FFFFF
// SUP_TRAILING = 0x00800000
// POS_ZERO = 0x00000000
// NEG_ZERO = 0x80000000
// MIN_SUBNORMAL = 0x00000001
// MAX_SUBNORMAL = 0x007FFFFF
// MIN_NORMAL = 0x00800000
// MAX_NORMAL = 0x7F7FFFFF
// POS_INFINITY = 0x7F800000
// NEG_INFINITY = 0xFF800000
using IEEEBinary64 = IEEEBinary<double, uint64_t, 64, 53>;
// NUM_ENCODING_BITS = 64
// NUM_EXPONENT_BITS = 11
// NUM_SIGNIFICAND_BITS = 53
// NUM_TRAILING_BITS = 52
// EXPONENT_BIAS = 1023
// MAX_BIASED_EXPONENT = 2047
// MIN_SUB_EXPONENT = -1022
// MIN_EXPONENT = -1074
// SIGN_SHIFT = 63
// SIGN_MASK = 0x8000000000000000
// NOT_SIGN_MASK = 0x7FFFFFFFFFFFFFFF
// TRAILING_MASK = 0x000FFFFFFFFFFFFF
// EXPONENT_MASK = 0x7FF0000000000000
// NAN_QUIET_MASK = 0x0008000000000000
// NAN_PAYLOAD_MASK = 0x0007FFFFFFFFFFFF
// MAX_TRAILING = 0x000FFFFFFFFFFFFF
// SUP_TRAILING = 0x0010000000000000
// POS_ZERO = 0x0000000000000000
// NEG_ZERO = 0x8000000000000000
// MIN_SUBNORMAL = 0x0000000000000001
// MAX_SUBNORMAL = 0x000FFFFFFFFFFFFF
// MIN_NORMAL = 0x0010000000000000
// MAX_NORMAL = 0x7FEFFFFFFFFFFFFF
// POS_INFINITY = 0x7FF0000000000000
// NEG_INFINITY = 0xFFF0000000000000
}