From 13345cab65952a182d0d6cdb90f90d120ed59d7b Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Thu, 18 Sep 2025 21:28:20 +0300 Subject: [PATCH 1/6] added template overload for `integer_times_pow10()` --- include/fast_float/fast_float.h | 14 ++++++++ include/fast_float/parse_number.h | 57 +++++++++++++++++++++++++------ 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/include/fast_float/fast_float.h b/include/fast_float/fast_float.h index a190d7c8..eb822f58 100644 --- a/include/fast_float/fast_float.h +++ b/include/fast_float/fast_float.h @@ -63,6 +63,20 @@ integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept; FASTFLOAT_CONSTEXPR20 inline double integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept; +/** + * This function is a template overload of `integer_times_pow10()` + * that returns a floating-point value of type `T` that is one of + * supported floating-point types (e.g. `double`, `float`). + */ +template +FASTFLOAT_CONSTEXPR20 + typename std::enable_if::value, T>::type + integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept; +template +FASTFLOAT_CONSTEXPR20 + typename std::enable_if::value, T>::type + integer_times_pow10(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 a44fef0b..d453c145 100644 --- a/include/fast_float/parse_number.h +++ b/include/fast_float/parse_number.h @@ -344,44 +344,79 @@ from_chars(UC const *first, UC const *last, T &value, int base) noexcept { return from_chars_advanced(first, last, value, options); } -FASTFLOAT_CONSTEXPR20 inline double -integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept { - double value; +template +FASTFLOAT_CONSTEXPR20 + typename std::enable_if::value, T>::type + integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept { + T value; if (clinger_fast_path_impl(mantissa, decimal_exponent, false, value)) return value; adjusted_mantissa am = - compute_float>(decimal_exponent, mantissa); + compute_float>(decimal_exponent, mantissa); to_float(false, am, value); return value; } -FASTFLOAT_CONSTEXPR20 inline double -integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept { +template +FASTFLOAT_CONSTEXPR20 + typename std::enable_if::value, T>::type + integer_times_pow10(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; + T value; if (clinger_fast_path_impl(m, decimal_exponent, is_negative, value)) return value; - adjusted_mantissa am = - compute_float>(decimal_exponent, m); + adjusted_mantissa am = compute_float>(decimal_exponent, m); to_float(is_negative, am, value); return value; } +FASTFLOAT_CONSTEXPR20 inline double +integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept { + return integer_times_pow10(mantissa, decimal_exponent); +} + +FASTFLOAT_CONSTEXPR20 inline double +integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept { + return integer_times_pow10(mantissa, decimal_exponent); +} + // the following overloads are here to avoid surprising ambiguity for int, // unsigned, etc. +template +FASTFLOAT_CONSTEXPR20 + typename std::enable_if::value && + std::is_integral::value && + !std::is_signed::value, + T>::type + integer_times_pow10(Int mantissa, int decimal_exponent) noexcept { + return integer_times_pow10(static_cast(mantissa), + decimal_exponent); +} + +template +FASTFLOAT_CONSTEXPR20 + typename std::enable_if::value && + std::is_integral::value && + std::is_signed::value, + T>::type + integer_times_pow10(Int mantissa, int decimal_exponent) noexcept { + return integer_times_pow10(static_cast(mantissa), + decimal_exponent); +} + template -FASTFLOAT_CONSTEXPR20 inline typename std::enable_if< +FASTFLOAT_CONSTEXPR20 typename std::enable_if< std::is_integral::value && !std::is_signed::value, double>::type integer_times_pow10(Int mantissa, int decimal_exponent) noexcept { return integer_times_pow10(static_cast(mantissa), decimal_exponent); } template -FASTFLOAT_CONSTEXPR20 inline typename std::enable_if< +FASTFLOAT_CONSTEXPR20 typename std::enable_if< std::is_integral::value && std::is_signed::value, double>::type integer_times_pow10(Int mantissa, int decimal_exponent) noexcept { return integer_times_pow10(static_cast(mantissa), decimal_exponent); From 01e505797b8639a817e5f4d7e6b593b631f07e93 Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Thu, 18 Sep 2025 21:28:50 +0300 Subject: [PATCH 2/6] added tests + some refactoring --- tests/basictest.cpp | 393 +++++++++++++++++++++++++++++++------------- 1 file changed, 283 insertions(+), 110 deletions(-) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index dc117526..15ab344d 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -1507,9 +1507,20 @@ TEST_CASE("float.inf") { std::errc::result_out_of_range); verify("3.5028234666e38", std::numeric_limits::infinity(), std::errc::result_out_of_range); + // FLT_MAX + 0.00000007e38 + verify("3.40282357e38", std::numeric_limits::infinity(), + std::errc::result_out_of_range); + // FLT_MAX + 0.0000001e38 + verify("3.4028236e38", std::numeric_limits::infinity(), + std::errc::result_out_of_range); } TEST_CASE("float.general") { + // FLT_TRUE_MIN / 2 + verify("0.7006492e-45", 0.f, std::errc::result_out_of_range); + // FLT_TRUE_MIN / 2 + 0.0000001e-45 + verify("0.7006493e-45", 0x1p-149f); + // max verify("340282346638528859811704183484516925440", 0x1.fffffep+127f); // -max @@ -2086,12 +2097,11 @@ TEST_CASE("bfloat16.general") { } #endif -template -void verify_integer_multiplication_by_power_of_10(Int mantissa, - int decimal_exponent, - double expected) { - const double actual = - fast_float::integer_times_pow10(mantissa, decimal_exponent); +template +void verify_integer_times_pow10_result(Int mantissa, int decimal_exponent, + T actual, U expected) { + static_assert(std::is_same::value, + "expected and actual types must match"); INFO("m * 10^e=" << mantissa << " * 10^" << decimal_exponent << "\n" @@ -2105,45 +2115,173 @@ void verify_integer_multiplication_by_power_of_10(Int mantissa, CHECK_EQ(actual, expected); } -template -void verify_integer_multiplication_by_power_of_10(Int mantissa, - int decimal_exponent) { +template +T calculate_integer_times_pow10_expected_result(Int mantissa, + int decimal_exponent) { std::string constructed_string = std::to_string(mantissa) + "e" + std::to_string(decimal_exponent); - double expected_result; + T expected_result; const auto result = fast_float::from_chars( constructed_string.data(), constructed_string.data() + constructed_string.size(), expected_result); if (result.ec != std::errc()) INFO("Failed to parse: " << constructed_string); - verify_integer_multiplication_by_power_of_10(mantissa, decimal_exponent, - expected_result); + return expected_result; } +template +void verify_integer_times_pow10_dflt(Int mantissa, int decimal_exponent, + double expected) { + static_assert(std::is_integral::value); + + // the "default" overload + const double actual = + fast_float::integer_times_pow10(mantissa, decimal_exponent); + + verify_integer_times_pow10_result(mantissa, decimal_exponent, actual, + expected); +} + +template +void verify_integer_times_pow10_dflt(Int mantissa, int decimal_exponent) { + static_assert(std::is_integral::value); + + const auto expected_result = + calculate_integer_times_pow10_expected_result(mantissa, + decimal_exponent); + + verify_integer_times_pow10_dflt(mantissa, decimal_exponent, expected_result); +} + +template +void verify_integer_times_pow10(Int mantissa, int decimal_exponent, + T expected) { + static_assert(std::is_floating_point::value); + static_assert(std::is_integral::value); + + // explicit specialization + const auto actual = + fast_float::integer_times_pow10(mantissa, decimal_exponent); + + verify_integer_times_pow10_result(mantissa, decimal_exponent, actual, + expected); +} + +template +void verify_integer_times_pow10(Int mantissa, int decimal_exponent) { + static_assert(std::is_floating_point::value); + static_assert(std::is_integral::value); + + const auto expected_result = calculate_integer_times_pow10_expected_result( + mantissa, decimal_exponent); + + verify_integer_times_pow10(mantissa, decimal_exponent, expected_result); +} + +namespace all_supported_types { +template +void verify_integer_times_pow10(Int mantissa, int decimal_exponent) { + static_assert(std::is_integral::value); + + // verify the "default" overload + verify_integer_times_pow10_dflt(mantissa, decimal_exponent); + + // verify explicit specializations + ::verify_integer_times_pow10(mantissa, decimal_exponent); + ::verify_integer_times_pow10(mantissa, decimal_exponent); +#if defined(__STDCPP_FLOAT64_T__) + ::verify_integer_times_pow10(mantissa, decimal_exponent); +#endif +#if defined(__STDCPP_FLOAT32_T__) + ::verify_integer_times_pow10(mantissa, decimal_exponent); +#endif +#if defined(__STDCPP_FLOAT16_T__) + ::verify_integer_times_pow10(mantissa, decimal_exponent); +#endif +#if defined(__STDCPP_BFLOAT16_T__) + ::verify_integer_times_pow10(mantissa, decimal_exponent); +#endif +} +} // namespace all_supported_types + TEST_CASE("integer_times_pow10") { - // explicitly verifying API with different types of integers - verify_integer_multiplication_by_power_of_10(31, -1, 3.1); - verify_integer_multiplication_by_power_of_10(-31, -1, -3.1); - verify_integer_multiplication_by_power_of_10(31, -1, 3.1); - verify_integer_multiplication_by_power_of_10(31415, -4, 3.1415); - verify_integer_multiplication_by_power_of_10(-31415, -4, -3.1415); - verify_integer_multiplication_by_power_of_10(31415, -4, 3.1415); - verify_integer_multiplication_by_power_of_10(314159265, -8, - 3.14159265); - verify_integer_multiplication_by_power_of_10(-314159265, -8, - -3.14159265); - verify_integer_multiplication_by_power_of_10(3141592653, -9, - 3.141592653); - verify_integer_multiplication_by_power_of_10( - 3141592653589793238, -18, 3.141592653589793238); - verify_integer_multiplication_by_power_of_10( - -3141592653589793238, -18, -3.141592653589793238); - verify_integer_multiplication_by_power_of_10( - 3141592653589793238, -18, 3.141592653589793238); - verify_integer_multiplication_by_power_of_10( - -3141592653589793238, -18, -3.141592653589793238); - verify_integer_multiplication_by_power_of_10( + /* explicitly verifying API with different types of integers */ + // double (the "default" overload) + verify_integer_times_pow10_dflt(31, -1, 3.1); + verify_integer_times_pow10_dflt(-31, -1, -3.1); + verify_integer_times_pow10_dflt(31, -1, 3.1); + verify_integer_times_pow10_dflt(31415, -4, 3.1415); + verify_integer_times_pow10_dflt(-31415, -4, -3.1415); + verify_integer_times_pow10_dflt(31415, -4, 3.1415); + verify_integer_times_pow10_dflt(314159265, -8, 3.14159265); + verify_integer_times_pow10_dflt(-314159265, -8, -3.14159265); + verify_integer_times_pow10_dflt(3141592653, -9, 3.141592653); + verify_integer_times_pow10_dflt(314159265, -8, 3.14159265); + verify_integer_times_pow10_dflt(-314159265, -8, -3.14159265); + verify_integer_times_pow10_dflt(3141592653, -9, 3.141592653); + verify_integer_times_pow10_dflt(3141592653589793238, -18, + 3.141592653589793238); + verify_integer_times_pow10_dflt(-3141592653589793238, -18, + -3.141592653589793238); + verify_integer_times_pow10_dflt(3141592653589793238, -18, + 3.141592653589793238); + verify_integer_times_pow10_dflt(3141592653589793238, -18, + 3.141592653589793238); + verify_integer_times_pow10_dflt(-3141592653589793238, -18, + -3.141592653589793238); + verify_integer_times_pow10_dflt(3141592653589793238, -18, + 3.141592653589793238); + // double (explicit specialization) + verify_integer_times_pow10(31, -1, 3.1); + verify_integer_times_pow10(-31, -1, -3.1); + verify_integer_times_pow10(31, -1, 3.1); + verify_integer_times_pow10(31415, -4, 3.1415); + verify_integer_times_pow10(-31415, -4, -3.1415); + verify_integer_times_pow10(31415, -4, 3.1415); + verify_integer_times_pow10(314159265, -8, 3.14159265); + verify_integer_times_pow10(-314159265, -8, -3.14159265); + verify_integer_times_pow10(3141592653, -9, 3.141592653); + verify_integer_times_pow10(314159265, -8, 3.14159265); + verify_integer_times_pow10(-314159265, -8, -3.14159265); + verify_integer_times_pow10(3141592653, -9, + 3.141592653); + verify_integer_times_pow10(3141592653589793238, -18, + 3.141592653589793238); + verify_integer_times_pow10(-3141592653589793238, -18, + -3.141592653589793238); + verify_integer_times_pow10(3141592653589793238, -18, + 3.141592653589793238); + verify_integer_times_pow10(3141592653589793238, -18, + 3.141592653589793238); + verify_integer_times_pow10(-3141592653589793238, -18, + -3.141592653589793238); + verify_integer_times_pow10( 3141592653589793238, -18, 3.141592653589793238); + // float (explicit specialization) + verify_integer_times_pow10(31, -1, 3.1f); + verify_integer_times_pow10(-31, -1, -3.1f); + verify_integer_times_pow10(31, -1, 3.1f); + verify_integer_times_pow10(31415, -4, 3.1415f); + verify_integer_times_pow10(-31415, -4, -3.1415f); + verify_integer_times_pow10(31415, -4, 3.1415f); + verify_integer_times_pow10(314159265, -8, 3.14159265f); + verify_integer_times_pow10(-314159265, -8, -3.14159265f); + verify_integer_times_pow10(3141592653, -9, 3.14159265f); + verify_integer_times_pow10(314159265, -8, 3.14159265f); + verify_integer_times_pow10(-314159265, -8, -3.14159265f); + verify_integer_times_pow10(3141592653, -9, 3.14159265f); + verify_integer_times_pow10(3141592653589793238, -18, + 3.141592653589793238f); + verify_integer_times_pow10(-3141592653589793238, -18, + -3.141592653589793238f); + verify_integer_times_pow10(3141592653589793238, -18, + 3.141592653589793238f); + verify_integer_times_pow10(3141592653589793238, -18, + 3.141592653589793238f); + verify_integer_times_pow10(-3141592653589793238, -18, + -3.141592653589793238f); + verify_integer_times_pow10( + 3141592653589793238, -18, 3.141592653589793238f); for (int mode : {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, FE_TONEAREST}) { fesetround(mode); @@ -2153,87 +2291,122 @@ TEST_CASE("integer_times_pow10") { ~Guard() { fesetround(FE_TONEAREST); } } guard; - verify_integer_multiplication_by_power_of_10(0, 0); - verify_integer_multiplication_by_power_of_10(1, 0); - verify_integer_multiplication_by_power_of_10(0, 1); - verify_integer_multiplication_by_power_of_10(1, 1); - verify_integer_multiplication_by_power_of_10(-1, 0); - verify_integer_multiplication_by_power_of_10(0, -1); - verify_integer_multiplication_by_power_of_10(-1, -1); - verify_integer_multiplication_by_power_of_10(-1, 1); - verify_integer_multiplication_by_power_of_10(1, -1); - - verify_integer_multiplication_by_power_of_10( + namespace all = all_supported_types; + + all::verify_integer_times_pow10(0, 0); + all::verify_integer_times_pow10(1, 0); + all::verify_integer_times_pow10(0, 1); + all::verify_integer_times_pow10(1, 1); + all::verify_integer_times_pow10(-1, 0); + all::verify_integer_times_pow10(0, -1); + all::verify_integer_times_pow10(-1, -1); + all::verify_integer_times_pow10(-1, 1); + all::verify_integer_times_pow10(1, -1); + + /* denormal min */ + verify_integer_times_pow10_dflt(49406564584124654, -340, + std::numeric_limits::denorm_min()); + verify_integer_times_pow10( 49406564584124654, -340, std::numeric_limits::denorm_min()); - verify_integer_multiplication_by_power_of_10( - 22250738585072014, -324, std::numeric_limits::min()); - verify_integer_multiplication_by_power_of_10( - 17976931348623158, 292, std::numeric_limits::max()); - - // DBL_TRUE_MIN / 2 underflows to 0 - verify_integer_multiplication_by_power_of_10(49406564584124654 / 2, -340, - 0.); - - // DBL_TRUE_MIN / 2 + 0.0000000000000001e-324 rounds to DBL_TRUE_MIN - verify_integer_multiplication_by_power_of_10( + verify_integer_times_pow10(14012984, -52, + std::numeric_limits::denorm_min()); + + /* normal min */ + verify_integer_times_pow10_dflt(22250738585072014, -324, + std::numeric_limits::min()); + verify_integer_times_pow10(22250738585072014, -324, + std::numeric_limits::min()); + verify_integer_times_pow10(11754944, -45, + std::numeric_limits::min()); + + /* max */ + verify_integer_times_pow10_dflt(17976931348623158, 292, + std::numeric_limits::max()); + verify_integer_times_pow10(17976931348623158, 292, + std::numeric_limits::max()); + verify_integer_times_pow10(34028235, 31, + std::numeric_limits::max()); + + /* underflow */ + // (DBL_TRUE_MIN / 2) underflows to 0 + verify_integer_times_pow10_dflt(49406564584124654 / 2, -340, 0.); + verify_integer_times_pow10(49406564584124654 / 2, -340, 0.); + // (FLT_TRUE_MIN / 2) underflows to 0 + verify_integer_times_pow10(14012984 / 2, -52, 0.f); + + /* rounding to denormal min */ + // (DBL_TRUE_MIN / 2 + 0.0000000000000001e-324) rounds to DBL_TRUE_MIN + verify_integer_times_pow10_dflt(49406564584124654 / 2 + 1, -340, + std::numeric_limits::denorm_min()); + verify_integer_times_pow10( 49406564584124654 / 2 + 1, -340, std::numeric_limits::denorm_min()); - - // DBL_MAX + 0.0000000000000001e308 overflows to infinity - verify_integer_multiplication_by_power_of_10( - 17976931348623158 + 1, 292, std::numeric_limits::infinity()); - // DBL_MAX + 0.00000000000000001e308 overflows to infinity - verify_integer_multiplication_by_power_of_10( - 179769313486231580 + 1, 291, std::numeric_limits::infinity()); + // (FLT_TRUE_MIN / 2 + 0.0000001e-45) rounds to FLT_TRUE_MIN + verify_integer_times_pow10(14012984 / 2 + 1, -52, + std::numeric_limits::denorm_min()); + + /* overflow */ + // (DBL_MAX + 0.0000000000000001e308) overflows to infinity + verify_integer_times_pow10_dflt(17976931348623158 + 1, 292, + std::numeric_limits::infinity()); + verify_integer_times_pow10(17976931348623158 + 1, 292, + std::numeric_limits::infinity()); + // (DBL_MAX + 0.00000000000000001e308) overflows to infinity + verify_integer_times_pow10_dflt(179769313486231580 + 1, 291, + std::numeric_limits::infinity()); + verify_integer_times_pow10(179769313486231580 + 1, 291, + std::numeric_limits::infinity()); + // (FLT_MAX + 0.0000001e38) overflows to infinity + verify_integer_times_pow10(34028235 + 1, 31, + std::numeric_limits::infinity()); + // (FLT_MAX + 0.00000007e38) overflows to infinity + verify_integer_times_pow10(340282350 + 7, 30, + std::numeric_limits::infinity()); // loosely verifying correct rounding of 1 to 64 bits // worth of significant digits - verify_integer_multiplication_by_power_of_10(1, 42); - verify_integer_multiplication_by_power_of_10(1, -42); - verify_integer_multiplication_by_power_of_10(12, 42); - verify_integer_multiplication_by_power_of_10(12, -42); - verify_integer_multiplication_by_power_of_10(123, 42); - verify_integer_multiplication_by_power_of_10(123, -42); - verify_integer_multiplication_by_power_of_10(1234, 42); - verify_integer_multiplication_by_power_of_10(1234, -42); - verify_integer_multiplication_by_power_of_10(12345, 42); - verify_integer_multiplication_by_power_of_10(12345, -42); - verify_integer_multiplication_by_power_of_10(123456, 42); - verify_integer_multiplication_by_power_of_10(123456, -42); - verify_integer_multiplication_by_power_of_10(1234567, 42); - verify_integer_multiplication_by_power_of_10(1234567, -42); - verify_integer_multiplication_by_power_of_10(12345678, 42); - verify_integer_multiplication_by_power_of_10(12345678, -42); - verify_integer_multiplication_by_power_of_10(123456789, 42); - verify_integer_multiplication_by_power_of_10(1234567890, 42); - verify_integer_multiplication_by_power_of_10(1234567890, -42); - verify_integer_multiplication_by_power_of_10(12345678901, 42); - verify_integer_multiplication_by_power_of_10(12345678901, -42); - verify_integer_multiplication_by_power_of_10(123456789012, 42); - verify_integer_multiplication_by_power_of_10(123456789012, -42); - verify_integer_multiplication_by_power_of_10(1234567890123, 42); - verify_integer_multiplication_by_power_of_10(1234567890123, -42); - verify_integer_multiplication_by_power_of_10(12345678901234, 42); - verify_integer_multiplication_by_power_of_10(12345678901234, -42); - verify_integer_multiplication_by_power_of_10(123456789012345, 42); - verify_integer_multiplication_by_power_of_10(123456789012345, -42); - verify_integer_multiplication_by_power_of_10(1234567890123456, 42); - verify_integer_multiplication_by_power_of_10(1234567890123456, -42); - verify_integer_multiplication_by_power_of_10(12345678901234567, 42); - verify_integer_multiplication_by_power_of_10(12345678901234567, -42); - verify_integer_multiplication_by_power_of_10(123456789012345678, 42); - verify_integer_multiplication_by_power_of_10(123456789012345678, -42); - verify_integer_multiplication_by_power_of_10(1234567890123456789, 42); - verify_integer_multiplication_by_power_of_10(1234567890123456789, -42); - verify_integer_multiplication_by_power_of_10(12345678901234567890ull, 42); - verify_integer_multiplication_by_power_of_10(12345678901234567890ull, -42); - verify_integer_multiplication_by_power_of_10( - std::numeric_limits::max(), 42); - verify_integer_multiplication_by_power_of_10( - std::numeric_limits::max(), -42); - verify_integer_multiplication_by_power_of_10( - std::numeric_limits::max(), 42); - verify_integer_multiplication_by_power_of_10( - std::numeric_limits::max(), -42); + all::verify_integer_times_pow10(1, 42); + all::verify_integer_times_pow10(1, -42); + all::verify_integer_times_pow10(12, 42); + all::verify_integer_times_pow10(12, -42); + all::verify_integer_times_pow10(123, 42); + all::verify_integer_times_pow10(123, -42); + all::verify_integer_times_pow10(1234, 42); + all::verify_integer_times_pow10(1234, -42); + all::verify_integer_times_pow10(12345, 42); + all::verify_integer_times_pow10(12345, -42); + all::verify_integer_times_pow10(123456, 42); + all::verify_integer_times_pow10(123456, -42); + all::verify_integer_times_pow10(1234567, 42); + all::verify_integer_times_pow10(1234567, -42); + all::verify_integer_times_pow10(12345678, 42); + all::verify_integer_times_pow10(12345678, -42); + all::verify_integer_times_pow10(123456789, 42); + all::verify_integer_times_pow10(1234567890, 42); + all::verify_integer_times_pow10(1234567890, -42); + all::verify_integer_times_pow10(12345678901, 42); + all::verify_integer_times_pow10(12345678901, -42); + all::verify_integer_times_pow10(123456789012, 42); + all::verify_integer_times_pow10(123456789012, -42); + all::verify_integer_times_pow10(1234567890123, 42); + all::verify_integer_times_pow10(1234567890123, -42); + all::verify_integer_times_pow10(12345678901234, 42); + all::verify_integer_times_pow10(12345678901234, -42); + all::verify_integer_times_pow10(123456789012345, 42); + all::verify_integer_times_pow10(123456789012345, -42); + all::verify_integer_times_pow10(1234567890123456, 42); + all::verify_integer_times_pow10(1234567890123456, -42); + all::verify_integer_times_pow10(12345678901234567, 42); + all::verify_integer_times_pow10(12345678901234567, -42); + all::verify_integer_times_pow10(123456789012345678, 42); + all::verify_integer_times_pow10(123456789012345678, -42); + all::verify_integer_times_pow10(1234567890123456789, 42); + all::verify_integer_times_pow10(1234567890123456789, -42); + all::verify_integer_times_pow10(12345678901234567890ull, 42); + all::verify_integer_times_pow10(12345678901234567890ull, -42); + all::verify_integer_times_pow10(std::numeric_limits::max(), 42); + all::verify_integer_times_pow10(std::numeric_limits::max(), -42); + all::verify_integer_times_pow10(std::numeric_limits::max(), 42); + all::verify_integer_times_pow10(std::numeric_limits::max(), -42); } } \ No newline at end of file From 7abb574ffc55080713b528b6b0e271c541f07a53 Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Mon, 29 Sep 2025 13:00:28 +0300 Subject: [PATCH 3/6] added doc to README and examples --- README.md | 17 +++++++++++++++++ tests/example_integer_times_pow10.cpp | 26 +++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8fdddbc5..1c7e7964 100644 --- a/README.md +++ b/README.md @@ -401,6 +401,23 @@ except `fast_float::integer_times_pow10()` does not report out-of-range errors, underflows to zero or overflows to infinity when the resulting value is out of range. +You can use template overloads to get the result converted to different +supported floating-point types: `float`, `double`, etc. +For example, to get result as `float` use +`fast_float::integer_times_pow10()` specialization: +```C++ +const uint64_t W = 1234567; +const int Q = 23; +const double result = fast_float::integer_times_pow10(W, Q); +std::cout.precision(7); +std::cout << "float: " << W << " * 10^" << Q << " = " << result << " (" + << (result == 1234567e23f ? "==" : "!=") << "expected)\n"; +``` +outputs +``` +float: 1234567 * 10^23 = 1.234567e+29 (==expected) +``` + Overloads of `fast_float::integer_times_pow10()` are provided for signed and unsigned integer types: `int64_t`, `uint64_t`, etc. diff --git a/tests/example_integer_times_pow10.cpp b/tests/example_integer_times_pow10.cpp index 3e86826c..785daeca 100644 --- a/tests/example_integer_times_pow10.cpp +++ b/tests/example_integer_times_pow10.cpp @@ -2,7 +2,7 @@ #include -int main() { +void default_overload() { const uint64_t W = 12345678901234567; const int Q = 23; const double result = fast_float::integer_times_pow10(W, Q); @@ -10,3 +10,27 @@ int main() { std::cout << W << " * 10^" << Q << " = " << result << " (" << (result == 12345678901234567e23 ? "==" : "!=") << "expected)\n"; } + +void double_specialization() { + const uint64_t W = 12345678901234567; + const int Q = 23; + const double result = fast_float::integer_times_pow10(W, Q); + std::cout.precision(17); + std::cout << "double: " << W << " * 10^" << Q << " = " << result << " (" + << (result == 12345678901234567e23 ? "==" : "!=") << "expected)\n"; +} + +void float_specialization() { + const uint64_t W = 1234567; + const int Q = 23; + const double result = fast_float::integer_times_pow10(W, Q); + std::cout.precision(7); + std::cout << "float: " << W << " * 10^" << Q << " = " << result << " (" + << (result == 1234567e23f ? "==" : "!=") << "expected)\n"; +} + +int main() { + default_overload(); + double_specialization(); + float_specialization(); +} From e9438e64ba6e537c9e0051b210183231aa27a6e3 Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Mon, 29 Sep 2025 19:54:22 +0300 Subject: [PATCH 4/6] fixed copy&paste error and minor mess --- README.md | 10 +++++----- tests/example_integer_times_pow10.cpp | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 1c7e7964..7a06fb06 100644 --- a/README.md +++ b/README.md @@ -406,16 +406,16 @@ supported floating-point types: `float`, `double`, etc. For example, to get result as `float` use `fast_float::integer_times_pow10()` specialization: ```C++ -const uint64_t W = 1234567; +const uint64_t W = 12345678; const int Q = 23; -const double result = fast_float::integer_times_pow10(W, Q); -std::cout.precision(7); +const float result = fast_float::integer_times_pow10(W, Q); +std::cout.precision(9); std::cout << "float: " << W << " * 10^" << Q << " = " << result << " (" - << (result == 1234567e23f ? "==" : "!=") << "expected)\n"; + << (result == 12345678e23f ? "==" : "!=") << "expected)\n"; ``` outputs ``` -float: 1234567 * 10^23 = 1.234567e+29 (==expected) +float: 12345678 * 10^23 = 1.23456782e+30 (==expected) ``` Overloads of `fast_float::integer_times_pow10()` are provided for diff --git a/tests/example_integer_times_pow10.cpp b/tests/example_integer_times_pow10.cpp index 785daeca..0205c275 100644 --- a/tests/example_integer_times_pow10.cpp +++ b/tests/example_integer_times_pow10.cpp @@ -21,12 +21,12 @@ void double_specialization() { } void float_specialization() { - const uint64_t W = 1234567; + const uint64_t W = 12345678; const int Q = 23; - const double result = fast_float::integer_times_pow10(W, Q); - std::cout.precision(7); + const float result = fast_float::integer_times_pow10(W, Q); + std::cout.precision(9); std::cout << "float: " << W << " * 10^" << Q << " = " << result << " (" - << (result == 1234567e23f ? "==" : "!=") << "expected)\n"; + << (result == 12345678e23f ? "==" : "!=") << "expected)\n"; } int main() { From 197c0ffca7c0252581cd73549102f00b834aa341 Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Mon, 29 Sep 2025 21:41:56 +0300 Subject: [PATCH 5/6] clang format --- include/fast_float/float_common.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 62d199ca..46d2f1eb 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -198,12 +198,16 @@ using parse_options = parse_options_t; #ifndef FASTFLOAT_ASSERT #define FASTFLOAT_ASSERT(x) \ - { ((void)(x)); } + { \ + ((void)(x)); \ + } #endif #ifndef FASTFLOAT_DEBUG_ASSERT #define FASTFLOAT_DEBUG_ASSERT(x) \ - { ((void)(x)); } + { \ + ((void)(x)); \ + } #endif // rust style `try!()` macro, or `?` operator From fd98fd668931ba1583e3fd851997d6614e7f21a5 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Mon, 29 Sep 2025 14:14:01 -0400 Subject: [PATCH 6/6] specialize for std::float32_t and std::float64_t explicitly credit: @lemire --- include/fast_float/float_common.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 46d2f1eb..34eaa017 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -1170,6 +1170,9 @@ static_assert(std::is_same, uint64_t>::value, static_assert( std::numeric_limits::is_iec559, "std::float64_t must fulfill the requirements of IEC 559 (IEEE 754)"); + +template <> +struct binary_format : public binary_format {}; #endif // __STDCPP_FLOAT64_T__ #ifdef __STDCPP_FLOAT32_T__ @@ -1178,6 +1181,9 @@ static_assert(std::is_same, uint32_t>::value, static_assert( std::numeric_limits::is_iec559, "std::float32_t must fulfill the requirements of IEC 559 (IEEE 754)"); + +template <> +struct binary_format : public binary_format {}; #endif // __STDCPP_FLOAT32_T__ #ifdef __STDCPP_FLOAT16_T__ @@ -1249,7 +1255,6 @@ constexpr chars_format adjust_for_feature_macros(chars_format fmt) { ; } } // namespace detail - } // namespace fast_float #endif