Merge pull request #431 from aikhanjum/codex/fastfloat-correctness

Reject integer_times_pow10 arguments wider than 64 bits
This commit is contained in:
Daniel Lemire 2026-10-29 13:54:48 -04:00 committed by GitHub
commit 33785d5d61
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 55 additions and 19 deletions

View File

@ -482,7 +482,7 @@ float: 12345678 * 10^23 = 1.23456782e+30 (==expected)
```
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 up to 64 bits: `int64_t`, `uint64_t`, etc.
## Users and Related Work

View File

@ -535,38 +535,42 @@ integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept {
// the following overloads are here to avoid surprising ambiguity for int,
// 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 {
FASTFLOAT_CONSTEXPR20 typename std::enable_if<
is_supported_float_type<T>::value && std::is_integral<Int>::value &&
sizeof(Int) <= sizeof(uint64_t) && !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 {
FASTFLOAT_CONSTEXPR20 typename std::enable_if<
is_supported_float_type<T>::value && std::is_integral<Int>::value &&
sizeof(Int) <= sizeof(int64_t) && 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>
FASTFLOAT_CONSTEXPR20 typename std::enable_if<
std::is_integral<Int>::value && !std::is_signed<Int>::value, double>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
FASTFLOAT_CONSTEXPR20
typename std::enable_if<std::is_integral<Int>::value &&
sizeof(Int) <= sizeof(uint64_t) &&
!std::is_signed<Int>::value,
double>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
return integer_times_pow10(static_cast<uint64_t>(mantissa), decimal_exponent);
}
template <typename Int>
FASTFLOAT_CONSTEXPR20 typename std::enable_if<
std::is_integral<Int>::value && std::is_signed<Int>::value, double>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
FASTFLOAT_CONSTEXPR20
typename std::enable_if<std::is_integral<Int>::value &&
sizeof(Int) <= sizeof(int64_t) &&
std::is_signed<Int>::value,
double>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
return integer_times_pow10(static_cast<int64_t>(mantissa), decimal_exponent);
}

View File

@ -2283,6 +2283,38 @@ void verify_integer_times_pow10(Int mantissa, int decimal_exponent) {
}
} // namespace all_supported_types
#if defined(__SIZEOF_INT128__)
template <typename Int, typename = void>
struct has_integer_times_pow10 : std::false_type {};
template <typename Int>
struct has_integer_times_pow10<
Int, std::void_t<decltype(fast_float::integer_times_pow10(Int{}, 0))>>
: std::true_type {};
template <typename Float, typename Int, typename = void>
struct has_typed_integer_times_pow10 : std::false_type {};
template <typename Float, typename Int>
struct has_typed_integer_times_pow10<
Float, Int,
std::void_t<decltype(fast_float::integer_times_pow10<Float>(Int{}, 0))>>
: std::true_type {};
TEST_CASE("integer_times_pow10 rejects integers wider than 64 bits") {
CHECK(has_integer_times_pow10<int64_t>::value);
CHECK(has_integer_times_pow10<uint64_t>::value);
CHECK(has_typed_integer_times_pow10<float, int64_t>::value);
CHECK(has_typed_integer_times_pow10<double, uint64_t>::value);
CHECK_FALSE(has_integer_times_pow10<__int128_t>::value);
CHECK_FALSE(has_integer_times_pow10<__uint128_t>::value);
CHECK_FALSE(has_typed_integer_times_pow10<float, __int128_t>::value);
CHECK_FALSE(has_typed_integer_times_pow10<float, __uint128_t>::value);
CHECK_FALSE(has_typed_integer_times_pow10<double, __int128_t>::value);
CHECK_FALSE(has_typed_integer_times_pow10<double, __uint128_t>::value);
}
#endif
TEST_CASE("integer_times_pow10") {
/* explicitly verifying API with different types of integers */
// double (the "default" overload)