Compare commits

..

No commits in common. "main" and "v8.1.0" have entirely different histories.
main ... v8.1.0

10 changed files with 133 additions and 432 deletions

View File

@ -20,14 +20,14 @@ jobs:
fuzz-seconds: 300 fuzz-seconds: 300
output-sarif: true output-sarif: true
- name: Upload Crash - name: Upload Crash
uses: actions/upload-artifact@v5 uses: actions/upload-artifact@v4
if: failure() && steps.build.outcome == 'success' if: failure() && steps.build.outcome == 'success'
with: with:
name: artifacts name: artifacts
path: ./out/artifacts path: ./out/artifacts
- name: Upload Sarif - name: Upload Sarif
if: always() && steps.build.outcome == 'success' if: always() && steps.build.outcome == 'success'
uses: github/codeql-action/upload-sarif@v4 uses: github/codeql-action/upload-sarif@v3
with: with:
# Path to SARIF file relative to the root of the repository # Path to SARIF file relative to the root of the repository
sarif_file: cifuzz-sarif/results.sarif sarif_file: cifuzz-sarif/results.sarif

View File

@ -5,7 +5,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.2.2 - uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.2.2
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
- uses: mymindstorm/setup-emsdk@6ab9eb1bda2574c4ddb79809fc9247783eaf9021 # v14 - uses: mymindstorm/setup-emsdk@6ab9eb1bda2574c4ddb79809fc9247783eaf9021 # v14
- name: Verify - name: Verify
run: emcc -v run: emcc -v

View File

@ -6,7 +6,7 @@ jobs:
build: build:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- name: Install packages - name: Install packages
run: | run: |
sudo apt-get update -q -y sudo apt-get update -q -y

View File

