Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions include/fast_float/decimal_to_binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,21 @@ compute_float(int64_t q, uint64_t w) noexcept {
return answer;
}
// next line is safe because -answer.power2 + 1 < 64
answer.mantissa >>= -answer.power2 + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0 in the 32-bit and
// and 64-bit case (with no more than 19 digits).
int const subnormal_shift = -answer.power2 + 1;
answer.mantissa >>= subnormal_shift;
// A subnormal result can also fall exactly between two floats. With at
// most 19 digits this never happens for float and double, but it does for
// 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 (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) {
answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up
}
}
answer.mantissa += (answer.mantissa & 1); // round up
answer.mantissa >>= 1;
// There is a weird scenario where we don't have a subnormal but just.
Expand Down
42 changes: 34 additions & 8 deletions include/fast_float/float_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,8 @@ template <typename T> struct binary_format : binary_format_lookup_tables<T> {
static constexpr uint64_t max_mantissa_fast_path(int64_t power);
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);
Expand Down Expand Up @@ -696,6 +698,7 @@ template <typename U> struct binary_format_lookup_tables<double, U> {
0x20000000000000 / (constant_55555 * constant_55555 * 5),
0x20000000000000 / (constant_55555 * constant_55555 * 5 * 5),
0x20000000000000 / (constant_55555 * constant_55555 * 5 * 5 * 5),
0x20000000000000 / (constant_55555 * constant_55555 * 5 * 5 * 5 * 5),
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555),
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5),
0x20000000000000 /
Expand All @@ -711,9 +714,7 @@ template <typename U> struct binary_format_lookup_tables<double, U> {
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 *
constant_55555 * 5 * 5),
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 *
constant_55555 * 5 * 5 * 5),
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 *
constant_55555 * 5 * 5 * 5 * 5)};
constant_55555 * 5 * 5 * 5)};
};

#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
Expand Down Expand Up @@ -930,7 +931,9 @@ binary_format<std::float16_t>::max_mantissa_fast_path(int64_t power) {

template <>
inline constexpr int binary_format<std::float16_t>::min_exponent_fast_path() {
return 0;
// w / 10^k with w <= 2^11 and k <= 4 rounds correctly even when evaluated
// in float or double first (checked in script/format_parameters.py).
return -4;
}

template <>
Expand All @@ -942,7 +945,9 @@ binary_format<std::float16_t>::max_exponent_round_to_even() {
template <>
inline constexpr int
binary_format<std::float16_t>::min_exponent_round_to_even() {
return -22;
// -22 covers the normal ties; subnormal ties such as
// 2^-25 = 298023223876953125e-25 need q = -25 and q = -26.
return -26;
}

template <>
Expand All @@ -966,7 +971,8 @@ inline constexpr int binary_format<std::float16_t>::largest_power_of_ten() {

template <>
inline constexpr int binary_format<std::float16_t>::smallest_power_of_ten() {
return -27;
// (10^19 - 1) * 10^-27 < 2^-25, so any q < -26 rounds to zero.
return -26;
}

template <>
Expand Down Expand Up @@ -1053,7 +1059,8 @@ binary_format<std::bfloat16_t>::max_mantissa_fast_path(int64_t power) {

template <>
inline constexpr int binary_format<std::bfloat16_t>::min_exponent_fast_path() {
return 0;
// Same argument as for std::float16_t (w <= 2^8, k <= 3).
return -3;
}

template <>
Expand Down Expand Up @@ -1089,7 +1096,8 @@ inline constexpr int binary_format<std::bfloat16_t>::largest_power_of_ten() {

template <>
inline constexpr int binary_format<std::bfloat16_t>::smallest_power_of_ten() {
return -60;
// (10^19 - 1) * 10^-60 < 2^-134, so any q < -59 rounds to zero.
return -59;
}

template <>
Expand All @@ -1098,6 +1106,24 @@ inline constexpr size_t binary_format<std::bfloat16_t>::max_digits() {
}
#endif // __STDCPP_BFLOAT16_T__

// Whether Clinger's fast path can overflow: only for std::float16_t, where
// 2^11 * 10^4 > 65504.
template <typename T>
inline constexpr bool binary_format<T>::fast_path_can_overflow() {
return double(max_mantissa_fast_path()) *
double(exact_power_of_ten(max_exponent_fast_path())) >
double((std::numeric_limits<T>::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 <typename T>
inline constexpr bool binary_format<T>::subnormal_ties_possible() {
return min_exponent_round_to_even() <=
(((minimum_exponent() + 1) * 1233) >> 12);
}

template <>
inline constexpr uint64_t
binary_format<double>::max_mantissa_fast_path(int64_t power) {
Expand Down
6 changes: 6 additions & 0 deletions include/fast_float/parse_number.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,12 @@ clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
value = value / binary_format<T>::exact_power_of_ten(-exponent);
} else {
value = value * binary_format<T>::exact_power_of_ten(exponent);
// Only std::float16_t can overflow here (e.g., "656e2"); let the
// slow path report result_out_of_range.
if (binary_format<T>::fast_path_can_overflow() &&
value > (std::numeric_limits<T>::max)()) {
return false;
}
}
if (is_negative) {
value = -value;
Expand Down
Loading