This commit is contained in:
IRainman 2025-09-18 22:10:37 +03:00
commit af8ece24d7
9 changed files with 623 additions and 42 deletions

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.14) cmake_minimum_required(VERSION 3.14)
project(fast_float VERSION 8.0.2 LANGUAGES CXX) project(fast_float VERSION 8.1.0 LANGUAGES CXX)
set(FASTFLOAT_CXX_STANDARD 11 CACHE STRING "the C++ standard to use for fastfloat") set(FASTFLOAT_CXX_STANDARD 11 CACHE STRING "the C++ standard to use for fastfloat")
set(CMAKE_CXX_STANDARD ${FASTFLOAT_CXX_STANDARD}) set(CMAKE_CXX_STANDARD ${FASTFLOAT_CXX_STANDARD})
option(FASTFLOAT_TEST "Enable tests" OFF) option(FASTFLOAT_TEST "Enable tests" OFF)

View File

@ -377,6 +377,33 @@ int main() {
} }
``` ```
## Multiplication of an integer by a power of 10
An integer `W` can be multiplied by a power of ten `10^Q` and
converted to `double` with correctly rounded value
(in "round to nearest, tie to even" fashion) using
`fast_float::integer_times_pow10()`, e.g.:
```C++
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 << W << " * 10^" << Q << " = " << result << " ("
<< (result == 12345678901234567e23 ? "==" : "!=") << "expected)\n";
```
outputs
```
12345678901234567 * 10^23 = 1.2345678901234567e+39 (==expected)
```
`fast_float::integer_times_pow10()` gives the same result as
using `fast_float::from_chars()` when parsing the string `"WeQ"`
(in this example `"12345678901234567e23"`),
except `fast_float::integer_times_pow10()` does not report out-of-range errors, and
underflows to zero or overflows to infinity when the resulting value is
out of range.
Overloads of `fast_float::integer_times_pow10()` are provided for
signed and unsigned integer types: `int64_t`, `uint64_t`, etc.
You also can use not standard options: You also can use not standard options:
```C++ ```C++
@ -426,6 +453,8 @@ The fast_float library is part of:
* GCC (as of version 12): the `from_chars` function in GCC relies on fast_float, * GCC (as of version 12): the `from_chars` function in GCC relies on fast_float,
* [Chromium](https://github.com/Chromium/Chromium), the engine behind Google * [Chromium](https://github.com/Chromium/Chromium), the engine behind Google
Chrome, Microsoft Edge, and Opera, Chrome, Microsoft Edge, and Opera,
* Boost JSON, MySQL, etc.
* Blender
* [WebKit](https://github.com/WebKit/WebKit), the engine behind Safari (Apple's * [WebKit](https://github.com/WebKit/WebKit), the engine behind Safari (Apple's
web browser), web browser),
* [DuckDB](https://duckdb.org), * [DuckDB](https://duckdb.org),
@ -528,7 +557,7 @@ sufficiently recent version of CMake (3.11 or better at least):
FetchContent_Declare( FetchContent_Declare(
fast_float fast_float
GIT_REPOSITORY https://github.com/fastfloat/fast_float.git GIT_REPOSITORY https://github.com/fastfloat/fast_float.git
GIT_TAG tags/v8.0.2 GIT_TAG tags/v8.1.0
GIT_SHALLOW TRUE) GIT_SHALLOW TRUE)
FetchContent_MakeAvailable(fast_float) FetchContent_MakeAvailable(fast_float)
@ -544,7 +573,7 @@ You may also use [CPM](https://github.com/cpm-cmake/CPM.cmake), like so:
CPMAddPackage( CPMAddPackage(
NAME fast_float NAME fast_float
GITHUB_REPOSITORY "fastfloat/fast_float" GITHUB_REPOSITORY "fastfloat/fast_float"
GIT_TAG v8.0.2) GIT_TAG v8.1.0)
``` ```
## Using as single header ## Using as single header
@ -556,7 +585,7 @@ if desired as described in the command line help.
You may directly download automatically generated single-header files: You may directly download automatically generated single-header files:
<https://github.com/fastfloat/fast_float/releases/download/v8.0.2/fast_float.h> <https://github.com/fastfloat/fast_float/releases/download/v8.1.0/fast_float.h>
## Benchmarking ## Benchmarking

View File

@ -45,6 +45,24 @@ 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> const &options) noexcept; parse_options_t<UC> const &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 inline double
integer_times_pow10(uint64_t const mantissa, int const decimal_exponent) noexcept;
FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(int64_t const mantissa, int const decimal_exponent) noexcept;
/** /**
* from_chars for integer types. * from_chars for integer types.
*/ */

View File

@ -16,8 +16,8 @@
#include "constexpr_feature_detect.h" #include "constexpr_feature_detect.h"
#define FASTFLOAT_VERSION_MAJOR 8 #define FASTFLOAT_VERSION_MAJOR 8
#define FASTFLOAT_VERSION_MINOR 0 #define FASTFLOAT_VERSION_MINOR 1
#define FASTFLOAT_VERSION_PATCH 2 #define FASTFLOAT_VERSION_PATCH 0
#define FASTFLOAT_STRINGIZE_IMPL(x) #x #define FASTFLOAT_STRINGIZE_IMPL(x) #x
#define FASTFLOAT_STRINGIZE(x) FASTFLOAT_STRINGIZE_IMPL(x) #define FASTFLOAT_STRINGIZE(x) FASTFLOAT_STRINGIZE_IMPL(x)
@ -1202,7 +1202,12 @@ template <typename T> constexpr uint64_t int_luts<T>::min_safe_u64[];
template <typename UC> template <typename UC>
fastfloat_really_inline constexpr uint_fast8_t ch_to_digit(UC c) noexcept { fastfloat_really_inline constexpr uint_fast8_t ch_to_digit(UC c) noexcept {
return int_luts<>::chdigit[static_cast<unsigned char>(c)]; // wchar_t and char can be signed, so we need to be careful.
using UnsignedUC = typename std::make_unsigned<UC>::type;
return int_luts<>::chdigit[static_cast<unsigned char>(
static_cast<UnsignedUC>(c) &
static_cast<UnsignedUC>(
-((static_cast<UnsignedUC>(c) & ~0xFFull) == 0)))];
} }
fastfloat_really_inline constexpr uint_fast8_t fastfloat_really_inline constexpr uint_fast8_t

View File

@ -195,32 +195,21 @@ 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_really_inline FASTFLOAT_CONSTEXPR20 bool
* and populated either by from_chars_advanced function taking chars range and clinger_fast_path_impl(uint64_t const mantissa, int64_t const exponent,
* parsing options or other parsing custom function implemented by user. #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
*/ bool const is_negative,
template <typename T, typename UC> #endif
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> T &value) noexcept {
from_chars_advanced(parsed_number_string_t<UC> const &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.
// //
@ -233,37 +222,36 @@ from_chars_advanced(parsed_number_string_t<UC> const &pns, T &value) noexcept {
#endif #endif
// 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);
} }
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (pns.negative) { if (is_negative) {
value = -value; value = -value;
} }
#endif #endif
return answer; return true;
} }
#ifndef FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED #ifndef FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
} 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 = value =
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
pns.negative ? T(-0.) : is_negative ? T(-0.) :
#endif #endif
T(0.); T(0.);
return answer; return true;
} }
#endif #endif
value = T(pns.mantissa) * value = T(pns.mantissa) *
@ -273,11 +261,40 @@ from_chars_advanced(parsed_number_string_t<UC> const &pns, T &value) noexcept {
value = -value; value = -value;
} }
#endif #endif
return answer; return true;
} }
} }
#endif #endif
} }
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,
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
pns.negative,
#endif
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) {
@ -369,6 +386,69 @@ from_chars(UC const *first, UC const *last, T &value, int const base) noexcept {
return from_chars_advanced(first, last, value, options); return from_chars_advanced(first, last, value, options);
} }
FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(uint64_t const mantissa, int const decimal_exponent) noexcept {
double value;
if (clinger_fast_path_impl(mantissa, decimal_exponent,
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
false,
#endif
value))
return value;
adjusted_mantissa am =
compute_float<binary_format<double>>(decimal_exponent, mantissa);
to_float(
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
false,
#endif
am, value);
return value;
}
FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(int64_t const mantissa, int const decimal_exponent) noexcept {
#ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
FASTFLOAT_ASSUME(mantissa > 0);
const uint64_t m = static_cast<uint64_t>(mantissa);
#else
const bool is_negative = mantissa < 0;
const uint64_t m = static_cast<uint64_t>(is_negative ? -mantissa : mantissa);
#endif
double value;
if (clinger_fast_path_impl(m, decimal_exponent,
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
is_negative,
#endif
value))
return value;
adjusted_mantissa am =
compute_float<binary_format<double>>(decimal_exponent, m);
to_float(
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
is_negative,
#endif
am, value);
return value;
}
// the following overloads are here to avoid surprising ambiguity for int,
// unsigned, etc.
template <typename Int>
FASTFLOAT_CONSTEXPR20 inline 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 {
return integer_times_pow10(static_cast<uint64_t>(mantissa), decimal_exponent);
}
template <typename Int>
FASTFLOAT_CONSTEXPR20 inline 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 {
return integer_times_pow10(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,

View File

@ -61,6 +61,7 @@ fast_float_add_cpp_test(wide_char_test)
fast_float_add_cpp_test(supported_chars_test) fast_float_add_cpp_test(supported_chars_test)
fast_float_add_cpp_test(example_test) fast_float_add_cpp_test(example_test)
fast_float_add_cpp_test(example_comma_test) fast_float_add_cpp_test(example_comma_test)
fast_float_add_cpp_test(example_integer_times_pow10)
fast_float_add_cpp_test(basictest) fast_float_add_cpp_test(basictest)
option(FASTFLOAT_CONSTEXPR_TESTS "Require constexpr tests (build will fail if the compiler won't support it)" OFF) option(FASTFLOAT_CONSTEXPR_TESTS "Require constexpr tests (build will fail if the compiler won't support it)" OFF)
if (FASTFLOAT_CONSTEXPR_TESTS) if (FASTFLOAT_CONSTEXPR_TESTS)

View File

@ -1134,6 +1134,14 @@ TEST_CASE("double.inf") {
std::errc::result_out_of_range); std::errc::result_out_of_range);
verify("1.9e308", std::numeric_limits<double>::infinity(), verify("1.9e308", std::numeric_limits<double>::infinity(),
std::errc::result_out_of_range); std::errc::result_out_of_range);
// DBL_MAX + 0.00000000000000001e308
verify("1.79769313486231581e308", std::numeric_limits<double>::infinity(),
std::errc::result_out_of_range);
// DBL_MAX + 0.0000000000000001e308
verify("1.7976931348623159e308", std::numeric_limits<double>::infinity(),
std::errc::result_out_of_range);
} }
TEST_CASE("double.general") { TEST_CASE("double.general") {
@ -1143,6 +1151,13 @@ TEST_CASE("double.general") {
verify("-22250738585072012e-324", verify("-22250738585072012e-324",
-0x1p-1022); /* limit between normal and subnormal*/ -0x1p-1022); /* limit between normal and subnormal*/
verify("-1e-999", -0.0, std::errc::result_out_of_range); verify("-1e-999", -0.0, std::errc::result_out_of_range);
// DBL_TRUE_MIN / 2
verify("2.4703282292062327e-324", 0.0, std::errc::result_out_of_range);
// DBL_TRUE_MIN / 2 + 0.0000000000000001e-324
verify("2.4703282292062328e-324", 0x0.0000000000001p-1022);
verify("-2.2222222222223e-322", -0x1.68p-1069); verify("-2.2222222222223e-322", -0x1.68p-1069);
verify("9007199254740993.0", 0x1p+53); verify("9007199254740993.0", 0x1p+53);
verify("860228122.6654514319E+90", 0x1.92bb20990715fp+328); verify("860228122.6654514319E+90", 0x1.92bb20990715fp+328);
@ -2070,3 +2085,155 @@ TEST_CASE("bfloat16.general") {
// 0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875bf16); // 0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875bf16);
} }
#endif #endif
template <typename Int>
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);
INFO("m * 10^e=" << mantissa << " * 10^" << decimal_exponent
<< "\n"
" expected="
<< fHexAndDec(expected) << "\n"
<< " ..actual=" << fHexAndDec(actual) << "\n"
<< " expected mantissa="
<< iHexAndDec(get_mantissa(expected)) << "\n"
<< " ..actual mantissa=" << iHexAndDec(get_mantissa(actual))
<< "\n");
CHECK_EQ(actual, expected);
}
template <typename Int>
void verify_integer_multiplication_by_power_of_10(Int mantissa,
int decimal_exponent) {
std::string constructed_string =
std::to_string(mantissa) + "e" + std::to_string(decimal_exponent);
double 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);
}
TEST_CASE("integer_times_pow10") {
// explicitly verifying API with different types of integers
verify_integer_multiplication_by_power_of_10<int8_t>(31, -1, 3.1);
verify_integer_multiplication_by_power_of_10<int8_t>(-31, -1, -3.1);
verify_integer_multiplication_by_power_of_10<uint8_t>(31, -1, 3.1);
verify_integer_multiplication_by_power_of_10<int16_t>(31415, -4, 3.1415);
verify_integer_multiplication_by_power_of_10<int16_t>(-31415, -4, -3.1415);
verify_integer_multiplication_by_power_of_10<uint16_t>(31415, -4, 3.1415);
verify_integer_multiplication_by_power_of_10<int32_t>(314159265, -8,
3.14159265);
verify_integer_multiplication_by_power_of_10<int32_t>(-314159265, -8,
-3.14159265);
verify_integer_multiplication_by_power_of_10<uint32_t>(3141592653, -9,
3.141592653);
verify_integer_multiplication_by_power_of_10<int64_t>(
3141592653589793238, -18, 3.141592653589793238);
verify_integer_multiplication_by_power_of_10<int64_t>(
-3141592653589793238, -18, -3.141592653589793238);
verify_integer_multiplication_by_power_of_10<uint64_t>(
3141592653589793238, -18, 3.141592653589793238);
verify_integer_multiplication_by_power_of_10<long long>(
-3141592653589793238, -18, -3.141592653589793238);
verify_integer_multiplication_by_power_of_10<unsigned long long>(
3141592653589793238, -18, 3.141592653589793238);
for (int mode : {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, FE_TONEAREST}) {
fesetround(mode);
INFO("fesetround(): " << std::string{round_name(mode)});
struct Guard {
~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(
49406564584124654, -340, std::numeric_limits<double>::denorm_min());
verify_integer_multiplication_by_power_of_10(
22250738585072014, -324, std::numeric_limits<double>::min());
verify_integer_multiplication_by_power_of_10(
17976931348623158, 292, std::numeric_limits<double>::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(
49406564584124654 / 2 + 1, -340,
std::numeric_limits<double>::denorm_min());
// DBL_MAX + 0.0000000000000001e308 overflows to infinity
verify_integer_multiplication_by_power_of_10(
17976931348623158 + 1, 292, std::numeric_limits<double>::infinity());
// DBL_MAX + 0.00000000000000001e308 overflows to infinity
verify_integer_multiplication_by_power_of_10(
179769313486231580 + 1, 291, std::numeric_limits<double>::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<int64_t>::max(), 42);
verify_integer_multiplication_by_power_of_10(
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

@ -0,0 +1,12 @@
#include "fast_float/fast_float.h"
#include <iostream>
int main() {
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 << W << " * 10^" << Q << " = " << result << " ("
<< (result == 12345678901234567e23 ? "==" : "!=") << "expected)\n";
}

View File

@ -831,6 +831,275 @@ int main() {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// dont parse UTF-16 code units of emojis as int if low byte is ascii digit
{
const std::u16string emojis[] = {
u"", u"", u"", u"☸️", u"", u"☹️", u"", u"✳️",
u"", u"✴️", u"", u"⤴️", u"", u"⤵️", u"", u"〰️",
};
bool failed = false;
auto array_size = sizeof(emojis) / sizeof(emojis[0]);
for (size_t i = 0; i < array_size; i++) {
auto e = emojis[i];
int foo;
auto answer = fast_float::from_chars(e.data(), e.data() + e.size(), foo);
if (answer.ec == std::errc()) {
failed = true;
std::cerr << "Incorrectly parsed emoji #" << i << " as integer " << foo
<< "." << std::endl;
}
}
if (failed) {
return EXIT_FAILURE;
}
}
// dont parse UTF-32 code points of emojis as int if low byte is ascii digit
{
const std::u32string emojis[] = {
U"",
U"",
U"",
U"☸️",
U"",
U"☹️",
U"",
U"✳️",
U"",
U"✴️",
U"",
U"⤴️",
U"",
U"⤵️",
U"",
U"〰️",
U"🈲",
U"🈳",
U"🈴",
U"🈵",
U"🈶",
U"🈷",
U"🈷️",
U"🈸",
U"🈹",
U"🌰",
U"🌱",
U"🌲",
U"🌳",
U"🌴",
U"🌵",
U"🌶",
U"🌶️",
U"🌷",
U"🌸",
U"🌹",
U"🐰",
U"🐱",
U"🐲",
U"🐳",
U"🐴",
U"🐵",
U"🐶",
U"🐷",
U"🐸",
U"🐹",
U"🔰",
U"🔱",
U"🔲",
U"🔳",
U"🔴",
U"🔵",
U"🔶",
U"🔷",
U"🔸",
U"🔹",
U"😰",
U"😱",
U"😲",
U"😳",
U"😴",
U"😵",
U"😵‍💫",
U"😶",
U"😶‍🌫",
U"😶‍🌫️",
U"😷",
U"😸",
U"😹",
U"🤰",
U"🤰🏻",
U"🤰🏼",
U"🤰🏽",
U"🤰🏾",
U"🤰🏿",
U"🤱",
U"🤱🏻",
U"🤱🏼",
U"🤱🏽",
U"🤱🏾",
U"🤱🏿",
U"🤲",
U"🤲🏻",
U"🤲🏼",
U"🤲🏽",
U"🤲🏾",
U"🤲🏿",
U"🤳",
U"🤳🏻",
U"🤳🏼",
U"🤳🏽",
U"🤳🏾",
U"🤳🏿",
U"🤴",
U"🤴🏻",
U"🤴🏼",
U"🤴🏽",
U"🤴🏾",
U"🤴🏿",
U"🤵",
U"🤵‍♀",
U"🤵‍♀️",
U"🤵‍♂",
U"🤵‍♂️",
U"🤵🏻",
U"🤵🏻‍♀",
U"🤵🏻‍♀️",
U"🤵🏻‍♂",
U"🤵🏻‍♂️",
U"🤵🏼",
U"🤵🏼‍♀",
U"🤵🏼‍♀️",
U"🤵🏼‍♂",
U"🤵🏼‍♂️",
U"🤵🏽",
U"🤵🏽‍♀",
U"🤵🏽‍♀️",
U"🤵🏽‍♂",
U"🤵🏽‍♂️",
U"🤵🏾",
U"🤵🏾‍♀",
U"🤵🏾‍♀️",
U"🤵🏾‍♂",
U"🤵🏾‍♂️",
U"🤵🏿",
U"🤵🏿‍♀",
U"🤵🏿‍♀️",
U"🤵🏿‍♂",
U"🤵🏿‍♂️",
U"🤶",
U"🤶🏻",
U"🤶🏼",
U"🤶🏽",
U"🤶🏾",
U"🤶🏿",
U"🤷",
U"🤷‍♀",
U"🤷‍♀️",
U"🤷‍♂",
U"🤷‍♂️",
U"🤷🏻",
U"🤷🏻‍♀",
U"🤷🏻‍♀️",
U"🤷🏻‍♂",
U"🤷🏻‍♂️",
U"🤷🏼",
U"🤷🏼‍♀",
U"🤷🏼‍♀️",
U"🤷🏼‍♂",
U"🤷🏼‍♂️",
U"🤷🏽",
U"🤷🏽‍♀",
U"🤷🏽‍♀️",
U"🤷🏽‍♂",
U"🤷🏽‍♂️",
U"🤷🏾",
U"🤷🏾‍♀",
U"🤷🏾‍♀️",
U"🤷🏾‍♂",
U"🤷🏾‍♂️",
U"🤷🏿",
U"🤷🏿‍♀",
U"🤷🏿‍♀️",
U"🤷🏿‍♂",
U"🤷🏿‍♂️",
U"🤸",
U"🤸‍♀",
U"🤸‍♀️",
U"🤸‍♂",
U"🤸‍♂️",
U"🤸🏻",
U"🤸🏻‍♀",
U"🤸🏻‍♀️",
U"🤸🏻‍♂",
U"🤸🏻‍♂️",
U"🤸🏼",
U"🤸🏼‍♀",
U"🤸🏼‍♀️",
U"🤸🏼‍♂",
U"🤸🏼‍♂️",
U"🤸🏽",
U"🤸🏽‍♀",
U"🤸🏽‍♀️",
U"🤸🏽‍♂",
U"🤸🏽‍♂️",
U"🤸🏾",
U"🤸🏾‍♀",
U"🤸🏾‍♀️",
U"🤸🏾‍♂",
U"🤸🏾‍♂️",
U"🤸🏿",
U"🤸🏿‍♀",
U"🤸🏿‍♀️",
U"🤸🏿‍♂",
U"🤸🏿‍♂️",
U"🤹",
U"🤹‍♀",
U"🤹‍♀️",
U"🤹‍♂",
U"🤹‍♂️",
U"🤹🏻",
U"🤹🏻‍♀",
U"🤹🏻‍♀️",
U"🤹🏻‍♂",
U"🤹🏻‍♂️",
U"🤹🏼",
U"🤹🏼‍♀",
U"🤹🏼‍♀️",
U"🤹🏼‍♂",
U"🤹🏼‍♂️",
U"🤹🏽",
U"🤹🏽‍♀",
U"🤹🏽‍♀️",
U"🤹🏽‍♂",
U"🤹🏽‍♂️",
U"🤹🏾",
U"🤹🏾‍♀",
U"🤹🏾‍♀️",
U"🤹🏾‍♂",
U"🤹🏾‍♂️",
U"🤹🏿",
U"🤹🏿‍♀",
U"🤹🏿‍♀️",
U"🤹🏿‍♂",
U"🤹🏿‍♂️",
};
bool failed = false;
auto array_size = sizeof(emojis) / sizeof(emojis[0]);
for (size_t i = 0; i < array_size; i++) {
auto e = emojis[i];
int foo;
auto answer = fast_float::from_chars(e.data(), e.data() + e.size(), foo);
if (answer.ec == std::errc()) {
failed = true;
std::cerr << "Incorrectly parsed emoji #" << i << " as integer " << foo
<< "." << std::endl;
}
}
if (failed) {
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
@ -842,4 +1111,4 @@ int main() {
std::cerr << "The test requires C++17." << std::endl; std::cerr << "The test requires C++17." << std::endl;
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
#endif #endif