@ -401,23 +401,6 @@ 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 underflows to zero or overflows to infinity when the resulting value is
out of range. 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<float>()` specialization:
```C++
const uint64_t W = 12345678;
const int Q = 23;
const float result = fast_float::integer_times_pow10<float>(W, Q);
std::cout.precision(9);
std::cout << "float: " << W << " * 10^" << Q << " = " << result << " ("
<< (result == 12345678e23f ? "==" : "!=") << "expected)\n";
```
outputs
```
float: 12345678 * 10^23 = 1.23456782e+30 (==expected)
```
Overloads of `fast_float::integer_times_pow10()` are provided for Overloads of `fast_float::integer_times_pow10()` are provided for
signed and unsigned integer types: `int64_t`, `uint64_t`, etc. signed and unsigned integer types: `int64_t`, `uint64_t`, etc.
@ -460,7 +443,7 @@ framework](https://github.com/microsoft/LightGBM).
Packages Packages
------ ------
[![Packaging status](https://repology.org/badge/vertical-allrepos/fast-float.svg)](https://repology.org/project/fast-float/versions) [![Packaging status](https://repology.org/badge/vertical-allrepos/fastfloat.svg)](https://repology.org/project/fastfloat/versions)
## References ## References
@ -615,11 +598,6 @@ long digits.
The library includes code adapted from Google Wuffs (written by Nigel Tao) which The library includes code adapted from Google Wuffs (written by Nigel Tao) which
was originally published under the Apache 2.0 license. was originally published under the Apache 2.0 license.
## Stars
[![Star History Chart](https://api.star-history.com/svg?repos=fastfloat/fast_float&type=Date)](https://www.star-history.com/#fastfloat/fast_float&Date)
## License ## License
<sup> <sup>

View File

@ -38,8 +38,11 @@ constexpr static uint64_t powers_of_ten_uint64[] = {1UL,
// this algorithm is not even close to optimized, but it has no practical // this algorithm is not even close to optimized, but it has no practical
// effect on performance: in order to have a faster algorithm, we'd need // effect on performance: in order to have a faster algorithm, we'd need
// to slow down performance for faster algorithms, and this is still fast. // to slow down performance for faster algorithms, and this is still fast.
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int32_t fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int32_t
scientific_exponent(uint64_t mantissa, int32_t exponent) noexcept { scientific_exponent(parsed_number_string_t<UC> &num) noexcept {
uint64_t mantissa = num.mantissa;
int32_t exponent = int32_t(num.exponent);
while (mantissa >= 10000) { while (mantissa >= 10000) {
mantissa /= 10000; mantissa /= 10000;
exponent += 4; exponent += 4;
@ -395,7 +398,7 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp))); FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp)));
} }
// compare digits, and use it to direct rounding // compare digits, and use it to director rounding
int ord = real_digits.compare(theor_digits); int ord = real_digits.compare(theor_digits);
adjusted_mantissa answer = am; adjusted_mantissa answer = am;
round<T>(answer, [ord](adjusted_mantissa &a, int32_t shift) { round<T>(answer, [ord](adjusted_mantissa &a, int32_t shift) {
@ -416,7 +419,7 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
return answer; return answer;
} }
// parse the significant digits as a big integer to unambiguously round // parse the significant digits as a big integer to unambiguously round the
// the significant digits. here, we are trying to determine how to round // the significant digits. here, we are trying to determine how to round
// an extended float representation close to `b+h`, halfway between `b` // an extended float representation close to `b+h`, halfway between `b`
// (the float rounded-down) and `b+u`, the next positive float. this // (the float rounded-down) and `b+u`, the next positive float. this
@ -435,8 +438,7 @@ digit_comp(parsed_number_string_t<UC> &num, adjusted_mantissa am) noexcept {
// remove the invalid exponent bias // remove the invalid exponent bias
am.power2 -= invalid_am_bias; am.power2 -= invalid_am_bias;
int32_t sci_exp = int32_t sci_exp = scientific_exponent(num);
scientific_exponent(num.mantissa, static_cast<int32_t>(num.exponent));
size_t max_digits = binary_format<T>::max_digits(); size_t max_digits = binary_format<T>::max_digits();
size_t digits = 0; size_t digits = 0;
bigint bigmant; bigint bigmant;

View File

@ -63,20 +63,6 @@ integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept;
FASTFLOAT_CONSTEXPR20 inline double FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept; 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 <typename T>
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept;
template <typename T>
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept;
/** /**
* from_chars for integer types. * from_chars for integer types.
*/ */

View File

@ -406,8 +406,8 @@ full_multiplication(uint64_t a, uint64_t b) {
// But MinGW on ARM64 doesn't have native support for 64-bit multiplications // But MinGW on ARM64 doesn't have native support for 64-bit multiplications
answer.high = __umulh(a, b); answer.high = __umulh(a, b);
answer.low = a * b; answer.low = a * b;
#elif defined(FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__) && \ #elif defined(FASTFLOAT_32BIT) || \
!defined(_M_ARM64) && !defined(__GNUC__)) (defined(_WIN64) && !defined(__clang__) && !defined(_M_ARM64))
answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64 answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64
#elif defined(FASTFLOAT_64BIT) && defined(__SIZEOF_INT128__) #elif defined(FASTFLOAT_64BIT) && defined(__SIZEOF_INT128__)
__uint128_t r = ((__uint128_t)a) * b; __uint128_t r = ((__uint128_t)a) * b;
@ -1166,9 +1166,6 @@ static_assert(std::is_same<equiv_uint_t<std::float64_t>, uint64_t>::value,
static_assert( static_assert(
std::numeric_limits<std::float64_t>::is_iec559, std::numeric_limits<std::float64_t>::is_iec559,
"std::float64_t must fulfill the requirements of IEC 559 (IEEE 754)"); "std::float64_t must fulfill the requirements of IEC 559 (IEEE 754)");
template <>
struct binary_format<std::float64_t> : public binary_format<double> {};
#endif // __STDCPP_FLOAT64_T__ #endif // __STDCPP_FLOAT64_T__
#ifdef __STDCPP_FLOAT32_T__ #ifdef __STDCPP_FLOAT32_T__
@ -1177,9 +1174,6 @@ static_assert(std::is_same<equiv_uint_t<std::float32_t>, uint32_t>::value,
static_assert( static_assert(
std::numeric_limits<std::float32_t>::is_iec559, std::numeric_limits<std::float32_t>::is_iec559,
"std::float32_t must fulfill the requirements of IEC 559 (IEEE 754)"); "std::float32_t must fulfill the requirements of IEC 559 (IEEE 754)");
template <>
struct binary_format<std::float32_t> : public binary_format<float> {};
#endif // __STDCPP_FLOAT32_T__ #endif // __STDCPP_FLOAT32_T__
#ifdef __STDCPP_FLOAT16_T__ #ifdef __STDCPP_FLOAT16_T__
@ -1251,6 +1245,7 @@ constexpr chars_format adjust_for_feature_macros(chars_format fmt) {
; ;
} }
} // namespace detail } // namespace detail
} // namespace fast_float } // namespace fast_float
#endif #endif

View File

@ -344,79 +344,44 @@ 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);
} }
template <typename T> FASTFLOAT_CONSTEXPR20 inline double
FASTFLOAT_CONSTEXPR20 integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept {
typename std::enable_if<is_supported_float_type<T>::value, T>::type double value;
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept {
T value;
if (clinger_fast_path_impl(mantissa, decimal_exponent, false, value)) if (clinger_fast_path_impl(mantissa, decimal_exponent, false, value))
return value; return value;
adjusted_mantissa am = adjusted_mantissa am =
compute_float<binary_format<T>>(decimal_exponent, mantissa); compute_float<binary_format<double>>(decimal_exponent, mantissa);
to_float(false, am, value); to_float(false, am, value);
return value; return value;
} }
template <typename T> FASTFLOAT_CONSTEXPR20 inline double
FASTFLOAT_CONSTEXPR20 integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept {
typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept {
const bool is_negative = mantissa < 0; const bool is_negative = mantissa < 0;
const uint64_t m = static_cast<uint64_t>(is_negative ? -mantissa : mantissa); const uint64_t m = static_cast<uint64_t>(is_negative ? -mantissa : mantissa);
T value; double value;
if (clinger_fast_path_impl(m, decimal_exponent, is_negative, value)) if (clinger_fast_path_impl(m, decimal_exponent, is_negative, value))
return value; return value;
adjusted_mantissa am = compute_float<binary_format<T>>(decimal_exponent, m); adjusted_mantissa am =
compute_float<binary_format<double>>(decimal_exponent, m);
to_float(is_negative, am, value); to_float(is_negative, am, value);
return value; return value;
} }
FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept {
return integer_times_pow10<double>(mantissa, decimal_exponent);
}
FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept {
return integer_times_pow10<double>(mantissa, decimal_exponent);
}
// the following overloads are here to avoid surprising ambiguity for int, // the following overloads are here to avoid surprising ambiguity for int,
// unsigned, etc. // unsigned, etc.
template <typename T, typename Int>
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value &&
std::is_integral<Int>::value &&
!std::is_signed<Int>::value,
T>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
return integer_times_pow10<T>(static_cast<uint64_t>(mantissa),
decimal_exponent);
}
template <typename T, typename Int>
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value &&
std::is_integral<Int>::value &&
std::is_signed<Int>::value,
T>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
return integer_times_pow10<T>(static_cast<int64_t>(mantissa),
decimal_exponent);
}
template <typename Int> template <typename Int>
FASTFLOAT_CONSTEXPR20 typename std::enable_if< FASTFLOAT_CONSTEXPR20 inline typename std::enable_if<
std::is_integral<Int>::value && !std::is_signed<Int>::value, double>::type std::is_integral<Int>::value && !std::is_signed<Int>::value, double>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept { integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
return integer_times_pow10(static_cast<uint64_t>(mantissa), decimal_exponent); return integer_times_pow10(static_cast<uint64_t>(mantissa), decimal_exponent);
} }
template <typename Int> template <typename Int>
FASTFLOAT_CONSTEXPR20 typename std::enable_if< FASTFLOAT_CONSTEXPR20 inline typename std::enable_if<
std::is_integral<Int>::value && std::is_signed<Int>::value, double>::type std::is_integral<Int>::value && std::is_signed<Int>::value, double>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept { integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
return integer_times_pow10(static_cast<int64_t>(mantissa), decimal_exponent); return integer_times_pow10(static_cast<int64_t>(mantissa), decimal_exponent);

View File

@ -1142,15 +1142,6 @@ TEST_CASE("double.inf") {
// DBL_MAX + 0.0000000000000001e308 // DBL_MAX + 0.0000000000000001e308
verify("1.7976931348623159e308", std::numeric_limits<double>::infinity(), verify("1.7976931348623159e308", std::numeric_limits<double>::infinity(),
std::errc::result_out_of_range); std::errc::result_out_of_range);
// ( (2 - 0.5*2^(52)) * 2^1023 ) smallest number that overflows to infinity
verify("179769313486231580793728971405303415079934132710037826936173778980444"
"968292764750946649017977587207096330286416692887910946555547851940402"
"630657488671505820681908902000708383676273854845817711531764475730270"
"069855571366959622842914819860834936475292719074168444365510704342711"
"559699508093042880177904174497792",
std::numeric_limits<double>::infinity(),
std::errc::result_out_of_range);
} }
TEST_CASE("double.general") { TEST_CASE("double.general") {
@ -1342,15 +1333,6 @@ TEST_CASE("double.general") {
std::numeric_limits<double>::infinity(), std::errc::result_out_of_range); std::numeric_limits<double>::infinity(), std::errc::result_out_of_range);
verify("-2240084132271013504.131248280843119943687942846658579428", verify("-2240084132271013504.131248280843119943687942846658579428",
-0x1.f1660a65b00bfp+60); -0x1.f1660a65b00bfp+60);
// ( (2 - 0.5*2^(52)) * 2^1023 - 1 ) largest 309 decimal digit number
// that rounds to DBL_MAX
verify("179769313486231580793728971405303415079934132710037826936173778980444"
"968292764750946649017977587207096330286416692887910946555547851940402"
"630657488671505820681908902000708383676273854845817711531764475730270"
"069855571366959622842914819860834936475292719074168444365510704342711"
"559699508093042880177904174497791",
std::numeric_limits<double>::max());
} }
TEST_CASE("double.decimal_point") { TEST_CASE("double.decimal_point") {
@ -1525,35 +1507,14 @@ TEST_CASE("float.inf") {
std::errc::result_out_of_range); std::errc::result_out_of_range);
verify("3.5028234666e38", std::numeric_limits<float>::infinity(), verify("3.5028234666e38", std::numeric_limits<float>::infinity(),
std::errc::result_out_of_range); std::errc::result_out_of_range);
// FLT_MAX + 0.00000007e38
verify("3.40282357e38", std::numeric_limits<float>::infinity(),
std::errc::result_out_of_range);
// FLT_MAX + 0.0000001e38
verify("3.4028236e38", std::numeric_limits<float>::infinity(),
std::errc::result_out_of_range);
// ( (2 - 0.5*2^(-23)) * 2^127 ) smallest number that overflows to infinity
verify("340282356779733661637539395458142568448",
std::numeric_limits<float>::infinity(),
std::errc::result_out_of_range);
} }
TEST_CASE("float.general") { 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 // max
verify("340282346638528859811704183484516925440", 0x1.fffffep+127f); verify("340282346638528859811704183484516925440", 0x1.fffffep+127f);
// -max // -max
verify("-340282346638528859811704183484516925440", -0x1.fffffep+127f); verify("-340282346638528859811704183484516925440", -0x1.fffffep+127f);
// ( (2 - 0.5*2^(-23)) * 2^127 - 1 ) largest 39 decimal digits number
// that rounds to FLT_MAX
verify("340282356779733661637539395458142568447",
std::numeric_limits<float>::max());
verify("-1e-999", -0.0f, std::errc::result_out_of_range); verify("-1e-999", -0.0f, std::errc::result_out_of_range);
verify("1." verify("1."
"175494140627517859246175898662808184331245864732796240031385942718174" "175494140627517859246175898662808184331245864732796240031385942718174"
@ -2125,11 +2086,12 @@ TEST_CASE("bfloat16.general") {
} }
#endif #endif
template <typename Int, typename T, typename U> template <typename Int>
void verify_integer_times_pow10_result(Int mantissa, int decimal_exponent, void verify_integer_multiplication_by_power_of_10(Int mantissa,
T actual, U expected) { int decimal_exponent,
static_assert(std::is_same<T, U>::value, double expected) {
"expected and actual types must match"); const double actual =
fast_float::integer_times_pow10(mantissa, decimal_exponent);
INFO("m * 10^e=" << mantissa << " * 10^" << decimal_exponent INFO("m * 10^e=" << mantissa << " * 10^" << decimal_exponent
<< "\n" << "\n"
@ -2143,173 +2105,45 @@ void verify_integer_times_pow10_result(Int mantissa, int decimal_exponent,
CHECK_EQ(actual, expected); CHECK_EQ(actual, expected);
} }
template <typename T, typename Int> template <typename Int>
T calculate_integer_times_pow10_expected_result(Int mantissa, void verify_integer_multiplication_by_power_of_10(Int mantissa,
int decimal_exponent) { int decimal_exponent) {
std::string constructed_string = std::string constructed_string =
std::to_string(mantissa) + "e" + std::to_string(decimal_exponent); std::to_string(mantissa) + "e" + std::to_string(decimal_exponent);
T expected_result; double expected_result;
const auto result = fast_float::from_chars( const auto result = fast_float::from_chars(
constructed_string.data(), constructed_string.data(),
constructed_string.data() + constructed_string.size(), expected_result); constructed_string.data() + constructed_string.size(), expected_result);
if (result.ec != std::errc()) if (result.ec != std::errc())
INFO("Failed to parse: " << constructed_string); INFO("Failed to parse: " << constructed_string);
return expected_result; verify_integer_multiplication_by_power_of_10(mantissa, decimal_exponent,
expected_result);
} }
template <typename Int>
void verify_integer_times_pow10_dflt(Int mantissa, int decimal_exponent,
double expected) {
static_assert(std::is_integral<Int>::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 <typename Int>
void verify_integer_times_pow10_dflt(Int mantissa, int decimal_exponent) {
static_assert(std::is_integral<Int>::value);
const auto expected_result =
calculate_integer_times_pow10_expected_result<double>(mantissa,
decimal_exponent);
verify_integer_times_pow10_dflt(mantissa, decimal_exponent, expected_result);
}
template <typename T, typename Int>
void verify_integer_times_pow10(Int mantissa, int decimal_exponent,
T expected) {
static_assert(std::is_floating_point<T>::value);
static_assert(std::is_integral<Int>::value);
// explicit specialization
const auto actual =
fast_float::integer_times_pow10<T>(mantissa, decimal_exponent);
verify_integer_times_pow10_result(mantissa, decimal_exponent, actual,
expected);
}
template <typename T, typename Int>
void verify_integer_times_pow10(Int mantissa, int decimal_exponent) {
static_assert(std::is_floating_point<T>::value);
static_assert(std::is_integral<Int>::value);
const auto expected_result = calculate_integer_times_pow10_expected_result<T>(
mantissa, decimal_exponent);
verify_integer_times_pow10(mantissa, decimal_exponent, expected_result);
}
namespace all_supported_types {
template <typename Int>
void verify_integer_times_pow10(Int mantissa, int decimal_exponent) {
static_assert(std::is_integral<Int>::value);
// verify the "default" overload
verify_integer_times_pow10_dflt(mantissa, decimal_exponent);
// verify explicit specializations
::verify_integer_times_pow10<double>(mantissa, decimal_exponent);
::verify_integer_times_pow10<float>(mantissa, decimal_exponent);
#if defined(__STDCPP_FLOAT64_T__)
::verify_integer_times_pow10<std::float64_t>(mantissa, decimal_exponent);
#endif
#if defined(__STDCPP_FLOAT32_T__)
::verify_integer_times_pow10<std::float32_t>(mantissa, decimal_exponent);
#endif
#if defined(__STDCPP_FLOAT16_T__)
::verify_integer_times_pow10<std::float16_t>(mantissa, decimal_exponent);
#endif
#if defined(__STDCPP_BFLOAT16_T__)
::verify_integer_times_pow10<std::bfloat16_t>(mantissa, decimal_exponent);
#endif
}
} // namespace all_supported_types
TEST_CASE("integer_times_pow10") { TEST_CASE("integer_times_pow10") {
/* explicitly verifying API with different types of integers */ // explicitly verifying API with different types of integers
// double (the "default" overload) verify_integer_multiplication_by_power_of_10<int8_t>(31, -1, 3.1);
verify_integer_times_pow10_dflt<int8_t>(31, -1, 3.1); verify_integer_multiplication_by_power_of_10<int8_t>(-31, -1, -3.1);
verify_integer_times_pow10_dflt<int8_t>(-31, -1, -3.1); verify_integer_multiplication_by_power_of_10<uint8_t>(31, -1, 3.1);
verify_integer_times_pow10_dflt<uint8_t>(31, -1, 3.1); verify_integer_multiplication_by_power_of_10<int16_t>(31415, -4, 3.1415);
verify_integer_times_pow10_dflt<int16_t>(31415, -4, 3.1415); verify_integer_multiplication_by_power_of_10<int16_t>(-31415, -4, -3.1415);
verify_integer_times_pow10_dflt<int16_t>(-31415, -4, -3.1415); verify_integer_multiplication_by_power_of_10<uint16_t>(31415, -4, 3.1415);
verify_integer_times_pow10_dflt<uint16_t>(31415, -4, 3.1415); verify_integer_multiplication_by_power_of_10<int32_t>(314159265, -8,
verify_integer_times_pow10_dflt<int32_t>(314159265, -8, 3.14159265); 3.14159265);
verify_integer_times_pow10_dflt<int32_t>(-314159265, -8, -3.14159265); verify_integer_multiplication_by_power_of_10<int32_t>(-314159265, -8,
verify_integer_times_pow10_dflt<uint32_t>(3141592653, -9, 3.141592653); -3.14159265);
verify_integer_times_pow10_dflt<long>(314159265, -8, 3.14159265); verify_integer_multiplication_by_power_of_10<uint32_t>(3141592653, -9,
verify_integer_times_pow10_dflt<long>(-314159265, -8, -3.14159265); 3.141592653);
verify_integer_times_pow10_dflt<unsigned long>(3141592653, -9, 3.141592653); verify_integer_multiplication_by_power_of_10<int64_t>(
verify_integer_times_pow10_dflt<int64_t>(3141592653589793238, -18, 3141592653589793238, -18, 3.141592653589793238);
3.141592653589793238); verify_integer_multiplication_by_power_of_10<int64_t>(
verify_integer_times_pow10_dflt<int64_t>(-3141592653589793238, -18, -3141592653589793238, -18, -3.141592653589793238);
-3.141592653589793238); verify_integer_multiplication_by_power_of_10<uint64_t>(
verify_integer_times_pow10_dflt<uint64_t>(3141592653589793238, -18, 3141592653589793238, -18, 3.141592653589793238);
3.141592653589793238); verify_integer_multiplication_by_power_of_10<long long>(
verify_integer_times_pow10_dflt<long long>(3141592653589793238, -18, -3141592653589793238, -18, -3.141592653589793238);
3.141592653589793238); verify_integer_multiplication_by_power_of_10<unsigned long long>(
verify_integer_times_pow10_dflt<long long>(-3141592653589793238, -18,
-3.141592653589793238);
verify_integer_times_pow10_dflt<unsigned long long>(3141592653589793238, -18,
3.141592653589793238);
// double (explicit specialization)
verify_integer_times_pow10<double, int8_t>(31, -1, 3.1);
verify_integer_times_pow10<double, int8_t>(-31, -1, -3.1);
verify_integer_times_pow10<double, uint8_t>(31, -1, 3.1);
verify_integer_times_pow10<double, int16_t>(31415, -4, 3.1415);
verify_integer_times_pow10<double, int16_t>(-31415, -4, -3.1415);
verify_integer_times_pow10<double, uint16_t>(31415, -4, 3.1415);
verify_integer_times_pow10<double, int32_t>(314159265, -8, 3.14159265);
verify_integer_times_pow10<double, int32_t>(-314159265, -8, -3.14159265);
verify_integer_times_pow10<double, uint32_t>(3141592653, -9, 3.141592653);
verify_integer_times_pow10<double, long>(314159265, -8, 3.14159265);
verify_integer_times_pow10<double, long>(-314159265, -8, -3.14159265);
verify_integer_times_pow10<double, unsigned long>(3141592653, -9,
3.141592653);
verify_integer_times_pow10<double, int64_t>(3141592653589793238, -18,
3.141592653589793238);
verify_integer_times_pow10<double, int64_t>(-3141592653589793238, -18,
-3.141592653589793238);
verify_integer_times_pow10<double, uint64_t>(3141592653589793238, -18,
3.141592653589793238);
verify_integer_times_pow10<double, long long>(3141592653589793238, -18,
3.141592653589793238);
verify_integer_times_pow10<double, long long>(-3141592653589793238, -18,
-3.141592653589793238);
verify_integer_times_pow10<double, unsigned long long>(
3141592653589793238, -18, 3.141592653589793238); 3141592653589793238, -18, 3.141592653589793238);
// float (explicit specialization)
verify_integer_times_pow10<float, int8_t>(31, -1, 3.1f);
verify_integer_times_pow10<float, int8_t>(-31, -1, -3.1f);
verify_integer_times_pow10<float, uint8_t>(31, -1, 3.1f);
verify_integer_times_pow10<float, int16_t>(31415, -4, 3.1415f);
verify_integer_times_pow10<float, int16_t>(-31415, -4, -3.1415f);
verify_integer_times_pow10<float, uint16_t>(31415, -4, 3.1415f);
verify_integer_times_pow10<float, int32_t>(314159265, -8, 3.14159265f);
verify_integer_times_pow10<float, int32_t>(-314159265, -8, -3.14159265f);
verify_integer_times_pow10<float, uint32_t>(3141592653, -9, 3.14159265f);
verify_integer_times_pow10<float, long>(314159265, -8, 3.14159265f);
verify_integer_times_pow10<float, long>(-314159265, -8, -3.14159265f);
verify_integer_times_pow10<float, unsigned long>(3141592653, -9, 3.14159265f);
verify_integer_times_pow10<float, int64_t>(3141592653589793238, -18,
3.141592653589793238f);
verify_integer_times_pow10<float, int64_t>(-3141592653589793238, -18,
-3.141592653589793238f);
verify_integer_times_pow10<float, uint64_t>(3141592653589793238, -18,
3.141592653589793238f);
verify_integer_times_pow10<float, long long>(3141592653589793238, -18,
3.141592653589793238f);
verify_integer_times_pow10<float, long long>(-3141592653589793238, -18,
-3.141592653589793238f);
verify_integer_times_pow10<float, unsigned long long>(
3141592653589793238, -18, 3.141592653589793238f);
for (int mode : {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, FE_TONEAREST}) { for (int mode : {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, FE_TONEAREST}) {
fesetround(mode); fesetround(mode);
@ -2319,122 +2153,87 @@ TEST_CASE("integer_times_pow10") {
~Guard() { fesetround(FE_TONEAREST); } ~Guard() { fesetround(FE_TONEAREST); }
} guard; } guard;
namespace all = all_supported_types; 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);
all::verify_integer_times_pow10(0, 0); verify_integer_multiplication_by_power_of_10(
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<double>::denorm_min());
verify_integer_times_pow10<double>(
49406564584124654, -340, std::numeric_limits<double>::denorm_min()); 49406564584124654, -340, std::numeric_limits<double>::denorm_min());
verify_integer_times_pow10<float>(14012984, -52, verify_integer_multiplication_by_power_of_10(
std::numeric_limits<float>::denorm_min()); 22250738585072014, -324, std::numeric_limits<double>::min());
verify_integer_multiplication_by_power_of_10(
17976931348623158, 292, std::numeric_limits<double>::max());
/* normal min */ // DBL_TRUE_MIN / 2 underflows to 0
verify_integer_times_pow10_dflt(22250738585072014, -324, verify_integer_multiplication_by_power_of_10(49406564584124654 / 2, -340,
std::numeric_limits<double>::min()); 0.);
verify_integer_times_pow10<double>(22250738585072014, -324,
std::numeric_limits<double>::min());
verify_integer_times_pow10<float>(11754944, -45,
std::numeric_limits<float>::min());
/* max */ // DBL_TRUE_MIN / 2 + 0.0000000000000001e-324 rounds to DBL_TRUE_MIN
verify_integer_times_pow10_dflt(17976931348623158, 292, verify_integer_multiplication_by_power_of_10(
std::numeric_limits<double>::max());
verify_integer_times_pow10<double>(17976931348623158, 292,
std::numeric_limits<double>::max());
verify_integer_times_pow10<float>(34028235, 31,
std::numeric_limits<float>::max());
/* underflow */
// (DBL_TRUE_MIN / 2) underflows to 0
verify_integer_times_pow10_dflt(49406564584124654 / 2, -340, 0.);
verify_integer_times_pow10<double>(49406564584124654 / 2, -340, 0.);
// (FLT_TRUE_MIN / 2) underflows to 0
verify_integer_times_pow10<float>(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<double>::denorm_min());
verify_integer_times_pow10<double>(
49406564584124654 / 2 + 1, -340, 49406564584124654 / 2 + 1, -340,
std::numeric_limits<double>::denorm_min()); std::numeric_limits<double>::denorm_min());
// (FLT_TRUE_MIN / 2 + 0.0000001e-45) rounds to FLT_TRUE_MIN
verify_integer_times_pow10<float>(14012984 / 2 + 1, -52,
std::numeric_limits<float>::denorm_min());
/* overflow */ // DBL_MAX + 0.0000000000000001e308 overflows to infinity
// (DBL_MAX + 0.0000000000000001e308) overflows to infinity verify_integer_multiplication_by_power_of_10(
verify_integer_times_pow10_dflt(17976931348623158 + 1, 292, 17976931348623158 + 1, 292, std::numeric_limits<double>::infinity());
std::numeric_limits<double>::infinity()); // DBL_MAX + 0.00000000000000001e308 overflows to infinity
verify_integer_times_pow10<double>(17976931348623158 + 1, 292, verify_integer_multiplication_by_power_of_10(
std::numeric_limits<double>::infinity()); 179769313486231580 + 1, 291, std::numeric_limits<double>::infinity());
// (DBL_MAX + 0.00000000000000001e308) overflows to infinity
verify_integer_times_pow10_dflt(179769313486231580 + 1, 291,
std::numeric_limits<double>::infinity());
verify_integer_times_pow10<double>(179769313486231580 + 1, 291,
std::numeric_limits<double>::infinity());
// (FLT_MAX + 0.0000001e38) overflows to infinity
verify_integer_times_pow10<float>(34028235 + 1, 31,
std::numeric_limits<float>::infinity());
// (FLT_MAX + 0.00000007e38) overflows to infinity
verify_integer_times_pow10<float>(340282350 + 7, 30,
std::numeric_limits<float>::infinity());
// loosely verifying correct rounding of 1 to 64 bits // loosely verifying correct rounding of 1 to 64 bits
// worth of significant digits // worth of significant digits
all::verify_integer_times_pow10(1, 42); verify_integer_multiplication_by_power_of_10(1, 42);
all::verify_integer_times_pow10(1, -42); verify_integer_multiplication_by_power_of_10(1, -42);
all::verify_integer_times_pow10(12, 42); verify_integer_multiplication_by_power_of_10(12, 42);
all::verify_integer_times_pow10(12, -42); verify_integer_multiplication_by_power_of_10(12, -42);
all::verify_integer_times_pow10(123, 42); verify_integer_multiplication_by_power_of_10(123, 42);
all::verify_integer_times_pow10(123, -42); verify_integer_multiplication_by_power_of_10(123, -42);
all::verify_integer_times_pow10(1234, 42); verify_integer_multiplication_by_power_of_10(1234, 42);
all::verify_integer_times_pow10(1234, -42); verify_integer_multiplication_by_power_of_10(1234, -42);
all::verify_integer_times_pow10(12345, 42); verify_integer_multiplication_by_power_of_10(12345, 42);
all::verify_integer_times_pow10(12345, -42); verify_integer_multiplication_by_power_of_10(12345, -42);
all::verify_integer_times_pow10(123456, 42); verify_integer_multiplication_by_power_of_10(123456, 42);
all::verify_integer_times_pow10(123456, -42); verify_integer_multiplication_by_power_of_10(123456, -42);
all::verify_integer_times_pow10(1234567, 42); verify_integer_multiplication_by_power_of_10(1234567, 42);
all::verify_integer_times_pow10(1234567, -42); verify_integer_multiplication_by_power_of_10(1234567, -42);
all::verify_integer_times_pow10(12345678, 42); verify_integer_multiplication_by_power_of_10(12345678, 42);
all::verify_integer_times_pow10(12345678, -42); verify_integer_multiplication_by_power_of_10(12345678, -42);
all::verify_integer_times_pow10(123456789, 42); verify_integer_multiplication_by_power_of_10(123456789, 42);
all::verify_integer_times_pow10(1234567890, 42); verify_integer_multiplication_by_power_of_10(1234567890, 42);
all::verify_integer_times_pow10(1234567890, -42); verify_integer_multiplication_by_power_of_10(1234567890, -42);
all::verify_integer_times_pow10(12345678901, 42); verify_integer_multiplication_by_power_of_10(12345678901, 42);
all::verify_integer_times_pow10(12345678901, -42); verify_integer_multiplication_by_power_of_10(12345678901, -42);
all::verify_integer_times_pow10(123456789012, 42); verify_integer_multiplication_by_power_of_10(123456789012, 42);
all::verify_integer_times_pow10(123456789012, -42); verify_integer_multiplication_by_power_of_10(123456789012, -42);
all::verify_integer_times_pow10(1234567890123, 42); verify_integer_multiplication_by_power_of_10(1234567890123, 42);
all::verify_integer_times_pow10(1234567890123, -42); verify_integer_multiplication_by_power_of_10(1234567890123, -42);
all::verify_integer_times_pow10(12345678901234, 42); verify_integer_multiplication_by_power_of_10(12345678901234, 42);
all::verify_integer_times_pow10(12345678901234, -42); verify_integer_multiplication_by_power_of_10(12345678901234, -42);
all::verify_integer_times_pow10(123456789012345, 42); verify_integer_multiplication_by_power_of_10(123456789012345, 42);
all::verify_integer_times_pow10(123456789012345, -42); verify_integer_multiplication_by_power_of_10(123456789012345, -42);
all::verify_integer_times_pow10(1234567890123456, 42); verify_integer_multiplication_by_power_of_10(1234567890123456, 42);
all::verify_integer_times_pow10(1234567890123456, -42); verify_integer_multiplication_by_power_of_10(1234567890123456, -42);
all::verify_integer_times_pow10(12345678901234567, 42); verify_integer_multiplication_by_power_of_10(12345678901234567, 42);
all::verify_integer_times_pow10(12345678901234567, -42); verify_integer_multiplication_by_power_of_10(12345678901234567, -42);
all::verify_integer_times_pow10(123456789012345678, 42); verify_integer_multiplication_by_power_of_10(123456789012345678, 42);
all::verify_integer_times_pow10(123456789012345678, -42); verify_integer_multiplication_by_power_of_10(123456789012345678, -42);
all::verify_integer_times_pow10(1234567890123456789, 42); verify_integer_multiplication_by_power_of_10(1234567890123456789, 42);
all::verify_integer_times_pow10(1234567890123456789, -42); verify_integer_multiplication_by_power_of_10(1234567890123456789, -42);
all::verify_integer_times_pow10(12345678901234567890ull, 42); verify_integer_multiplication_by_power_of_10(12345678901234567890ull, 42);
all::verify_integer_times_pow10(12345678901234567890ull, -42); verify_integer_multiplication_by_power_of_10(12345678901234567890ull, -42);
all::verify_integer_times_pow10(std::numeric_limits<int64_t>::max(), 42); verify_integer_multiplication_by_power_of_10(
all::verify_integer_times_pow10(std::numeric_limits<int64_t>::max(), -42); std::numeric_limits<int64_t>::max(), 42);
all::verify_integer_times_pow10(std::numeric_limits<uint64_t>::max(), 42); verify_integer_multiplication_by_power_of_10(
all::verify_integer_times_pow10(std::numeric_limits<uint64_t>::max(), -42); std::numeric_limits<int64_t>::max(), -42);
verify_integer_multiplication_by_power_of_10(
std::numeric_limits<uint64_t>::max(), 42);
verify_integer_multiplication_by_power_of_10(
std::numeric_limits<uint64_t>::max(), -42);
} }
} }

View File

@ -2,7 +2,7 @@
#include <iostream> #include <iostream>
void default_overload() { int main() {
const uint64_t W = 12345678901234567; const uint64_t W = 12345678901234567;
const int Q = 23; const int Q = 23;
const double result = fast_float::integer_times_pow10(W, Q); const double result = fast_float::integer_times_pow10(W, Q);
@ -10,27 +10,3 @@ void default_overload() {
std::cout << W << " * 10^" << Q << " = " << result << " (" std::cout << W << " * 10^" << Q << " = " << result << " ("
<< (result == 12345678901234567e23 ? "==" : "!=") << "expected)\n"; << (result == 12345678901234567e23 ? "==" : "!=") << "expected)\n";
} }
void double_specialization() {
const uint64_t W = 12345678901234567;
const int Q = 23;
const double result = fast_float::integer_times_pow10<double>(W, Q);
std::cout.precision(17);
std::cout << "double: " << W << " * 10^" << Q << " = " << result << " ("
<< (result == 12345678901234567e23 ? "==" : "!=") << "expected)\n";
}
void float_specialization() {
const uint64_t W = 12345678;
const int Q = 23;
const float result = fast_float::integer_times_pow10<float>(W, Q);
std::cout.precision(9);
std::cout << "float: " << W << " * 10^" << Q << " = " << result << " ("
<< (result == 12345678e23f ? "==" : "!=") << "expected)\n";
}
int main() {
default_overload();
double_specialization();
float_specialization();
}