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/generic/sqrt_80_bit_long_double.h b/libc/src/__support/FPUtil/generic/sqrt_80_bit_long_double.h --- a/libc/src/__support/FPUtil/generic/sqrt_80_bit_long_double.h +++ b/libc/src/__support/FPUtil/generic/sqrt_80_bit_long_double.h @@ -60,7 +60,7 @@ // sqrt( negative numbers ) = NaN return FPBits::build_quiet_nan(ONE >> 1); } else { - int x_exp = bits.get_exponent(); + int x_exp = bits.get_explicit_exponent(); UIntType x_mant = bits.get_mantissa(); // Step 1a: Normalize denormal input 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; diff --git a/libc/src/__support/float_to_string.h b/libc/src/__support/float_to_string.h --- a/libc/src/__support/float_to_string.h +++ b/libc/src/__support/float_to_string.h @@ -423,18 +423,9 @@ public: LIBC_INLINE constexpr FloatToString(T init_float) : float_bits(init_float) { is_negative = float_bits.get_sign(); - exponent = float_bits.get_exponent(); + exponent = float_bits.get_explicit_exponent(); mantissa = float_bits.get_explicit_mantissa(); - // Handle the exponent for numbers with a 0 exponent. - if (exponent == -EXP_BIAS) { - if (mantissa > 0) { // Subnormals - ++exponent; - } else { // Zeroes - exponent = 0; - } - } - // Adjust for the width of the mantissa. exponent -= MANT_WIDTH; diff --git a/libc/src/stdio/printf_core/float_dec_converter.h b/libc/src/stdio/printf_core/float_dec_converter.h --- a/libc/src/stdio/printf_core/float_dec_converter.h +++ b/libc/src/stdio/printf_core/float_dec_converter.h @@ -483,7 +483,7 @@ // signed because later we use -MANT_WIDTH constexpr int32_t MANT_WIDTH = fputil::MantissaWidth::VALUE; bool is_negative = float_bits.get_sign(); - int exponent = float_bits.get_exponent(); + int exponent = float_bits.get_explicit_exponent(); char sign_char = 0; @@ -626,7 +626,7 @@ // signed because later we use -MANT_WIDTH constexpr int32_t MANT_WIDTH = fputil::MantissaWidth::VALUE; bool is_negative = float_bits.get_sign(); - int exponent = float_bits.get_exponent(); + int exponent = float_bits.get_explicit_exponent(); MantissaInt mantissa = float_bits.get_explicit_mantissa(); const char a = (to_conv.conv_name & 32) | 'A'; @@ -787,7 +787,7 @@ // signed because later we use -MANT_WIDTH constexpr int32_t MANT_WIDTH = fputil::MantissaWidth::VALUE; bool is_negative = float_bits.get_sign(); - int exponent = float_bits.get_exponent(); + int exponent = float_bits.get_explicit_exponent(); MantissaInt mantissa = float_bits.get_explicit_mantissa(); // From the standard: Let P (init_precision) equal the precision if nonzero, 6 diff --git a/libc/src/stdio/printf_core/float_hex_converter.h b/libc/src/stdio/printf_core/float_hex_converter.h --- a/libc/src/stdio/printf_core/float_hex_converter.h +++ b/libc/src/stdio/printf_core/float_hex_converter.h @@ -39,24 +39,21 @@ MantissaInt mantissa; bool is_inf_or_nan; uint32_t mantissa_width; - int exponent_bias; if (to_conv.length_modifier == LengthModifier::L) { mantissa_width = fputil::MantissaWidth::VALUE; - exponent_bias = fputil::FPBits::EXPONENT_BIAS; fputil::FPBits::UIntType float_raw = to_conv.conv_val_raw; fputil::FPBits float_bits(float_raw); is_negative = float_bits.get_sign(); - exponent = float_bits.get_exponent(); + exponent = float_bits.get_explicit_exponent(); mantissa = float_bits.get_explicit_mantissa(); is_inf_or_nan = float_bits.is_inf_or_nan(); } else { mantissa_width = fputil::MantissaWidth::VALUE; - exponent_bias = fputil::FPBits::EXPONENT_BIAS; fputil::FPBits::UIntType float_raw = static_cast::UIntType>(to_conv.conv_val_raw); fputil::FPBits float_bits(float_raw); is_negative = float_bits.get_sign(); - exponent = float_bits.get_exponent(); + exponent = float_bits.get_explicit_exponent(); mantissa = float_bits.get_explicit_mantissa(); is_inf_or_nan = float_bits.is_inf_or_nan(); } @@ -74,14 +71,6 @@ FormatFlags::SPACE_PREFIX) sign_char = ' '; - // Handle the exponent for numbers with a 0 exponent - if (exponent == -exponent_bias) { - if (mantissa > 0) // Subnormals - ++exponent; - else // Zeroes - exponent = 0; - } - constexpr size_t BITS_IN_HEX_DIGIT = 4; // This is to handle situations where the mantissa isn't an even number of hex