diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index c20d328f..c5ff5188 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -245,8 +245,9 @@ struct fp { template explicit FMT_CONSTEXPR fp(Float n) { assign(n); } template - using is_supported = bool_constant; + using is_supported = bool_constant::digits == 64>; // Assigns d to this and return true iff predecessor is closer than successor. template ::value)> @@ -255,13 +256,13 @@ struct fp { const int num_float_significand_bits = detail::num_significand_bits(); const uint64_t implicit_bit = 1ULL << num_float_significand_bits; - const uint64_t significand_mask = implicit_bit - 1; - constexpr bool is_double = sizeof(Float) == sizeof(uint64_t); - auto u = bit_cast>(n); + using carrier_uint = typename dragonbox::float_info::carrier_uint; + const carrier_uint significand_mask = implicit_bit - 1; + auto u = bit_cast(n); f = u & significand_mask; - const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask; int biased_e = - static_cast((u & exponent_mask) >> num_float_significand_bits); + static_cast((u & exponent_mask()) >> + dragonbox::float_info::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; @@ -2120,10 +2121,9 @@ FMT_CONSTEXPR20 inline void format_dragon(fp value, bool is_predecessor_closer, // is closer) to make lower and upper integers. This eliminates multiplication // by 2 during later computations. int shift = is_predecessor_closer ? 2 : 1; - uint64_t significand = value.f << shift; if (value.e >= 0) { - numerator.assign(significand); - numerator <<= value.e; + numerator.assign(value.f); + numerator <<= value.e + shift; lower.assign(1); lower <<= value.e; if (shift != 1) { @@ -2141,11 +2141,13 @@ FMT_CONSTEXPR20 inline void format_dragon(fp value, bool is_predecessor_closer, upper_store <<= 1; upper = &upper_store; } - numerator *= significand; + numerator *= value.f; + numerator <<= shift; denominator.assign(1); denominator <<= shift - value.e; } else { - numerator.assign(significand); + numerator.assign(value.f); + numerator <<= shift; denominator.assign_pow10(exp10); denominator <<= shift - value.e; lower.assign(1); @@ -2261,7 +2263,7 @@ FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision, // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf. const int min_exp = -60; // alpha in Grisu. int cached_exp10 = 0; // K in Grisu. - fp normalized = normalize(fp(value)); + fp normalized = normalize(fp(convert_float(value))); const auto cached_pow = get_cached_power( min_exp - (normalized.e + fp::num_significand_bits), cached_exp10); normalized = normalized * cached_pow; @@ -2278,8 +2280,9 @@ FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision, } if (use_dragon) { auto f = fp(); - bool is_predecessor_closer = - specs.binary32 ? f.assign(static_cast(value)) : f.assign(value); + bool is_predecessor_closer = specs.binary32 + ? f.assign(static_cast(value)) + : f.assign(convert_float(value)); // Limit precision to the maximum possible number of significant digits in // an IEEE754 double because we don't need to generate zeros. const int max_double_digits = 767; diff --git a/include/fmt/format.h b/include/fmt/format.h index 989b05b8..80c36df0 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1198,7 +1198,7 @@ class utf8_to_utf16 { namespace dragonbox { // Type-specific information that Dragonbox uses. -template struct float_info; +template struct float_info; template <> struct float_info { using carrier_uint = uint32_t; @@ -1246,6 +1246,15 @@ template <> struct float_info { static const int max_trailing_zeros = 16; }; +// 80-bit extended precision long double. +template +struct float_info::value && + std::numeric_limits::digits == 64>> { + using carrier_uint = detail::uint128_t; + static const int significand_bits = 64; + static const int exponent_bits = 15; +}; + template struct decimal_fp { using significand_type = typename float_info::carrier_uint; significand_type significand; @@ -1295,11 +1304,14 @@ template auto snprintf_float(T value, int precision, float_specs specs, buffer& buf) -> int; -template constexpr auto promote_float(T value) -> T { - return value; -} -constexpr auto promote_float(float value) -> double { - return static_cast(value); +template +using convert_float_result = + conditional_t::value || sizeof(T) == sizeof(double), + double, T>; + +template +constexpr auto convert_float(T value) -> convert_float_result { + return static_cast>(value); } template @@ -2207,7 +2219,7 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value, memory_buffer buffer; if (fspecs.format == float_format::hex) { if (fspecs.sign) buffer.push_back(detail::sign(fspecs.sign)); - snprintf_float(promote_float(value), specs.precision, fspecs, buffer); + snprintf_float(convert_float(value), specs.precision, fspecs, buffer); return write_bytes(out, {buffer.data(), buffer.size()}, specs); } @@ -2222,7 +2234,7 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value, } if (const_check(std::is_same())) fspecs.binary32 = true; if (!is_fast_float()) fspecs.fallback = true; - int exp = format_float(promote_float(value), precision, fspecs, buffer); + int exp = format_float(convert_float(value), precision, fspecs, buffer); fspecs.precision = precision; auto fp = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; return write_float(out, fp, specs, fspecs, loc);