From dce8e49b4f7c0613f0f4ae36795a6dd543e0658f Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 13 Sep 2020 09:03:03 -0700 Subject: [PATCH] Handle float in fallback formatter --- include/fmt/format-inl.h | 43 ++++++++++++++++++++++++---------------- test/format-test.cc | 1 + 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 0b12ba6b..7c58cfe2 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -363,6 +363,10 @@ class fp { private: using significand_type = uint64_t; + template + using is_supported_float = bool_constant; + public: significand_type f; int e; @@ -385,32 +389,35 @@ class fp { template explicit fp(Double d) { assign(d); } // Assigns d to this and return true iff predecessor is closer than successor. - template - bool assign(Double d) { - // Assume double is in the format [sign][exponent][significand]. - using limits = std::numeric_limits; + template ::value)> + bool assign(Float d) { + // Assume float is in the format [sign][exponent][significand]. + using limits = std::numeric_limits; + const int float_significand_size = limits::digits - 1; const int exponent_size = - bits::value - double_significand_size - 1; // -1 for sign - const uint64_t significand_mask = implicit_bit - 1; + bits::value - float_significand_size - 1; // -1 for sign + const uint64_t float_implicit_bit = 1ULL << float_significand_size; + const uint64_t significand_mask = float_implicit_bit - 1; const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask; const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1; - auto u = bit_cast(d); + constexpr bool is_double = sizeof(Float) == sizeof(uint64_t); + auto u = bit_cast>(d); f = u & significand_mask; int biased_e = - static_cast((u & exponent_mask) >> double_significand_size); + static_cast((u & exponent_mask) >> float_significand_size); // Predecessor is closer if d 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; if (biased_e != 0) - f += implicit_bit; + f += float_implicit_bit; else biased_e = 1; // Subnormals use biased exponent 1 (min exponent). - e = biased_e - exponent_bias - double_significand_size; + e = biased_e - exponent_bias - float_significand_size; return is_predecessor_closer; } - template - bool assign(Double) { + template ::value)> + bool assign(Float) { *this = fp(); return false; } @@ -983,7 +990,8 @@ struct grisu_shortest_handler { // Floating-Point Printout ((FPP)^2) algorithm by Steele & White: // https://fmt.dev/p372-steele.pdf. template -void fallback_format(Double d, int num_digits, buffer& buf, int& exp10) { +void fallback_format(Double d, int num_digits, bool binary32, buffer& buf, + int& exp10) { bigint numerator; // 2 * R in (FPP)^2. bigint denominator; // 2 * S in (FPP)^2. // lower and upper are differences between value and corresponding boundaries. @@ -994,8 +1002,9 @@ void fallback_format(Double d, int num_digits, buffer& buf, int& exp10) { // Shift numerator and denominator by an extra bit or two (if lower boundary // is closer) to make lower and upper integers. This eliminates multiplication // by 2 during later computations. - // TODO: handle float - int shift = value.assign(d) ? 2 : 1; + int shift = (binary32 ? value.assign(static_cast(d)) : value.assign(d)) + ? 2 + : 1; uint64_t significand = value.f << shift; if (value.e >= 0) { numerator.assign(significand); @@ -1149,7 +1158,7 @@ int format_float(T value, int precision, float_specs specs, buffer& buf) { boundaries.upper - boundaries.lower, exp, handler); if (result == digits::error) { exp += handler.size - cached_exp10 - 1; - fallback_format(value, -1, buf, exp); + fallback_format(value, -1, specs.binary32, buf, exp); return exp; } buf.try_resize(to_unsigned(handler.size)); @@ -1161,7 +1170,7 @@ int format_float(T value, int precision, float_specs specs, buffer& buf) { fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed}; if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error) { exp += handler.size - cached_exp10 - 1; - fallback_format(value, handler.precision, buf, exp); + fallback_format(value, handler.precision, specs.binary32, buf, exp); return exp; } int num_digits = handler.size; diff --git a/test/format-test.cc b/test/format-test.cc index fd1c3ee5..7841c68f 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -955,6 +955,7 @@ TEST(FormatterTest, Precision) { EXPECT_EQ("123.", format("{:#.0f}", 123.0)); EXPECT_EQ("1.23", format("{:.02f}", 1.234)); EXPECT_EQ("0.001", format("{:.1g}", 0.001)); + EXPECT_EQ("1019666400.0", format("{}", 1019666432.0f)); EXPECT_THROW_MSG(format("{0:.2}", reinterpret_cast(0xcafe)), format_error,