3rdparty: Update fast_float to v8.1.0

Signed-off-by: SternXD <stern@sidestore.io>
This commit is contained in:
SternXD 2025-11-26 13:06:40 -05:00 committed by Ty
parent 98c74b939a
commit 746174d73d
5 changed files with 191 additions and 49 deletions

View File

@ -57,6 +57,7 @@ Example:
```C++
#include "fast_float/fast_float.h"
#include <iostream>
#include <string>
int main() {
std::string input = "3.1416 xyz ";
@ -68,6 +69,25 @@ int main() {
}
```
Though the C++17 standard has you do a comparison with `std::errc()` to check whether the conversion worked, you can avoid it by casting the result to a `bool` like so:
```cpp
#include "fast_float/fast_float.h"
#include <iostream>
#include <string>
int main() {
std::string input = "3.1416 xyz ";
double result;
if(auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result)) {
std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}
std::cerr << "failed to parse " << result << std::endl;
return EXIT_FAILURE;
}
```
You can parse delimited numbers:
```C++
@ -357,6 +377,34 @@ 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.
## Users and Related Work
The fast_float library is part of:
@ -364,6 +412,8 @@ The fast_float library is part of:
* 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
Chrome, Microsoft Edge, and Opera,
* Boost JSON, MySQL, etc.
* Blender
* [WebKit](https://github.com/WebKit/WebKit), the engine behind Safari (Apple's
web browser),
* [DuckDB](https://duckdb.org),
@ -376,7 +426,10 @@ The fast_float library is part of:
The fastfloat algorithm is part of the [LLVM standard
libraries](https://github.com/llvm/llvm-project/commit/87c016078ad72c46505461e4ff8bfa04819fe7ba).
There is a [derived implementation part of
AdaCore](https://github.com/AdaCore/VSS).
AdaCore](https://github.com/AdaCore/VSS). The [SerenityOS operating
system](https://github.com/SerenityOS/serenity/commit/53b7f5e6a11e663c83df8030c3171c5945cb75ec)
has a derived implementation that is inherited by the [Ladybird
Browser](https://github.com/LadybirdBrowser/ladybird).
The fast_float library provides a performance similar to that of the
[fast_double_parser](https://github.com/lemire/fast_double_parser) library but
@ -385,6 +438,14 @@ API more in line with the expectations of C++ programmers. The
fast_double_parser library is part of the [Microsoft LightGBM machine-learning
framework](https://github.com/microsoft/LightGBM).
Packages
------
[![Packaging status](https://repology.org/badge/vertical-allrepos/fastfloat.svg)](https://repology.org/project/fastfloat/versions)
## References
* Daniel Lemire, [Number Parsing at a Gigabyte per
@ -455,7 +516,7 @@ sufficiently recent version of CMake (3.11 or better at least):
FetchContent_Declare(
fast_float
GIT_REPOSITORY https://github.com/fastfloat/fast_float.git
GIT_TAG tags/v8.0.2
GIT_TAG tags/v8.1.0
GIT_SHALLOW TRUE)
FetchContent_MakeAvailable(fast_float)
@ -471,7 +532,7 @@ You may also use [CPM](https://github.com/cpm-cmake/CPM.cmake), like so:
CPMAddPackage(
NAME fast_float
GITHUB_REPOSITORY "fastfloat/fast_float"
GIT_TAG v8.0.2)
GIT_TAG v8.1.0)
```
## Using as single header
@ -483,7 +544,7 @@ if desired as described in the command line help.
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
@ -522,6 +583,7 @@ cmake --build build
manager](https://conan.io/center/recipes/fast_float).
* It is part of the [brew package
manager](https://formulae.brew.sh/formula/fast_float).
* fast_float is available on [xmake](https://xmake.io) repository.
* Some Linux distribution like Fedora include fast_float (e.g., as
`fast_float-devel`).

View File

@ -441,7 +441,7 @@ parse_number_string(UC const *p, UC const *pend,
if (digit_count > 19) {
answer.too_many_digits = true;
// Let us start again, this time, avoiding overflows.
// We don't need to check if is_integer, since we use the
// We don't need to call if is_integer, since we use the
// pre-tokenized spans from above.
i = 0;
p = answer.integer.ptr;
@ -451,7 +451,7 @@ parse_number_string(UC const *p, UC const *pend,
i = i * 10 + uint64_t(*p - UC('0'));
++p;
}
if (i >= minimal_nineteen_digit_integer) { // We have a big integers
if (i >= minimal_nineteen_digit_integer) { // We have a big integer
exponent = end_of_integer_part - p + exp_number;
} else { // We have a value with a fractional component.
p = answer.fraction.ptr;

View File

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

View File

@ -16,8 +16,8 @@
#include "constexpr_feature_detect.h"
#define FASTFLOAT_VERSION_MAJOR 8
#define FASTFLOAT_VERSION_MINOR 0
#define FASTFLOAT_VERSION_PATCH 2
#define FASTFLOAT_VERSION_MINOR 1
#define FASTFLOAT_VERSION_PATCH 0
#define FASTFLOAT_STRINGIZE_IMPL(x) #x
#define FASTFLOAT_STRINGIZE(x) FASTFLOAT_STRINGIZE_IMPL(x)
@ -58,6 +58,11 @@ enum class chars_format : uint64_t {
template <typename UC> struct from_chars_result_t {
UC const *ptr;
std::errc ec;
// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2497r0.html
constexpr explicit operator bool() const noexcept {
return ec == std::errc();
}
};
using from_chars_result = from_chars_result_t<char>;
@ -88,11 +93,12 @@ using parse_options = parse_options_t<char>;
defined(__MINGW64__) || defined(__s390x__) || \
(defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || \
defined(__PPC64LE__)) || \
defined(__loongarch64))
defined(__loongarch64) || (defined(__riscv) && __riscv_xlen == 64))
#define FASTFLOAT_64BIT 1
#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) || \
defined(__arm__) || defined(_M_ARM) || defined(__ppc__) || \
defined(__MINGW32__) || defined(__EMSCRIPTEN__))
defined(__MINGW32__) || defined(__EMSCRIPTEN__) || \
(defined(__riscv) && __riscv_xlen == 32))
#define FASTFLOAT_32BIT 1
#else
// Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow.
@ -1126,7 +1132,12 @@ template <typename T> constexpr uint64_t int_luts<T>::min_safe_u64[];
template <typename UC>
fastfloat_really_inline constexpr uint8_t ch_to_digit(UC c) {
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 size_t max_digits_u64(int base) {

View File

@ -188,32 +188,17 @@ from_chars(UC const *first, UC const *last, T &value,
parse_options_t<UC>(fmt));
}
/**
* This function overload takes parsed_number_string_t structure that is created
* and populated either by from_chars_advanced function taking chars range and
* parsing options or other parsing custom function implemented by user.
*/
template <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;
template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
T &value) noexcept {
// The implementation of the Clinger's fast path is convoluted because
// we want round-to-nearest in all cases, irrespective of the rounding mode
// selected on the thread.
// We proceed optimistically, assuming that detail::rounds_to_nearest()
// returns true.
if (binary_format<T>::min_exponent_fast_path() <= pns.exponent &&
pns.exponent <= binary_format<T>::max_exponent_fast_path() &&
!pns.too_many_digits) {
if (binary_format<T>::min_exponent_fast_path() <= exponent &&
exponent <= binary_format<T>::max_exponent_fast_path()) {
// Unfortunately, the conventional Clinger's fast path is only possible
// when the system rounds to the nearest float.
//
@ -224,41 +209,64 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) {
// We have that fegetround() == FE_TONEAREST.
// Next is Clinger's fast path.
if (pns.mantissa <= binary_format<T>::max_mantissa_fast_path()) {
value = T(pns.mantissa);
if (pns.exponent < 0) {
value = value / binary_format<T>::exact_power_of_ten(-pns.exponent);
if (mantissa <= binary_format<T>::max_mantissa_fast_path()) {
value = T(mantissa);
if (exponent < 0) {
value = value / binary_format<T>::exact_power_of_ten(-exponent);
} else {
value = value * binary_format<T>::exact_power_of_ten(pns.exponent);
value = value * binary_format<T>::exact_power_of_ten(exponent);
}
if (pns.negative) {
if (is_negative) {
value = -value;
}
return answer;
return true;
}
} else {
// We do not have that fegetround() == FE_TONEAREST.
// Next is a modified Clinger's fast path, inspired by Jakub Jelínek's
// proposal
if (pns.exponent >= 0 &&
pns.mantissa <=
binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
if (exponent >= 0 &&
mantissa <= binary_format<T>::max_mantissa_fast_path(exponent)) {
#if defined(__clang__) || defined(FASTFLOAT_32BIT)
// Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
if (pns.mantissa == 0) {
value = pns.negative ? T(-0.) : T(0.);
return answer;
if (mantissa == 0) {
value = is_negative ? T(-0.) : T(0.);
return true;
}
#endif
value = T(pns.mantissa) *
binary_format<T>::exact_power_of_ten(pns.exponent);
if (pns.negative) {
value = T(mantissa) * binary_format<T>::exact_power_of_ten(exponent);
if (is_negative) {
value = -value;
}
return answer;
return true;
}
}
}
return false;
}
/**
* This function overload takes parsed_number_string_t structure that is created
* and populated either by from_chars_advanced function taking chars range and
* parsing options or other parsing custom function implemented by user.
*/
template <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, pns.negative, value))
return answer;
adjusted_mantissa am =
compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
if (pns.too_many_digits && am.power2 >= 0) {
@ -336,6 +344,49 @@ 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;
if (clinger_fast_path_impl(mantissa, decimal_exponent, false, value))
return value;
adjusted_mantissa am =
compute_float<binary_format<double>>(decimal_exponent, mantissa);
to_float(false, am, value);
return value;
}
FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept {
const bool is_negative = mantissa < 0;
const uint64_t m = static_cast<uint64_t>(is_negative ? -mantissa : mantissa);
double value;
if (clinger_fast_path_impl(m, decimal_exponent, is_negative, value))
return value;
adjusted_mantissa am =
compute_float<binary_format<double>>(decimal_exponent, m);
to_float(is_negative, 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>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_int_advanced(UC const *first, UC const *last, T &value,