Adopting proposal.

This commit is contained in:
Daniel Lemire 2022-11-18 11:28:34 -05:00
parent bfc0478feb
commit 39ea41b84a

View File

@ -76,8 +76,10 @@ fastfloat_really_inline bool rounds_to_nearest() noexcept {
// However, it is expected to be much faster than the fegetround() // However, it is expected to be much faster than the fegetround()
// function call. // function call.
// //
// volatile prevents the compiler from computing the function at compile-time // The volatile keywoard prevents the compiler from computing the function
// It does not need to be std::numeric_limits<float>::min(), any small // at compile-time.
// There might be other ways to prevent compile-time optimizations (e.g., asm).
// The value does not need to be std::numeric_limits<float>::min(), any small
// value so that 1 + x should round to 1 would do. // value so that 1 + x should round to 1 would do.
static volatile float fmin = std::numeric_limits<float>::min(); static volatile float fmin = std::numeric_limits<float>::min();
// //
@ -100,6 +102,8 @@ fastfloat_really_inline bool rounds_to_nearest() noexcept {
// fmin + 1.0f = 0x1 (1) // fmin + 1.0f = 0x1 (1)
// 1.0f - fmin = 0x1 (1) // 1.0f - fmin = 0x1 (1)
// //
// Note: This may fail to be accurate if fast-math has been
// enabled, as rounding conventions may not apply.
return (fmin + 1.0f == 1.0f - fmin); return (fmin + 1.0f == 1.0f - fmin);
} }
@ -130,12 +134,23 @@ from_chars_result from_chars_advanced(const char *first, const char *last,
} }
answer.ec = std::errc(); // be optimistic answer.ec = std::errc(); // be optimistic
answer.ptr = pns.lastmatch; answer.ptr = pns.lastmatch;
// The implementation of the Clinger's fast path is convoluted because
// we want round-to-nearest in all cases, irrespective of the rounding mode
// selected on the thread.
// We proceed optimistically, assuming that detail::rounds_to_nearest() returns
// true.
if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && !pns.too_many_digits) {
// Unfortunately, the conventional Clinger's fast path is only possible // Unfortunately, the conventional Clinger's fast path is only possible
// when the system rounds to the nearest float. // when the system rounds to the nearest float.
//
// We expect the next branch to almost always be selected.
// We could check it first (before the previous branch), but
// there might be performance advantages at having the check
// be last.
if(detail::rounds_to_nearest()) { if(detail::rounds_to_nearest()) {
// We have that fegetround() == FE_TONEAREST. // We have that fegetround() == FE_TONEAREST.
// Next is Clinger's fast path. // Next is Clinger's fast path.
if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && pns.mantissa <=binary_format<T>::max_mantissa_fast_path() && !pns.too_many_digits) { if (pns.mantissa <=binary_format<T>::max_mantissa_fast_path()) {
value = T(pns.mantissa); value = T(pns.mantissa);
if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); } if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); }
else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); } else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); }
@ -145,7 +160,7 @@ from_chars_result from_chars_advanced(const char *first, const char *last,
} else { } else {
// We do not have that fegetround() == FE_TONEAREST. // We do not have that fegetround() == FE_TONEAREST.
// Next is a modified Clinger's fast path, inspired by Jakub Jelínek's proposal // Next is a modified Clinger's fast path, inspired by Jakub Jelínek's proposal
if (pns.exponent >= 0 && pns.exponent <= binary_format<T>::max_exponent_fast_path() && pns.mantissa <=binary_format<T>::max_mantissa_fast_path(pns.exponent) && !pns.too_many_digits) { if (pns.exponent >= 0 && pns.mantissa <=binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
#if (defined(_WIN32) && defined(__clang__)) #if (defined(_WIN32) && defined(__clang__))
// ClangCL may map 0 to -0.0 when fegetround() == FE_DOWNWARD // ClangCL may map 0 to -0.0 when fegetround() == FE_DOWNWARD
if(pns.mantissa == 0) { if(pns.mantissa == 0) {
@ -158,6 +173,7 @@ from_chars_result from_chars_advanced(const char *first, const char *last,
return answer; return answer;
} }
} }
}
adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa); adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
if(pns.too_many_digits && am.power2 >= 0) { if(pns.too_many_digits && am.power2 >= 0) {
if(am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) { if(am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {