diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h --- a/libc/src/__support/FPUtil/FPBits.h +++ b/libc/src/__support/FPUtil/FPBits.h @@ -128,6 +128,23 @@ return int(get_unbiased_exponent()) - EXPONENT_BIAS; } + // If the number is subnormal, the exponent is treated as if it were the + // minimum exponent for a normal number. This is to keep continuity between + // the normal and subnormal ranges, but it causes problems for functions where + // values are calculated from the exponent, since just subtracting the bias + // will give a slightly incorrect result. Additionally, zero has an exponent + // of zero, and that should actually be treated as zero. + LIBC_INLINE int get_explicit_exponent() const { + const int unbiased_exp = int(get_unbiased_exponent()); + if (is_zero()) { + return 0; + } else if (unbiased_exp == 0) { + return 1 - EXPONENT_BIAS; + } else { + return unbiased_exp - EXPONENT_BIAS; + } + } + LIBC_INLINE bool is_zero() const { // Remove sign bit by shift return (bits << 1) == 0; diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h --- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h +++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h @@ -135,11 +135,26 @@ } LIBC_INLINE int get_exponent() const { - if (get_unbiased_exponent() == 0) - return int(1) - EXPONENT_BIAS; return int(get_unbiased_exponent()) - EXPONENT_BIAS; } + // If the number is subnormal, the exponent is treated as if it were the + // minimum exponent for a normal number. This is to keep continuity between + // the normal and subnormal ranges, but it causes problems for functions where + // values are calculated from the exponent, since just subtracting the bias + // will give a slightly incorrect result. Additionally, zero has an exponent + // of zero, and that should actually be treated as zero. + LIBC_INLINE int get_explicit_exponent() const { + const int unbiased_exp = int(get_unbiased_exponent()); + if (is_zero()) { + return 0; + } else if (unbiased_exp == 0) { + return 1 - EXPONENT_BIAS; + } else { + return unbiased_exp - EXPONENT_BIAS; + } + } + LIBC_INLINE bool is_zero() const { return get_unbiased_exponent() == 0 && get_mantissa() == 0 && get_implicit_bit() == 0;