diff --git a/include/fast_float/decimal_to_binary.h b/include/fast_float/decimal_to_binary.h index a16d2c5..273f0b7 100644 --- a/include/fast_float/decimal_to_binary.h +++ b/include/fast_float/decimal_to_binary.h @@ -163,7 +163,8 @@ compute_float(int64_t q, uint64_t w) noexcept { // std::float16_t (e.g., 2^-25 = 298023223876953125e-25), so we apply the // same round-to-even test as in the normal case below. // See script/format_parameters.py. - if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) && + if (binary::subnormal_ties_possible() && (product.low <= 1) && + (q >= binary::min_exponent_round_to_even()) && (q <= binary::max_exponent_round_to_even()) && ((answer.mantissa & 3) == 1)) { if (((answer.mantissa << subnormal_shift) << shift) == product.high) { diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 77858da..76d94b3 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -225,12 +225,16 @@ using parse_options = parse_options_t; #ifndef FASTFLOAT_ASSERT #define FASTFLOAT_ASSERT(x) \ - { static_cast(x); } + { \ + static_cast(x); \ + } #endif #ifndef FASTFLOAT_DEBUG_ASSERT #define FASTFLOAT_DEBUG_ASSERT(x) \ - { static_cast(x); } + { \ + static_cast(x); \ + } #endif // rust style `try!()` macro, or `?` operator @@ -662,6 +666,7 @@ template struct binary_format : binary_format_lookup_tables { static constexpr uint64_t max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST static constexpr bool fast_path_can_overflow(); + static constexpr bool subnormal_ties_possible(); static constexpr int largest_power_of_ten(); static constexpr int smallest_power_of_ten(); static constexpr T exact_power_of_ten(int64_t power); @@ -1110,6 +1115,15 @@ inline constexpr bool binary_format::fast_path_can_overflow() { double((std::numeric_limits::max)()); } +// A subnormal needs w * 10^q < 2^(minimum_exponent() + 1), so q is at most +// (minimum_exponent() + 1) * log10(2), with 1233/4096 < log10(2). Only +// std::float16_t has such q in its round-to-even range. +template +inline constexpr bool binary_format::subnormal_ties_possible() { + return min_exponent_round_to_even() <= + (((minimum_exponent() + 1) * 1233) >> 12); +} + template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path(int64_t power) { diff --git a/script/format_parameters.py b/script/format_parameters.py index 1fb5cda..e981b8f 100644 --- a/script/format_parameters.py +++ b/script/format_parameters.py @@ -310,6 +310,11 @@ def check_ties(fmt): check(lo <= min(sub), "and the subnormal ties at q in [%d, %d]" % (min(sub), max(sub))) else: note("no subnormal tie has fewer than 20 digits") + # subnormal_ties_possible() in float_common.h: an upper bound on the + # largest q that can give a subnormal, compared with the tie range + q_sub = ((fmt.minimum_exponent + 1) * 1233) >> 12 + check(10**q_sub < 2**fmt.emin < 10 ** (q_sub + 2), "q <= %d bounds the subnormal exponents" % q_sub) + check((lo <= q_sub) == bool(sub), "subnormal_ties_possible() == %s" % bool(sub)) longest = len(str(((1 << (p + 2)) - 1) * 5**fmt.bias)) check(fmt.max_digits >= longest, "max_digits=%d >= %d, the longest tie" % (fmt.max_digits, longest))