Fix handling of subnormals in exotic FP

This commit is contained in:
Victor Zverovich 2022-04-17 08:12:38 -07:00
parent 86e27ccb41
commit 5d804ee7fe
2 changed files with 7 additions and 8 deletions

View File

@ -216,7 +216,7 @@ template <typename F> struct basic_fp {
template <typename Float> FMT_CONSTEXPR basic_fp(Float n) { assign(n); } template <typename Float> FMT_CONSTEXPR basic_fp(Float n) { assign(n); }
// Assigns n to this and return true iff predecessor is closer than successor. // Assigns n to this and return true iff predecessor is closer than successor.
template <typename Float> FMT_CONSTEXPR bool assign(Float n) { template <typename Float> FMT_CONSTEXPR auto assign(Float n) -> bool {
static_assert((std::numeric_limits<Float>::is_iec559 && static_assert((std::numeric_limits<Float>::is_iec559 &&
std::numeric_limits<Float>::digits <= 113) || std::numeric_limits<Float>::digits <= 113) ||
is_float128<Float>::value, is_float128<Float>::value,
@ -250,7 +250,7 @@ using fp = basic_fp<unsigned long long>;
template <int SHIFT = 0, typename F> template <int SHIFT = 0, typename F>
FMT_CONSTEXPR basic_fp<F> normalize(basic_fp<F> value) { FMT_CONSTEXPR basic_fp<F> normalize(basic_fp<F> value) {
// Handle subnormals. // Handle subnormals.
const uint64_t implicit_bit = 1ULL << num_significand_bits<double>(); const auto implicit_bit = F(1) << num_significand_bits<double>();
const auto shifted_implicit_bit = implicit_bit << SHIFT; const auto shifted_implicit_bit = implicit_bit << SHIFT;
while ((value.f & shifted_implicit_bit) == 0) { while ((value.f & shifted_implicit_bit) == 0) {
value.f <<= 1; value.f <<= 1;
@ -2212,17 +2212,14 @@ FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision,
unsigned dragon_flags = 0; unsigned dragon_flags = 0;
if (!is_fast_float<Float>()) { if (!is_fast_float<Float>()) {
const auto inv_log2_10 = 0.3010299956639812; // 1 / log2(10) const auto inv_log2_10 = 0.3010299956639812; // 1 / log2(10)
const auto e = basic_fp<typename dragonbox::float_info< using info = dragonbox::float_info<decltype(converted_value)>;
decltype(converted_value)>::carrier_uint>(converted_value) const auto f = basic_fp<typename info::carrier_uint>(converted_value);
.e;
// Compute exp, an approximate power of 10, such that // Compute exp, an approximate power of 10, such that
// 10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1). // 10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1).
// This is based on log10(value) == log2(value) / log2(10) and approximation // This is based on log10(value) == log2(value) / log2(10) and approximation
// of log2(value) by e + num_fraction_bits idea from double-conversion. // of log2(value) by e + num_fraction_bits idea from double-conversion.
auto num_fraction_bits =
num_significand_bits<Float>() - (has_implicit_bit<Float>() ? 0 : 1);
exp = static_cast<int>( exp = static_cast<int>(
std::ceil((e + num_fraction_bits) * inv_log2_10 - 1e-10)); std::ceil((f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10));
dragon_flags = dragon::fixup; dragon_flags = dragon::fixup;
} else if (!is_constant_evaluated() && precision < 0) { } else if (!is_constant_evaluated() && precision < 0) {
// Use Dragonbox for the shortest format. // Use Dragonbox for the shortest format.

View File

@ -1009,6 +1009,8 @@ TEST(format_test, precision) {
if (std::numeric_limits<long double>::digits == 64) { if (std::numeric_limits<long double>::digits == 64) {
auto ld = (std::numeric_limits<long double>::min)(); auto ld = (std::numeric_limits<long double>::min)();
EXPECT_EQ(fmt::format("{:.0}", ld), "3e-4932"); EXPECT_EQ(fmt::format("{:.0}", ld), "3e-4932");
EXPECT_EQ(fmt::format("{:0g}", 5.02957084971264961283E-4940L),
"5.02957e-4940");
} }
EXPECT_EQ("123.", fmt::format("{:#.0f}", 123.0)); EXPECT_EQ("123.", fmt::format("{:#.0f}", 123.0));