diff --git a/include/fast_float/fast_float.h b/include/fast_float/fast_float.h index af65c96..453a0be 100644 --- a/include/fast_float/fast_float.h +++ b/include/fast_float/fast_float.h @@ -45,6 +45,28 @@ FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars_advanced(UC const *first, UC const *last, T &value, parse_options_t 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::value, double>::type +multiply_integer_and_power_of_10(uint64_t mantissa, + int decimal_exponent) noexcept; +FASTFLOAT_CONSTEXPR20 +typename std::enable_if::value, double>::type +multiply_integer_and_power_of_10(int64_t mantissa, + int decimal_exponent) noexcept; + /** * from_chars for integer types. */ diff --git a/include/fast_float/parse_number.h b/include/fast_float/parse_number.h index e74c478..8a04a35 100644 --- a/include/fast_float/parse_number.h +++ b/include/fast_float/parse_number.h @@ -188,32 +188,17 @@ from_chars(UC const *first, UC const *last, T &value, parse_options_t(fmt)); } -/** - * 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 -FASTFLOAT_CONSTEXPR20 from_chars_result_t -from_chars_advanced(parsed_number_string_t &pns, T &value) noexcept { - - static_assert(is_supported_float_type::value, - "only some floating-point types are supported"); - static_assert(is_supported_char_type::value, - "only char, wchar_t, char16_t and char32_t are supported"); - - from_chars_result_t answer; - - answer.ec = std::errc(); // be optimistic - answer.ptr = pns.lastmatch; +template +FASTFLOAT_CONSTEXPR20 bool +clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative, + T &value) noexcept { // 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::min_exponent_fast_path() <= pns.exponent && - pns.exponent <= binary_format::max_exponent_fast_path() && - !pns.too_many_digits) { + if (binary_format::min_exponent_fast_path() <= exponent && + exponent <= binary_format::max_exponent_fast_path()) { // Unfortunately, the conventional Clinger's fast path is only possible // when the system rounds to the nearest float. // @@ -224,41 +209,64 @@ from_chars_advanced(parsed_number_string_t &pns, T &value) noexcept { if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) { // We have that fegetround() == FE_TONEAREST. // Next is Clinger's fast path. - if (pns.mantissa <= binary_format::max_mantissa_fast_path()) { - value = T(pns.mantissa); - if (pns.exponent < 0) { - value = value / binary_format::exact_power_of_ten(-pns.exponent); + if (mantissa <= binary_format::max_mantissa_fast_path()) { + value = T(mantissa); + if (exponent < 0) { + value = value / binary_format::exact_power_of_ten(-exponent); } else { - value = value * binary_format::exact_power_of_ten(pns.exponent); + value = value * binary_format::exact_power_of_ten(exponent); } - if (pns.negative) { + if (is_negative) { value = -value; } - return answer; + return true; } } else { // We do not have that fegetround() == FE_TONEAREST. // Next is a modified Clinger's fast path, inspired by Jakub JelĂ­nek's // proposal - if (pns.exponent >= 0 && - pns.mantissa <= - binary_format::max_mantissa_fast_path(pns.exponent)) { + if (exponent >= 0 && + mantissa <= binary_format::max_mantissa_fast_path(exponent)) { #if defined(__clang__) || defined(FASTFLOAT_32BIT) // Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD - if (pns.mantissa == 0) { - value = pns.negative ? T(-0.) : T(0.); - return answer; + if (mantissa == 0) { + value = is_negative ? T(-0.) : T(0.); + return true; } #endif - value = T(pns.mantissa) * - binary_format::exact_power_of_ten(pns.exponent); - if (pns.negative) { + value = T(mantissa) * binary_format::exact_power_of_ten(exponent); + if (is_negative) { 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 +FASTFLOAT_CONSTEXPR20 from_chars_result_t +from_chars_advanced(parsed_number_string_t &pns, T &value) noexcept { + static_assert(is_supported_float_type::value, + "only some floating-point types are supported"); + static_assert(is_supported_char_type::value, + "only char, wchar_t, char16_t and char32_t are supported"); + + from_chars_result_t 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 = compute_float>(pns.exponent, pns.mantissa); 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); } +FASTFLOAT_CONSTEXPR20 +typename std::enable_if::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>(decimal_exponent, mantissa); + to_float(false, am, value); + return value; +} + +FASTFLOAT_CONSTEXPR20 +typename std::enable_if::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(is_negative ? -mantissa : mantissa); + + double value; + if (clinger_fast_path_impl(m, decimal_exponent, is_negative, value)) + return value; + + adjusted_mantissa am = + compute_float>(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::value, double>::type +multiply_integer_and_power_of_10(unsigned mantissa, + int decimal_exponent) noexcept { + return multiply_integer_and_power_of_10(static_cast(mantissa), + decimal_exponent); +} + +FASTFLOAT_CONSTEXPR20 +typename std::enable_if::value, double>::type +multiply_integer_and_power_of_10(int mantissa, int decimal_exponent) noexcept { + return multiply_integer_and_power_of_10(static_cast(mantissa), + decimal_exponent); +} + template FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars_int_advanced(UC const *first, UC const *last, T &value,