From db745986f264cc8f36c2d56bff7f6a2fbcfcc591 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 20 Mar 2022 07:51:46 -0700 Subject: [PATCH] Workaround broken std::numeric_limits --- include/fmt/format-inl.h | 45 +++++++++++++++++----------------------- include/fmt/format.h | 14 +++++++------ 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 5c95269e..65d46781 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -212,42 +212,36 @@ template struct basic_fp { constexpr basic_fp() : f(0), e(0) {} constexpr basic_fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} - // Constructs fp from an IEEE754 floating-point number. It is a template to - // prevent compile errors on systems where n is not IEEE754. + // Constructs fp from an IEEE754 floating-point number. template FMT_CONSTEXPR basic_fp(Float n) { assign(n); } - template - using is_supported = - bool_constant<(std::numeric_limits::is_iec559 && - std::numeric_limits::digits <= 113) || - is_float128::value>; - - // Assigns d to this and return true iff predecessor is closer than successor. - template ::value)> - FMT_CONSTEXPR bool assign(Float n) { - // Assume float is in the format [sign][exponent][significand]. + // Assigns n to this and return true iff predecessor is closer than successor. + template FMT_CONSTEXPR bool assign(Float n) { + static_assert((std::numeric_limits::is_iec559 && + std::numeric_limits::digits <= 113) || + is_float128::value, + "unsupported FP"); + // Assume Float is in the format [sign][exponent][significand]. using carrier_uint = typename dragonbox::float_info::carrier_uint; - const carrier_uint implicit_bit = carrier_uint(1) - << detail::num_significand_bits(); - const carrier_uint significand_mask = implicit_bit - 1; + constexpr auto num_float_significand_bits = + detail::num_significand_bits(); + constexpr auto implicit_bit = carrier_uint(1) << num_float_significand_bits; + constexpr auto significand_mask = implicit_bit - 1; auto u = bit_cast(n); f = static_cast(u & significand_mask); - int biased_e = static_cast((u & exponent_mask()) >> - detail::num_significand_bits()); + auto biased_e = static_cast((u & exponent_mask()) >> + num_float_significand_bits); // The predecessor is closer if n is a normalized power of 2 (f == 0) other // than the smallest normalized number (biased_e > 1). - bool is_predecessor_closer = f == 0 && biased_e > 1; + auto is_predecessor_closer = f == 0 && biased_e > 1; if (biased_e != 0) - f += static_cast(implicit_bit); + f += static_cast(implicit_bit); else biased_e = 1; // Subnormals use biased exponent 1 (min exponent). - const int exponent_bias = std::numeric_limits::max_exponent - 1; - e = biased_e - exponent_bias - std::numeric_limits::digits + 1; + e = biased_e - exponent_bias() - num_float_significand_bits; + if (!has_implicit_bit()) ++e; return is_predecessor_closer; } - - template ::value)> - bool assign(Float) = delete; }; using fp = basic_fp; @@ -1917,8 +1911,7 @@ template decimal_fp to_decimal(T x) noexcept { static_cast((br & exponent_mask()) >> num_significand_bits()); if (exponent != 0) { // Check if normal. - const int exponent_bias = std::numeric_limits::max_exponent - 1; - exponent -= exponent_bias + num_significand_bits(); + exponent -= exponent_bias() + num_significand_bits(); // Shorter interval case; proceed like Schubfach. // In fact, when exponent == 1 and significand == 0, the interval is diff --git a/include/fmt/format.h b/include/fmt/format.h index 2d18621a..c57c0e6e 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1308,6 +1308,11 @@ constexpr auto exponent_mask() -> return ((uint(1) << dragonbox::float_info::exponent_bits) - 1) << num_significand_bits(); } +template constexpr auto exponent_bias() -> int { + // std::numeric_limits may not support __float128. + return is_float128() ? 16383 + : std::numeric_limits::max_exponent - 1; +} // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. template @@ -2370,13 +2375,8 @@ template OutputIt { if (is_constant_evaluated()) return write(out, value, basic_format_specs()); - if (const_check(!is_supported_floating_point(value))) return out; - using floaty = conditional_t::value, double, T>; - using uint = typename dragonbox::float_info::carrier_uint; - auto bits = bit_cast(value); - auto fspecs = float_specs(); if (detail::signbit(value)) { fspecs.sign = sign::minus; @@ -2384,8 +2384,10 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { } constexpr auto specs = basic_format_specs(); + using floaty = conditional_t::value, double, T>; + using uint = typename dragonbox::float_info::carrier_uint; uint mask = exponent_mask(); - if ((bits & mask) == mask) + if ((bit_cast(value) & mask) == mask) return write_nonfinite(out, std::isnan(value), specs, fspecs); auto dec = dragonbox::to_decimal(static_cast(value));