implemented multiplication of integer by power of 10

This commit is contained in:
Pavel Novikov 2025-09-02 13:20:36 +03:00
parent 0a9257e825
commit 7b8f04500a
No known key found for this signature in database
GPG Key ID: F2C305CAE4167D14
2 changed files with 116 additions and 38 deletions

View File

@ -45,6 +45,28 @@ FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_advanced(UC const *first, UC const *last, T &value, from_chars_advanced(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept; parse_options_t<UC> options) noexcept;
/**
* This function multiplies an integer number by a power of 10 and returns
* the result as a double precision floating-point value that is correctly
* rounded. The resulting floating-point value is the closest floating-point
* value, using the "round to nearest, tie to even" convention for values that
* would otherwise fall right in-between two values. That is, we provide exact
* conversion according to the IEEE standard.
*
* On overflow infinity is returned, on underflow 0 is returned.
*
* The implementation does not throw and does not allocate memory (e.g., with
* `new` or `malloc`).
*/
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<double>::value, double>::type
multiply_integer_and_power_of_10(uint64_t mantissa,
int decimal_exponent) noexcept;
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<double>::value, double>::type
multiply_integer_and_power_of_10(int64_t mantissa,
int decimal_exponent) noexcept;
/** /**
* from_chars for integer types. * from_chars for integer types.
*/ */

View File

@ -188,32 +188,17 @@ from_chars(UC const *first, UC const *last, T &value,
parse_options_t<UC>(fmt)); parse_options_t<UC>(fmt));
} }
/** template <typename T>
* This function overload takes parsed_number_string_t structure that is created FASTFLOAT_CONSTEXPR20 bool
* and populated either by from_chars_advanced function taking chars range and clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
* parsing options or other parsing custom function implemented by user. T &value) noexcept {
*/
template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
static_assert(is_supported_float_type<T>::value,
"only some floating-point types are supported");
static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported");
from_chars_result_t<UC> answer;
answer.ec = std::errc(); // be optimistic
answer.ptr = pns.lastmatch;
// The implementation of the Clinger's fast path is convoluted because // The implementation of the Clinger's fast path is convoluted because
// we want round-to-nearest in all cases, irrespective of the rounding mode // we want round-to-nearest in all cases, irrespective of the rounding mode
// selected on the thread. // selected on the thread.
// We proceed optimistically, assuming that detail::rounds_to_nearest() // We proceed optimistically, assuming that detail::rounds_to_nearest()
// returns true. // returns true.
if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && if (binary_format<T>::min_exponent_fast_path() <= exponent &&
pns.exponent <= binary_format<T>::max_exponent_fast_path() && 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.
// //
@ -224,41 +209,64 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) { if (!cpp20_and_in_constexpr() && 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 (pns.mantissa <= binary_format<T>::max_mantissa_fast_path()) { if (mantissa <= binary_format<T>::max_mantissa_fast_path()) {
value = T(pns.mantissa); value = T(mantissa);
if (pns.exponent < 0) { if (exponent < 0) {
value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); value = value / binary_format<T>::exact_power_of_ten(-exponent);
} else { } else {
value = value * binary_format<T>::exact_power_of_ten(pns.exponent); value = value * binary_format<T>::exact_power_of_ten(exponent);
} }
if (pns.negative) { if (is_negative) {
value = -value; value = -value;
} }
return answer; return true;
} }
} 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 // Next is a modified Clinger's fast path, inspired by Jakub Jelínek's
// proposal // proposal
if (pns.exponent >= 0 && if (exponent >= 0 &&
pns.mantissa <= mantissa <= binary_format<T>::max_mantissa_fast_path(exponent)) {
binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
#if defined(__clang__) || defined(FASTFLOAT_32BIT) #if defined(__clang__) || defined(FASTFLOAT_32BIT)
// Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD // Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
if (pns.mantissa == 0) { if (mantissa == 0) {
value = pns.negative ? T(-0.) : T(0.); value = is_negative ? T(-0.) : T(0.);
return answer; return true;
} }
#endif #endif
value = T(pns.mantissa) * value = T(mantissa) * binary_format<T>::exact_power_of_ten(exponent);
binary_format<T>::exact_power_of_ten(pns.exponent); if (is_negative) {
if (pns.negative) {
value = -value; value = -value;
} }
return answer; return true;
} }
} }
} }
return false;
}
/**
* This function overload takes parsed_number_string_t structure that is created
* and populated either by from_chars_advanced function taking chars range and
* parsing options or other parsing custom function implemented by user.
*/
template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
static_assert(is_supported_float_type<T>::value,
"only some floating-point types are supported");
static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported");
from_chars_result_t<UC> answer;
answer.ec = std::errc(); // be optimistic
answer.ptr = pns.lastmatch;
if (!pns.too_many_digits &&
clinger_fast_path_impl(pns.mantissa, pns.exponent, pns.negative, value))
return answer;
adjusted_mantissa am = adjusted_mantissa am =
compute_float<binary_format<T>>(pns.exponent, pns.mantissa); 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) {
@ -336,6 +344,54 @@ from_chars(UC const *first, UC const *last, T &value, int base) noexcept {
return from_chars_advanced(first, last, value, options); return from_chars_advanced(first, last, value, options);
} }
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<double>::value, double>::type
multiply_integer_and_power_of_10(uint64_t mantissa,
int decimal_exponent) noexcept {
double value;
if (clinger_fast_path_impl(mantissa, decimal_exponent, false, value))
return value;
adjusted_mantissa am =
compute_float<binary_format<double>>(decimal_exponent, mantissa);
to_float(false, am, value);
return value;
}
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<double>::value, double>::type
multiply_integer_and_power_of_10(int64_t mantissa,
int decimal_exponent) noexcept {
const bool is_negative = mantissa < 0;
const uint64_t m = static_cast<uint64_t>(is_negative ? -mantissa : mantissa);
double value;
if (clinger_fast_path_impl(m, decimal_exponent, is_negative, value))
return value;
adjusted_mantissa am =
compute_float<binary_format<double>>(decimal_exponent, m);
to_float(is_negative, am, value);
return value;
}
// the following overloads are here to avoid surprising ambiguity for int,
// unsigned, etc.
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<double>::value, double>::type
multiply_integer_and_power_of_10(unsigned mantissa,
int decimal_exponent) noexcept {
return multiply_integer_and_power_of_10(static_cast<uint64_t>(mantissa),
decimal_exponent);
}
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<double>::value, double>::type
multiply_integer_and_power_of_10(int mantissa, int decimal_exponent) noexcept {
return multiply_integer_and_power_of_10(static_cast<int64_t>(mantissa),
decimal_exponent);
}
template <typename T, typename UC> template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_int_advanced(UC const *first, UC const *last, T &value, from_chars_int_advanced(UC const *first, UC const *last, T &value,