Merge 9e1d063628e6db7d4495859f74db75f6573c6892 into 7b21183a93c4a8943a2d384f207537d7330547e1

This commit is contained in:
HedgehogInTheCPP 2025-11-08 21:12:49 +00:00 committed by GitHub
commit 5c123e8614
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 958 additions and 664 deletions

View File

@ -27,7 +27,7 @@ jobs:
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.1.7 - uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.1.7
- name: Run clang-format - name: Run clang-format
uses: jidicula/clang-format-action@4726374d1aa3c6aecf132e5197e498979588ebc8 # v4.15.0 uses: jidicula/clang-format-action@6cd220de46c89139a0365edae93eee8eb30ca8fe # v4.16.0
with: with:
clang-format-version: '17' clang-format-version: '17'

View File

@ -11,7 +11,7 @@ jobs:
matrix: matrix:
include: include:
- {gen: Visual Studio 17 2022, arch: Win32, cfg: Release} - {gen: Visual Studio 17 2022, arch: Win32, cfg: Release}
#- {gen: Visual Studio 17 2022, arch: Win32, cfg: Debug} - {gen: Visual Studio 17 2022, arch: Win32, cfg: Debug}
- {gen: Visual Studio 17 2022, arch: x64, cfg: Release} - {gen: Visual Studio 17 2022, arch: x64, cfg: Release}
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug} - {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
steps: steps:

View File

@ -9,3 +9,4 @@ Jan Pharago
Maya Warrier Maya Warrier
Taha Khokhar Taha Khokhar
Anders Dalvander Anders Dalvander
Elle Solomina

View File

@ -35,7 +35,7 @@ struct from_chars_result {
}; };
``` ```
It parses the character sequence `[first, last)` for a number. It parses It parses the character sequence `[first, last]` for a number. It parses
floating-point numbers expecting a locale-independent format equivalent to the floating-point numbers expecting a locale-independent format equivalent to the
C++17 from_chars function. The resulting floating-point value is the closest C++17 from_chars function. The resulting floating-point value is the closest
floating-point values (using either `float` or `double`), using the "round to floating-point values (using either `float` or `double`), using the "round to
@ -48,7 +48,8 @@ parsed value. In case of error, the returned `ec` contains a representative
error, otherwise the default (`std::errc()`) value is stored. error, otherwise the default (`std::errc()`) value is stored.
The implementation does not throw and does not allocate memory (e.g., with `new` The implementation does not throw and does not allocate memory (e.g., with `new`
or `malloc`). or `malloc`) and can be usable in the kernel, embeded and other scenarious that
relays on such behavior.
It will parse infinity and nan values. It will parse infinity and nan values.
@ -288,7 +289,7 @@ int main() {
} }
``` ```
## Advanced options: using commas as decimal separator, JSON and Fortran ## Advanced options: using commas as decimal separator, parse JSON, Fortran and more
The C++ standard stipulate that `from_chars` has to be locale-independent. In The C++ standard stipulate that `from_chars` has to be locale-independent. In
particular, the decimal separator has to be the period (`.`). However, some particular, the decimal separator has to be the period (`.`). However, some
@ -377,6 +378,32 @@ int main() {
} }
``` ```
## You also can also use some additional options (currently only configure by macroses):
There is a really common use case in mathematical and other abstract syntax tree (AST) like parsers,
that already process sign and all other symbols before any number by itself. In this case you can
use FastFloat for only parse positive numbers in all supported formats with macros
`FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN`, that significantly reduce the code size and improve
performance. Additionally you can use macros `FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED` if you
only uneed `FE_TONEAREST` rounding mode in the parsing: this option is also improve performance a bit.
```C++
#define FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
#define FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
#include "fast_float/fast_float.h"
#include <iostream>
int main() {
std::string input = "23.14069263277926900572";
double result;
auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result);
if ((answer.ec != std::errc()) || ((result != 23.14069263277927 /*properly rounded value */)))
{ std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
return EXIT_SUCCESS;
}
```
## Multiplication of an integer by a power of 10 ## Multiplication of an integer by a power of 10
An integer `W` can be multiplied by a power of ten `10^Q` and An integer `W` can be multiplied by a power of ten `10^Q` and
converted to `double` with correctly rounded value converted to `double` with correctly rounded value
@ -421,7 +448,6 @@ 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.
## Users and Related Work ## Users and Related Work
The fast_float library is part of: The fast_float library is part of:

View File

@ -1,10 +1,12 @@
// #define FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
// #define FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
#if defined(__linux__) || (__APPLE__ && __aarch64__) #if defined(__linux__) || (__APPLE__ && __aarch64__)
#define USING_COUNTERS #define USING_COUNTERS
#endif #endif
#include "event_counter.h" #include "event_counter.h"
#include <algorithm> #include <algorithm>
#include "fast_float/fast_float.h"
#include <chrono>
#include <climits> #include <climits>
#include <cmath> #include <cmath>
#include <cstdint> #include <cstdint>
@ -19,15 +21,17 @@
#include <sstream> #include <sstream>
#include <stdio.h> #include <stdio.h>
#include <string> #include <string>
#include <vector>
#include <locale.h> #include <locale.h>
template <typename CharT> #include "fast_float/fast_float.h"
double findmax_fastfloat64(std::vector<std::basic_string<CharT>> &s) {
double answer = 0; template <typename CharT, typename Value>
double x = 0; Value findmax_fastfloat(std::vector<std::basic_string<CharT>> &s) {
Value answer = 0;
Value x = 0;
for (auto &st : s) { for (auto &st : s) {
auto [p, ec] = fast_float::from_chars(st.data(), st.data() + st.size(), x); auto [p, ec] = fast_float::from_chars(st.data(), st.data() + st.size(), x);
if (p == st.data()) { if (p == st.data()) {
throw std::runtime_error("bug in findmax_fastfloat"); throw std::runtime_error("bug in findmax_fastfloat");
} }
@ -36,23 +40,10 @@ double findmax_fastfloat64(std::vector<std::basic_string<CharT>> &s) {
return answer; return answer;
} }
template <typename CharT> #ifdef USING_COUNTERS
double findmax_fastfloat32(std::vector<std::basic_string<CharT>> &s) {
float answer = 0;
float x = 0;
for (auto &st : s) {
auto [p, ec] = fast_float::from_chars(st.data(), st.data() + st.size(), x);
if (p == st.data()) {
throw std::runtime_error("bug in findmax_fastfloat");
}
answer = answer > x ? answer : x;
}
return answer;
}
event_collector collector{}; event_collector collector{};
#ifdef USING_COUNTERS
template <class T, class CharT> template <class T, class CharT>
std::vector<event_count> std::vector<event_count>
time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function, time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
@ -61,7 +52,7 @@ time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
bool printed_bug = false; bool printed_bug = false;
for (size_t i = 0; i < repeat; i++) { for (size_t i = 0; i < repeat; i++) {
collector.start(); collector.start();
double ts = function(lines); auto const ts = function(lines);
if (ts == 0 && !printed_bug) { if (ts == 0 && !printed_bug) {
printf("bug\n"); printf("bug\n");
printed_bug = true; printed_bug = true;
@ -71,7 +62,7 @@ time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
return aggregate; return aggregate;
} }
void pretty_print(double volume, size_t number_of_floats, std::string name, void pretty_print(size_t volume, size_t number_of_floats, std::string name,
std::vector<event_count> events) { std::vector<event_count> events) {
double volumeMB = volume / (1024. * 1024.); double volumeMB = volume / (1024. * 1024.);
double average_ns{0}; double average_ns{0};
@ -141,14 +132,14 @@ time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
bool printed_bug = false; bool printed_bug = false;
for (size_t i = 0; i < repeat; i++) { for (size_t i = 0; i < repeat; i++) {
t1 = std::chrono::high_resolution_clock::now(); t1 = std::chrono::high_resolution_clock::now();
double ts = function(lines); auto const ts = function(lines);
if (ts == 0 && !printed_bug) { if (ts == 0 && !printed_bug) {
printf("bug\n"); printf("bug\n");
printed_bug = true; printed_bug = true;
} }
t2 = std::chrono::high_resolution_clock::now(); t2 = std::chrono::high_resolution_clock::now();
double dif = double const dif = static_cast<double>(
std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count(); std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count());
average += dif; average += dif;
min_value = min_value < dif ? min_value : dif; min_value = min_value < dif ? min_value : dif;
} }
@ -156,8 +147,8 @@ time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
return std::make_pair(min_value, average); return std::make_pair(min_value, average);
} }
void pretty_print(double volume, size_t number_of_floats, std::string name, void pretty_print(size_t volume, size_t number_of_floats,
std::pair<double, double> result) { std::string const &name, std::pair<double, double> result) {
double volumeMB = volume / (1024. * 1024.); double volumeMB = volume / (1024. * 1024.);
printf("%-40s: %8.2f MB/s (+/- %.1f %%) ", name.data(), printf("%-40s: %8.2f MB/s (+/- %.1f %%) ", name.data(),
volumeMB * 1000000000 / result.first, volumeMB * 1000000000 / result.first,
@ -168,7 +159,7 @@ void pretty_print(double volume, size_t number_of_floats, std::string name,
#endif #endif
// this is okay, all chars are ASCII // this is okay, all chars are ASCII
inline std::u16string widen(std::string line) { inline std::u16string widen(std::string const &line) {
std::u16string u16line; std::u16string u16line;
u16line.resize(line.size()); u16line.resize(line.size());
for (size_t i = 0; i < line.size(); ++i) { for (size_t i = 0; i < line.size(); ++i) {
@ -181,28 +172,29 @@ std::vector<std::u16string> widen(const std::vector<std::string> &lines) {
std::vector<std::u16string> u16lines; std::vector<std::u16string> u16lines;
u16lines.reserve(lines.size()); u16lines.reserve(lines.size());
for (auto const &line : lines) { for (auto const &line : lines) {
u16lines.push_back(widen(line)); u16lines.emplace_back(widen(line));
} }
return u16lines; return u16lines;
} }
void process(std::vector<std::string> &lines, size_t volume) { void process(std::vector<std::string> &lines, size_t volume) {
size_t repeat = 1000; size_t const repeat = 1000;
double volumeMB = volume / (1024. * 1024.); double volumeMB = volume / (1024. * 1024.);
std::cout << "ASCII volume = " << volumeMB << " MB " << std::endl; std::cout << "ASCII volume = " << volumeMB << " MB " << std::endl;
pretty_print(volume, lines.size(), "fastfloat (64)", pretty_print(volume, lines.size(), "fastfloat (64)",
time_it_ns(lines, findmax_fastfloat64<char>, repeat)); time_it_ns(lines, findmax_fastfloat<char, double>, repeat));
pretty_print(volume, lines.size(), "fastfloat (32)", pretty_print(volume, lines.size(), "fastfloat (32)",
time_it_ns(lines, findmax_fastfloat32<char>, repeat)); time_it_ns(lines, findmax_fastfloat<char, float>, repeat));
std::vector<std::u16string> lines16 = widen(lines); std::vector<std::u16string> lines16 = widen(lines);
volume = 2 * volume; volume = 2 * volume;
volumeMB = volume / (1024. * 1024.); volumeMB = volume / (1024. * 1024.);
std::cout << "UTF-16 volume = " << volumeMB << " MB " << std::endl; std::cout << "UTF-16 volume = " << volumeMB << " MB " << std::endl;
pretty_print(volume, lines.size(), "fastfloat (64)", pretty_print(
time_it_ns(lines16, findmax_fastfloat64<char16_t>, repeat)); volume, lines.size(), "fastfloat (64)",
time_it_ns(lines16, findmax_fastfloat<char16_t, double>, repeat));
pretty_print(volume, lines.size(), "fastfloat (32)", pretty_print(volume, lines.size(), "fastfloat (32)",
time_it_ns(lines16, findmax_fastfloat32<char16_t>, repeat)); time_it_ns(lines16, findmax_fastfloat<char16_t, float>, repeat));
} }
void fileload(std::string filename) { void fileload(std::string filename) {
@ -216,17 +208,31 @@ void fileload(std::string filename) {
std::cout << "#### " << std::endl; std::cout << "#### " << std::endl;
std::string line; std::string line;
std::vector<std::string> lines; std::vector<std::string> lines;
lines.reserve(10000); // let us reserve plenty of memory. lines.reserve(120000); // let us reserve plenty of memory.
size_t volume = 0; size_t volume = 0;
while (getline(inputfile, line)) { while (getline(inputfile, line)) {
#ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (line[0] == '-') {
line.erase(0, 1);
}
#endif
lines.emplace_back(line);
volume += line.size(); volume += line.size();
lines.push_back(line);
} }
std::cout << "# read " << lines.size() << " lines " << std::endl; std::cout << "# read " << lines.size() << " lines " << std::endl;
process(lines, volume); process(lines, volume);
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
#ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
std::cout << "# FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN is enabled"
<< std::endl;
#endif
#ifdef FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
std::cout << "# FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED is enabled"
<< std::endl;
#endif
#ifdef USING_COUNTERS
if (collector.has_events()) { if (collector.has_events()) {
std::cout << "# Using hardware counters" << std::endl; std::cout << "# Using hardware counters" << std::endl;
} else { } else {
@ -236,10 +242,12 @@ int main(int argc, char **argv) {
<< std::endl; << std::endl;
#endif #endif
} }
#endif
if (argc > 1) { if (argc > 1) {
fileload(argv[1]); fileload(argv[1]);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
fileload(std::string(BENCHMARK_DATA_DIR) + "/canada.txt"); fileload(std::string(BENCHMARK_DATA_DIR) + "/canada.txt");
fileload(std::string(BENCHMARK_DATA_DIR) + "/mesh.txt"); fileload(std::string(BENCHMARK_DATA_DIR) + "/mesh.txt");
return EXIT_SUCCESS; return EXIT_SUCCESS;

View File

@ -10,7 +10,7 @@
#include <cstring> #include <cstring>
#include <chrono> #include <chrono>
#include <vector> #include <array>
#include "linux-perf-events.h" #include "linux-perf-events.h"
#ifdef __linux__ #ifdef __linux__
@ -22,26 +22,28 @@
#endif #endif
struct event_count { struct event_count {
std::chrono::duration<double> elapsed;
std::vector<unsigned long long> event_counts;
event_count() : elapsed(0), event_counts{0, 0, 0, 0, 0} {}
event_count(const std::chrono::duration<double> _elapsed,
const std::vector<unsigned long long> _event_counts)
: elapsed(_elapsed), event_counts(_event_counts) {}
event_count(const event_count &other)
: elapsed(other.elapsed), event_counts(other.event_counts) {}
// The types of counters (so we can read the getter more easily) // The types of counters (so we can read the getter more easily)
enum event_counter_types { enum event_counter_types {
CPU_CYCLES = 0, CPU_CYCLES = 0,
INSTRUCTIONS = 1, INSTRUCTIONS = 1,
BRANCHES = 2, BRANCHES = 2,
MISSED_BRANCHES = 3 MISSED_BRANCHES = 3,
event_counter_types_size = 4
}; };
std::chrono::duration<double> elapsed;
std::array<unsigned long long, event_counter_types_size> event_counts;
event_count() : elapsed(0), event_counts{0, 0, 0, 0} {}
event_count(const std::chrono::duration<double> &_elapsed,
const std::array<unsigned long long, event_counter_types_size>
&_event_counts)
: elapsed(_elapsed), event_counts(_event_counts) {}
event_count(const event_count &other)
: elapsed(other.elapsed), event_counts(other.event_counts) {}
double elapsed_sec() const { double elapsed_sec() const {
return std::chrono::duration<double>(elapsed).count(); return std::chrono::duration<double>(elapsed).count();
} }
@ -79,7 +81,6 @@ struct event_count {
event_counts[1] + other.event_counts[1], event_counts[1] + other.event_counts[1],
event_counts[2] + other.event_counts[2], event_counts[2] + other.event_counts[2],
event_counts[3] + other.event_counts[3], event_counts[3] + other.event_counts[3],
event_counts[4] + other.event_counts[4],
}); });
} }
@ -129,7 +130,7 @@ struct event_collector {
LinuxEvents<PERF_TYPE_HARDWARE> linux_events; LinuxEvents<PERF_TYPE_HARDWARE> linux_events;
event_collector() event_collector()
: linux_events(std::vector<int>{ : linux_events(std::array<unsigned long long, 4>{
PERF_COUNT_HW_CPU_CYCLES, PERF_COUNT_HW_INSTRUCTIONS, PERF_COUNT_HW_CPU_CYCLES, PERF_COUNT_HW_INSTRUCTIONS,
PERF_COUNT_HW_BRANCH_INSTRUCTIONS, // Retired branch instructions PERF_COUNT_HW_BRANCH_INSTRUCTIONS, // Retired branch instructions
PERF_COUNT_HW_BRANCH_MISSES}) {} PERF_COUNT_HW_BRANCH_MISSES}) {}
@ -142,7 +143,7 @@ struct event_collector {
bool has_events() { return setup_performance_counters(); } bool has_events() { return setup_performance_counters(); }
#else #else
event_collector() {} event_collector() = default;
bool has_events() { return false; } bool has_events() { return false; }
#endif #endif
@ -171,7 +172,6 @@ struct event_collector {
count.event_counts[1] = diff.instructions; count.event_counts[1] = diff.instructions;
count.event_counts[2] = diff.branches; count.event_counts[2] = diff.branches;
count.event_counts[3] = diff.missed_branches; count.event_counts[3] = diff.missed_branches;
count.event_counts[4] = 0;
#endif #endif
count.elapsed = end_clock - start_clock; count.elapsed = end_clock - start_clock;
return count; return count;

View File

@ -10,7 +10,7 @@
#include <cstring> // for memset #include <cstring> // for memset
#include <stdexcept> #include <stdexcept>
#include <iostream> #include <array>
#include <vector> #include <vector>
template <int TYPE = PERF_TYPE_HARDWARE> class LinuxEvents { template <int TYPE = PERF_TYPE_HARDWARE> class LinuxEvents {
@ -22,7 +22,8 @@ template <int TYPE = PERF_TYPE_HARDWARE> class LinuxEvents {
std::vector<uint64_t> ids{}; std::vector<uint64_t> ids{};
public: public:
explicit LinuxEvents(std::vector<int> config_vec) : fd(0), working(true) { explicit LinuxEvents(std::array<unsigned long long, 4> config_vec)
: fd(0), working(true) {
memset(&attribs, 0, sizeof(attribs)); memset(&attribs, 0, sizeof(attribs));
attribs.type = TYPE; attribs.type = TYPE;
attribs.size = sizeof(attribs); attribs.size = sizeof(attribs);
@ -75,7 +76,7 @@ public:
} }
} }
inline void end(std::vector<unsigned long long> &results) { inline void end(std::array<unsigned long long, 4> &results) {
if (fd != -1) { if (fd != -1) {
if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) { if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) {
report_error("ioctl(PERF_EVENT_IOC_DISABLE)"); report_error("ioctl(PERF_EVENT_IOC_DISABLE)");

28
format.cmd Normal file
View File

@ -0,0 +1,28 @@
@echo off
setlocal enabledelayedexpansion
echo Searching for clang-format...
where clang-format >nul 2>&1
if %errorlevel% neq 0 (
echo Error: clang-format not found in PATH. Install LLVM or add it to PATH.
exit /b 1
)
echo Checking for .clang-format...
if not exist ".clang-format" (
echo Error: .clang-format config file not found in the current directory.
exit /b 1
)
echo Formatting files with .clang-format...
set count=0
for /R ".\" %%f in (*.cpp, *.h, *.c, *.hpp) do (
echo "%%f" | findstr /i "\\build\\ \\.vs\\ \\.git\\ \\.github\\" >nul
if !errorlevel! equ 1 (
echo Formatting "%%f"
clang-format -i -style=file "%%f"
set /a count+=1
)
)
echo Done. Processed !count! files.

View File

@ -10,17 +10,16 @@
#include "float_common.h" #include "float_common.h"
#ifdef FASTFLOAT_SSE2 #if defined(FASTFLOAT_SSE2)
#include <emmintrin.h> #include <emmintrin.h>
#endif #elif defined(FASTFLOAT_NEON)
#ifdef FASTFLOAT_NEON
#include <arm_neon.h> #include <arm_neon.h>
#endif #endif
namespace fast_float { namespace fast_float {
template <typename UC> fastfloat_really_inline constexpr bool has_simd_opt() { template <typename UC>
fastfloat_really_inline constexpr bool has_simd_opt() noexcept {
#ifdef FASTFLOAT_HAS_SIMD #ifdef FASTFLOAT_HAS_SIMD
return std::is_same<UC, char16_t>::value; return std::is_same<UC, char16_t>::value;
#else #else
@ -35,12 +34,14 @@ fastfloat_really_inline constexpr bool is_integer(UC c) noexcept {
return !(c > UC('9') || c < UC('0')); return !(c > UC('9') || c < UC('0'));
} }
fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) { #if FASTFLOAT_HAS_BYTESWAP == 0
fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) noexcept {
return (val & 0xFF00000000000000) >> 56 | (val & 0x00FF000000000000) >> 40 | return (val & 0xFF00000000000000) >> 56 | (val & 0x00FF000000000000) >> 40 |
(val & 0x0000FF0000000000) >> 24 | (val & 0x000000FF00000000) >> 8 | (val & 0x0000FF0000000000) >> 24 | (val & 0x000000FF00000000) >> 8 |
(val & 0x00000000FF000000) << 8 | (val & 0x0000000000FF0000) << 24 | (val & 0x00000000FF000000) << 8 | (val & 0x0000000000FF0000) << 24 |
(val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56; (val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56;
} }
#endif
// Read 8 UC into a u64. Truncates UC if not char. // Read 8 UC into a u64. Truncates UC if not char.
template <typename UC> template <typename UC>
@ -48,7 +49,7 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
read8_to_u64(UC const *chars) { read8_to_u64(UC const *chars) {
if (cpp20_and_in_constexpr() || !std::is_same<UC, char>::value) { if (cpp20_and_in_constexpr() || !std::is_same<UC, char>::value) {
uint64_t val = 0; uint64_t val = 0;
for (int i = 0; i < 8; ++i) { for (uint_fast8_t i = 0; i != 8; ++i) {
val |= uint64_t(uint8_t(*chars)) << (i * 8); val |= uint64_t(uint8_t(*chars)) << (i * 8);
++chars; ++chars;
} }
@ -58,18 +59,23 @@ read8_to_u64(UC const *chars) {
::memcpy(&val, chars, sizeof(uint64_t)); ::memcpy(&val, chars, sizeof(uint64_t));
#if FASTFLOAT_IS_BIG_ENDIAN == 1 #if FASTFLOAT_IS_BIG_ENDIAN == 1
// Need to read as-if the number was in little-endian order. // Need to read as-if the number was in little-endian order.
val = byteswap(val); val =
#if FASTFLOAT_HAS_BYTESWAP == 1
std::
#endif
byteswap(val);
#endif #endif
return val; return val;
} }
#ifdef FASTFLOAT_SSE2 #ifdef FASTFLOAT_SSE2
fastfloat_really_inline uint64_t simd_read8_to_u64(__m128i const data) { fastfloat_really_inline uint64_t simd_read8_to_u64(__m128i const &data) {
FASTFLOAT_SIMD_DISABLE_WARNINGS FASTFLOAT_SIMD_DISABLE_WARNINGS
// _mm_packus_epi16 is SSE2+, converts 8×u16 → 8×u8
__m128i const packed = _mm_packus_epi16(data, data); __m128i const packed = _mm_packus_epi16(data, data);
#ifdef FASTFLOAT_64BIT #ifdef FASTFLOAT_64BIT
return uint64_t(_mm_cvtsi128_si64(packed)); return static_cast<uint64_t>(_mm_cvtsi128_si64(packed));
#else #else
uint64_t value; uint64_t value;
// Visual Studio + older versions of GCC don't support _mm_storeu_si64 // Visual Studio + older versions of GCC don't support _mm_storeu_si64
@ -88,7 +94,7 @@ fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) {
#elif defined(FASTFLOAT_NEON) #elif defined(FASTFLOAT_NEON)
fastfloat_really_inline uint64_t simd_read8_to_u64(uint16x8_t const data) { fastfloat_really_inline uint64_t simd_read8_to_u64(uint16x8_t const &data) {
FASTFLOAT_SIMD_DISABLE_WARNINGS FASTFLOAT_SIMD_DISABLE_WARNINGS
uint8x8_t utf8_packed = vmovn_u16(data); uint8x8_t utf8_packed = vmovn_u16(data);
return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0); return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0);
@ -102,7 +108,7 @@ fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) {
FASTFLOAT_SIMD_RESTORE_WARNINGS FASTFLOAT_SIMD_RESTORE_WARNINGS
} }
#endif // FASTFLOAT_SSE2 #endif
// MSVC SFINAE is broken pre-VS2017 // MSVC SFINAE is broken pre-VS2017
#if defined(_MSC_VER) && _MSC_VER <= 1900 #if defined(_MSC_VER) && _MSC_VER <= 1900
@ -117,10 +123,10 @@ uint64_t simd_read8_to_u64(UC const *) {
// credit @aqrit // credit @aqrit
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t
parse_eight_digits_unrolled(uint64_t val) { parse_eight_digits_unrolled(uint64_t val) noexcept {
uint64_t const mask = 0x000000FF000000FF; constexpr uint64_t mask = 0x000000FF000000FF;
uint64_t const mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) constexpr uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
uint64_t const mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) constexpr uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
val -= 0x3030303030303030; val -= 0x3030303030303030;
val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8; val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32; val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
@ -157,19 +163,22 @@ simd_parse_if_eight_digits_unrolled(char16_t const *chars,
} }
#ifdef FASTFLOAT_SSE2 #ifdef FASTFLOAT_SSE2
FASTFLOAT_SIMD_DISABLE_WARNINGS FASTFLOAT_SIMD_DISABLE_WARNINGS
// Load 8 UTF-16 characters (16 bytes)
__m128i const data = __m128i const data =
_mm_loadu_si128(reinterpret_cast<__m128i const *>(chars)); _mm_loadu_si128(reinterpret_cast<__m128i const *>(chars));
// (x - '0') <= 9 // Branchless "are all digits?" trick from Lemire:
// (x - '0') <= 9 <=> (x + 32720) <= 32729
// encoded as signed comparison: (x + 32720) > -32759 ? not digit : digit
// http://0x80.pl/articles/simd-parsing-int-sequences.html // http://0x80.pl/articles/simd-parsing-int-sequences.html
__m128i const t0 = _mm_add_epi16(data, _mm_set1_epi16(32720)); __m128i const t0 = _mm_add_epi16(data, _mm_set1_epi16(32720));
__m128i const t1 = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759)); __m128i const mask = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759));
if (_mm_movemask_epi8(t1) == 0) { // If mask == 0 → all digits valid.
if (_mm_movemask_epi8(mask) == 0) {
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data)); i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
return true; return true;
} else }
return false;
FASTFLOAT_SIMD_RESTORE_WARNINGS FASTFLOAT_SIMD_RESTORE_WARNINGS
#elif defined(FASTFLOAT_NEON) #elif defined(FASTFLOAT_NEON)
FASTFLOAT_SIMD_DISABLE_WARNINGS FASTFLOAT_SIMD_DISABLE_WARNINGS
@ -183,17 +192,16 @@ simd_parse_if_eight_digits_unrolled(char16_t const *chars,
if (vminvq_u16(mask) == 0xFFFF) { if (vminvq_u16(mask) == 0xFFFF) {
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data)); i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
return true; return true;
} else }
return false;
FASTFLOAT_SIMD_RESTORE_WARNINGS FASTFLOAT_SIMD_RESTORE_WARNINGS
#else #else
(void)chars; (void)chars;
(void)i; (void)i;
#endif
return false; return false;
#endif // FASTFLOAT_SSE2
} }
#endif // FASTFLOAT_HAS_SIMD #endif
// MSVC SFINAE is broken pre-VS2017 // MSVC SFINAE is broken pre-VS2017
#if defined(_MSC_VER) && _MSC_VER <= 1900 #if defined(_MSC_VER) && _MSC_VER <= 1900
@ -232,12 +240,17 @@ loop_parse_if_eight_digits(char const *&p, char const *const pend,
} }
} }
enum class parse_error { enum class parse_error : uint_fast8_t {
no_error, no_error,
// [JSON-only] The minus sign must be followed by an integer.
missing_integer_after_sign,
// A sign must be followed by an integer or dot. // A sign must be followed by an integer or dot.
missing_integer_or_dot_after_sign, missing_integer_or_dot_after_sign,
// The mantissa must have at least one digit.
no_digits_in_mantissa,
// Scientific notation requires an exponential part.
missing_exponential_part,
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
// [JSON-only] The minus sign must be followed by an integer.
missing_integer_after_sign,
// [JSON-only] The integer part must not have leading zeros. // [JSON-only] The integer part must not have leading zeros.
leading_zeros_in_integer_part, leading_zeros_in_integer_part,
// [JSON-only] The integer part must have at least one digit. // [JSON-only] The integer part must have at least one digit.
@ -245,18 +258,18 @@ enum class parse_error {
// [JSON-only] If there is a decimal point, there must be digits in the // [JSON-only] If there is a decimal point, there must be digits in the
// fractional part. // fractional part.
no_digits_in_fractional_part, no_digits_in_fractional_part,
// The mantissa must have at least one digit. #endif
no_digits_in_mantissa,
// Scientific notation requires an exponential part.
missing_exponential_part,
}; };
template <typename UC> struct parsed_number_string_t { template <typename UC> struct parsed_number_string_t {
int64_t exponent{0}; // an unsigned int avoids signed overflows (which are bad)
uint64_t mantissa{0}; am_mant_t mantissa{0};
am_pow_t exponent{0};
UC const *lastmatch{nullptr}; UC const *lastmatch{nullptr};
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
bool negative{false}; bool negative{false};
bool valid{false}; #endif
bool invalid{false};
bool too_many_digits{false}; bool too_many_digits{false};
// contains the range of the significant digits // contains the range of the significant digits
span<UC const> integer{}; // non-nullable span<UC const> integer{}; // non-nullable
@ -269,9 +282,9 @@ using parsed_number_string = parsed_number_string_t<char>;
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC> fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
report_parse_error(UC const *p, parse_error error) { report_parse_error(UC const *p, parse_error error) noexcept {
parsed_number_string_t<UC> answer; parsed_number_string_t<UC> answer;
answer.valid = false; answer.invalid = true;
answer.lastmatch = p; answer.lastmatch = p;
answer.error = error; answer.error = error;
return answer; return answer;
@ -282,53 +295,60 @@ report_parse_error(UC const *p, parse_error error) {
template <bool basic_json_fmt, typename UC> template <bool basic_json_fmt, typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC> fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
parse_number_string(UC const *p, UC const *pend, parse_number_string(UC const *p, UC const *pend,
parse_options_t<UC> options) noexcept { parse_options_t<UC> const &options) noexcept {
chars_format const fmt = detail::adjust_for_feature_macros(options.format); // Cyclomatic complexity https://en.wikipedia.org/wiki/Cyclomatic_complexity
UC const decimal_point = options.decimal_point; // Consider refactoring the 'parse_number_string' function.
// FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN fix this.
parsed_number_string_t<UC> answer; parsed_number_string_t<UC> answer;
answer.valid = false; // so dereference without checks
answer.too_many_digits = false; FASTFLOAT_ASSUME(p < pend);
// assume p < pend, so dereference without checks; #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
answer.negative = (*p == UC('-')); answer.negative = (*p == UC('-'));
if (answer.negative ||
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
if ((*p == UC('-')) || (uint64_t(fmt & chars_format::allow_leading_plus) && ((chars_format_t(options.format & chars_format::allow_leading_plus)) &&
!basic_json_fmt && *p == UC('+'))) { (!basic_json_fmt && *p == UC('+')))) {
++p; ++p;
if (p == pend) { if (p == pend) {
return report_parse_error<UC>( return report_parse_error<UC>(
p, parse_error::missing_integer_or_dot_after_sign); p, parse_error::missing_integer_or_dot_after_sign);
} }
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) { FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
if (!is_integer(*p)) { // a sign must be followed by an integer // a sign must be followed by an integer
if (!is_integer(*p)) {
return report_parse_error<UC>(p, return report_parse_error<UC>(p,
parse_error::missing_integer_after_sign); parse_error::missing_integer_after_sign);
} }
} }
else { else {
if (!is_integer(*p) && // a sign must be followed by an integer or the dot
(*p != if (!is_integer(*p) && (*p != options.decimal_point)) {
decimal_point)) { // a sign must be followed by an integer or the dot
return report_parse_error<UC>( return report_parse_error<UC>(
p, parse_error::missing_integer_or_dot_after_sign); p, parse_error::missing_integer_or_dot_after_sign);
} }
} }
} }
UC const *const start_digits = p; #endif
uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad) UC const *const start_digits = p;
while ((p != pend) && is_integer(*p)) { while ((p != pend) && is_integer(*p)) {
// a multiplication by 10 is cheaper than an arbitrary integer // a multiplication by 10 is cheaper than an arbitrary integer
// multiplication // multiplication
i = 10 * i + answer.mantissa = static_cast<fast_float::am_mant_t>(
uint64_t(*p - answer.mantissa * 10 +
UC('0')); // might overflow, we will handle the overflow later static_cast<fast_float::am_mant_t>(
*p - UC('0'))); // might overflow, we will handle the overflow later
++p; ++p;
} }
UC const *const end_of_integer_part = p; UC const *const end_of_integer_part = p;
int64_t digit_count = int64_t(end_of_integer_part - start_digits); am_digits digit_count =
answer.integer = span<UC const>(start_digits, size_t(digit_count)); static_cast<am_digits>(end_of_integer_part - start_digits);
answer.integer = span<UC const>(start_digits, digit_count);
// We have now parsed the integer part of the mantissa.
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) { FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
// at least 1 digit in integer part, without leading zeros // at least 1 digit in integer part, without leading zeros
if (digit_count == 0) { if (digit_count == 0) {
@ -339,148 +359,180 @@ parse_number_string(UC const *p, UC const *pend,
parse_error::leading_zeros_in_integer_part); parse_error::leading_zeros_in_integer_part);
} }
} }
#endif
int64_t exponent = 0; // We can now parse the fraction part of the mantissa.
bool const has_decimal_point = (p != pend) && (*p == decimal_point); if ((p != pend) && (*p == options.decimal_point)) {
if (has_decimal_point) {
++p; ++p;
UC const *before = p; UC const *const before = p;
// can occur at most twice without overflowing, but let it occur more, since // can occur at most twice without overflowing, but let it occur more, since
// for integers with many digits, digit parsing is the primary bottleneck. // for integers with many digits, digit parsing is the primary bottleneck.
loop_parse_if_eight_digits(p, pend, i); loop_parse_if_eight_digits(p, pend, answer.mantissa);
while ((p != pend) && is_integer(*p)) { while ((p != pend) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - UC('0')); UC const digit = UC(*p - UC('0'));
answer.mantissa = static_cast<fast_float::am_mant_t>(
answer.mantissa * 10 +
static_cast<am_mant_t>(
digit)); // in rare cases, this will overflow, but that's ok
++p; ++p;
i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
}
exponent = before - p;
answer.fraction = span<UC const>(before, size_t(p - before));
digit_count -= exponent;
} }
answer.exponent = static_cast<am_pow_t>(before - p);
answer.fraction =
span<UC const>(before, static_cast<am_digits>(p - before));
digit_count -= static_cast<am_digits>(answer.exponent);
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) { FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
// at least 1 digit in fractional part // at least 1 digit in fractional part
if (has_decimal_point && exponent == 0) { if (answer.exponent == 0) {
return report_parse_error<UC>(p, return report_parse_error<UC>(
parse_error::no_digits_in_fractional_part); p, parse_error::no_digits_in_fractional_part);
} }
} }
else if (digit_count == 0) { // we must have encountered at least one integer! #endif
} else if (digit_count == 0) {
// We must have encountered at least one integer!
return report_parse_error<UC>(p, parse_error::no_digits_in_mantissa); return report_parse_error<UC>(p, parse_error::no_digits_in_mantissa);
} }
int64_t exp_number = 0; // explicit exponential part // We have now parsed the integer and the fraction part of the mantissa.
if ((uint64_t(fmt & chars_format::scientific) && (p != pend) &&
((UC('e') == *p) || (UC('E') == *p))) || // Now we can parse the explicit exponential part.
(uint64_t(fmt & detail::basic_fortran_fmt) && (p != pend) && am_pow_t exp_number = 0; // explicit exponential part
if ((p != pend) &&
((chars_format_t(options.format & chars_format::scientific) &&
(UC('e') == *p || UC('E') == *p))
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|| (chars_format_t(options.format & detail::basic_fortran_fmt) &&
((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) || ((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) ||
(UC('D') == *p)))) { (UC('D') == *p)))
#endif
)) {
UC const *location_of_e = p; UC const *location_of_e = p;
#ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
++p;
#else
if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) || if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) ||
(UC('D') == *p)) { (UC('D') == *p)) {
++p; ++p;
} }
#endif
bool neg_exp = false; bool neg_exp = false;
if ((p != pend) && (UC('-') == *p)) { if (p != pend) {
if (UC('-') == *p) {
neg_exp = true; neg_exp = true;
++p; ++p;
} else if ((p != pend) && } else if (UC('+') == *p) {
(UC('+') == // '+' on exponent is allowed by C++17 20.19.3.(7.1)
*p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
++p; ++p;
} }
}
// We have now parsed the sign of the exponent.
if ((p == pend) || !is_integer(*p)) { if ((p == pend) || !is_integer(*p)) {
if (!uint64_t(fmt & chars_format::fixed)) { if (!(chars_format_t(options.format & chars_format::fixed))) {
// The exponential part is invalid for scientific notation, so it must // The exponential part is invalid for scientific notation, so it
// be a trailing token for fixed notation. However, fixed notation is // must be a trailing token for fixed notation. However, fixed
// disabled, so report a scientific notation error. // notation is disabled, so report a scientific notation error.
return report_parse_error<UC>(p, parse_error::missing_exponential_part); return report_parse_error<UC>(p, parse_error::missing_exponential_part);
} }
// Otherwise, we will be ignoring the 'e'. // Otherwise, we will be ignoring the 'e'.
p = location_of_e; p = location_of_e;
} else { } else {
// Now let's parse the explicit exponent.
while ((p != pend) && is_integer(*p)) { while ((p != pend) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - UC('0')); if (exp_number < 0x10000) {
if (exp_number < 0x10000000) { // check for exponent overflow if we have too many digits.
exp_number = 10 * exp_number + digit; UC const digit = UC(*p - UC('0'));
exp_number = 10 * exp_number + static_cast<am_pow_t>(digit);
} }
++p; ++p;
} }
if (neg_exp) { if (neg_exp) {
exp_number = -exp_number; exp_number = -exp_number;
} }
exponent += exp_number; answer.exponent += exp_number;
} }
} else { } else {
// If it scientific and not fixed, we have to bail out. // If it scientific and not fixed, we have to bail out.
if (uint64_t(fmt & chars_format::scientific) && if ((chars_format_t(options.format & chars_format::scientific)) &&
!uint64_t(fmt & chars_format::fixed)) { !(chars_format_t(options.format & chars_format::fixed))) {
return report_parse_error<UC>(p, parse_error::missing_exponential_part); return report_parse_error<UC>(p, parse_error::missing_exponential_part);
} }
} }
answer.lastmatch = p;
answer.valid = true;
// If we frequently had to deal with long strings of digits, // We parsed all parts of the number, let's save progress.
answer.lastmatch = p;
// Now we can check for errors.
// TODO: If we frequently had to deal with long strings of digits,
// we could extend our code by using a 128-bit integer instead // we could extend our code by using a 128-bit integer instead
// of a 64-bit integer. However, this is uncommon. // of a 64-bit integer. However, this is uncommon.
//
// We can deal with up to 19 digits. // We can deal with up to 19 digits.
if (digit_count > 19) { // this is uncommon if (digit_count > 19) {
// It is possible that the integer had an overflow. // It is possible that the integer had an overflow.
// We have to handle the case where we have 0.0000somenumber. // We have to handle the case where we have 0.0000somenumber.
// We need to be mindful of the case where we only have zeroes... // We need to be mindful of the case where we only have zeroes...
// E.g., 0.000000000...000. // E.g., 0.000000000...000.
UC const *start = start_digits; UC const *start = start_digits;
while ((start != pend) && (*start == UC('0') || *start == decimal_point)) { while ((start != pend) &&
(*start == UC('0') || *start == options.decimal_point)) {
if (*start == UC('0')) { if (*start == UC('0')) {
digit_count--; --digit_count;
} }
start++; ++start;
} }
// We have to check if we have a number with more than 19 significant
// digits.
if (digit_count > 19) { if (digit_count > 19) {
answer.too_many_digits = true; answer.too_many_digits = true;
// Let us start again, this time, avoiding overflows. // Let us start again, this time, avoiding overflows.
// We don't need to call 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. // pre-tokenized spans from above.
i = 0; answer.mantissa = 0;
p = answer.integer.ptr; p = answer.integer.ptr;
UC const *int_end = p + answer.integer.len(); UC const *int_end = p + answer.integer.len();
uint64_t const minimal_nineteen_digit_integer{1000000000000000000}; constexpr am_mant_t minimal_nineteen_digit_integer{1000000000000000000};
while ((i < minimal_nineteen_digit_integer) && (p != int_end)) { while ((answer.mantissa < minimal_nineteen_digit_integer) &&
i = i * 10 + uint64_t(*p - UC('0')); (p != int_end)) {
answer.mantissa = static_cast<am_mant_t>(
answer.mantissa * 10 + static_cast<am_mant_t>(*p - UC('0')));
++p; ++p;
} }
if (i >= minimal_nineteen_digit_integer) { // We have a big integer if (answer.mantissa >= minimal_nineteen_digit_integer) {
exponent = end_of_integer_part - p + exp_number; // We have a big integers, so skip the fraction part completely.
} else { // We have a value with a fractional component. answer.exponent = am_pow_t(end_of_integer_part - p) + exp_number;
} else {
// We have a value with a significant fractional component.
p = answer.fraction.ptr; p = answer.fraction.ptr;
UC const *frac_end = p + answer.fraction.len(); UC const *const frac_end = p + answer.fraction.len();
while ((i < minimal_nineteen_digit_integer) && (p != frac_end)) { while ((answer.mantissa < minimal_nineteen_digit_integer) &&
i = i * 10 + uint64_t(*p - UC('0')); (p != frac_end)) {
answer.mantissa = static_cast<am_mant_t>(
answer.mantissa * 10 + static_cast<am_mant_t>(*p - UC('0')));
++p; ++p;
} }
exponent = answer.fraction.ptr - p + exp_number; answer.exponent = am_pow_t(answer.fraction.ptr - p) + exp_number;
}
// We have now corrected both exponent and i, to a truncated value
} }
} }
answer.exponent = exponent; // We have now corrected both exponent and mantissa, to a truncated value
answer.mantissa = i; }
return answer; return answer;
} }
template <typename T, typename UC> template <typename T, typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
parse_int_string(UC const *p, UC const *pend, T &value, parse_int_string(UC const *p, UC const *pend, T &value,
parse_options_t<UC> options) { parse_options_t<UC> const &options) noexcept {
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
int const base = options.base;
from_chars_result_t<UC> answer; from_chars_result_t<UC> answer;
UC const *const first = p; UC const *const first = p;
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
// Read sign
bool const negative = (*p == UC('-')); bool const negative = (*p == UC('-'));
#ifdef FASTFLOAT_VISUAL_STUDIO #ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(push) #pragma warning(push)
@ -495,12 +547,15 @@ parse_int_string(UC const *p, UC const *pend, T &value,
return answer; return answer;
} }
if ((*p == UC('-')) || if ((*p == UC('-')) ||
(uint64_t(fmt & chars_format::allow_leading_plus) && (*p == UC('+')))) { ((chars_format_t(options.format & chars_format::allow_leading_plus)) &&
(*p == UC('+')))) {
++p; ++p;
} }
#endif
UC const *const start_num = p; UC const *const start_num = p;
// Skip leading zeros
while (p != pend && *p == UC('0')) { while (p != pend && *p == UC('0')) {
++p; ++p;
} }
@ -509,20 +564,21 @@ parse_int_string(UC const *p, UC const *pend, T &value,
UC const *const start_digits = p; UC const *const start_digits = p;
// Parse digits
uint64_t i = 0; uint64_t i = 0;
if (base == 10) { if (options.base == 10) {
loop_parse_if_eight_digits(p, pend, i); // use SIMD if possible loop_parse_if_eight_digits(p, pend, i); // use SIMD if possible
} }
while (p != pend) { while (p != pend) {
uint8_t digit = ch_to_digit(*p); uint_fast8_t const digit = ch_to_digit(*p);
if (digit >= base) { if (digit >= options.base) {
break; break;
} }
i = uint64_t(base) * i + digit; // might overflow, check this later i = uint64_t(options.base) * i + digit; // might overflow, check this later
p++; p++;
} }
size_t digit_count = size_t(p - start_digits); am_digits const digit_count = static_cast<am_digits>(p - start_digits);
if (digit_count == 0) { if (digit_count == 0) {
if (has_leading_zeros) { if (has_leading_zeros) {
@ -539,26 +595,33 @@ parse_int_string(UC const *p, UC const *pend, T &value,
answer.ptr = p; answer.ptr = p;
// check u64 overflow // check u64 overflow
size_t max_digits = max_digits_u64(base); uint_fast8_t const max_digits = max_digits_u64(options.base);
if (digit_count > max_digits) { if (digit_count > max_digits) {
answer.ec = std::errc::result_out_of_range; answer.ec = std::errc::result_out_of_range;
return answer; return answer;
} }
// this check can be eliminated for all other types, but they will all require // this check can be eliminated for all other types, but they will all require
// a max_digits(base) equivalent // a max_digits(base) equivalent
if (digit_count == max_digits && i < min_safe_u64(base)) { if (digit_count == max_digits && i < min_safe_u64(options.base)) {
answer.ec = std::errc::result_out_of_range; answer.ec = std::errc::result_out_of_range;
return answer; return answer;
} }
// check other types overflow // check other types overflow
if (!std::is_same<T, uint64_t>::value) { if (!std::is_same<T, uint64_t>::value) {
if (i > uint64_t(std::numeric_limits<T>::max()) + uint64_t(negative)) { if (i > uint64_t(std::numeric_limits<T>::max())
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
+ uint64_t(negative)
#endif
) {
answer.ec = std::errc::result_out_of_range; answer.ec = std::errc::result_out_of_range;
return answer; return answer;
} }
} }
#ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
value = T(i);
#else
if (negative) { if (negative) {
#ifdef FASTFLOAT_VISUAL_STUDIO #ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(push) #pragma warning(push)
@ -578,6 +641,7 @@ parse_int_string(UC const *p, UC const *pend, T &value,
} else { } else {
value = T(i); value = T(i);
} }
#endif
answer.ec = std::errc(); answer.ec = std::errc();
return answer; return answer;

View File

@ -19,11 +19,11 @@ namespace fast_float {
#if defined(FASTFLOAT_64BIT) && !defined(__sparc) #if defined(FASTFLOAT_64BIT) && !defined(__sparc)
#define FASTFLOAT_64BIT_LIMB 1 #define FASTFLOAT_64BIT_LIMB 1
typedef uint64_t limb; typedef uint64_t limb;
constexpr size_t limb_bits = 64; constexpr limb_t limb_bits = 64;
#else #else
#define FASTFLOAT_32BIT_LIMB #define FASTFLOAT_32BIT_LIMB
typedef uint32_t limb; typedef uint32_t limb;
constexpr size_t limb_bits = 32; constexpr limb_t limb_bits = 32;
#endif #endif
typedef span<limb> limb_span; typedef span<limb> limb_span;
@ -32,59 +32,58 @@ typedef span<limb> limb_span;
// of bits required to store the largest bigint, which is // of bits required to store the largest bigint, which is
// `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or // `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or
// ~3600 bits, so we round to 4000. // ~3600 bits, so we round to 4000.
constexpr size_t bigint_bits = 4000; typedef uint_fast16_t bigint_bits_t;
constexpr size_t bigint_limbs = bigint_bits / limb_bits; constexpr bigint_bits_t bigint_bits = 4000;
constexpr limb_t bigint_limbs = bigint_bits / limb_bits;
// vector-like type that is allocated on the stack. the entire // vector-like type that is allocated on the stack. the entire
// buffer is pre-allocated, and only the length changes. // buffer is pre-allocated, and only the length changes.
template <uint16_t size> struct stackvec { template <limb_t size> struct stackvec {
limb data[size]; limb data[size];
// we never need more than 150 limbs // we never need more than 150 limbs
uint16_t length{0}; uint_fast8_t length{0};
stackvec() = default; FASTFLOAT_CONSTEXPR20 stackvec() noexcept = default;
stackvec(stackvec const &) = delete; stackvec(stackvec const &) = delete;
stackvec &operator=(stackvec const &) = delete; stackvec &operator=(stackvec const &) = delete;
stackvec(stackvec &&) = delete; stackvec(stackvec &&) = delete;
stackvec &operator=(stackvec &&other) = delete; stackvec &operator=(stackvec &&other) = delete;
// create stack vector from existing limb span. // create stack vector from existing limb span.
FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) { FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) noexcept {
FASTFLOAT_ASSERT(try_extend(s)); FASTFLOAT_ASSERT(try_extend(s));
} }
FASTFLOAT_CONSTEXPR14 limb &operator[](size_t index) noexcept { FASTFLOAT_CONSTEXPR14 limb &operator[](limb_t index) noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index]; return data[index];
} }
FASTFLOAT_CONSTEXPR14 const limb &operator[](size_t index) const noexcept { FASTFLOAT_CONSTEXPR14 const limb &operator[](limb_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index]; return data[index];
} }
// index from the end of the container // index from the end of the container
FASTFLOAT_CONSTEXPR14 const limb &rindex(size_t index) const noexcept { FASTFLOAT_CONSTEXPR14 const limb &rindex(limb_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
size_t rindex = length - index - 1; limb_t rindex = static_cast<limb_t>(length - index - 1);
return data[rindex]; return data[rindex];
} }
// set the length, without bounds checking. // set the length, without bounds checking.
FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept { FASTFLOAT_CONSTEXPR14 void set_len(limb_t len) noexcept { length = len; }
length = uint16_t(len);
}
constexpr size_t len() const noexcept { return length; } constexpr limb_t len() const noexcept { return length; }
constexpr bool is_empty() const noexcept { return length == 0; } constexpr bool is_empty() const noexcept { return length == 0; }
constexpr size_t capacity() const noexcept { return size; } constexpr limb_t capacity() const noexcept { return size; }
// append item to vector, without bounds checking // append item to vector, without bounds checking
FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept { FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept {
data[length] = value; data[length] = value;
length++; ++length;
} }
// append item to vector, returning if item was added // append item to vector, returning if item was added
@ -101,7 +100,7 @@ template <uint16_t size> struct stackvec {
FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept { FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept {
limb *ptr = data + length; limb *ptr = data + length;
std::copy_n(s.ptr, s.len(), ptr); std::copy_n(s.ptr, s.len(), ptr);
set_len(len() + s.len()); set_len(limb_t(len() + s.len()));
} }
// try to add items to the vector, returning if items were added // try to add items to the vector, returning if items were added
@ -118,37 +117,34 @@ template <uint16_t size> struct stackvec {
// if the new size is longer than the vector, assign value to each // if the new size is longer than the vector, assign value to each
// appended item. // appended item.
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20
void resize_unchecked(size_t new_len, limb value) noexcept { void resize_unchecked(limb_t new_len, limb value) noexcept {
if (new_len > len()) { if (new_len > len()) {
size_t count = new_len - len(); limb_t count = new_len - len();
limb *first = data + len(); limb *first = data + len();
limb *last = first + count; limb *last = first + count;
::std::fill(first, last, value); ::std::fill(first, last, value);
set_len(new_len);
} else {
set_len(new_len);
} }
set_len(new_len);
} }
// try to resize the vector, returning if the vector was resized. // try to resize the vector, returning if the vector was resized.
FASTFLOAT_CONSTEXPR20 bool try_resize(size_t new_len, limb value) noexcept { FASTFLOAT_CONSTEXPR20 bool try_resize(limb_t new_len, limb value) noexcept {
if (new_len > capacity()) { if (new_len > capacity()) {
return false; return false;
} else { }
resize_unchecked(new_len, value); resize_unchecked(new_len, value);
return true; return true;
} }
}
// check if any limbs are non-zero after the given index. // check if any limbs are non-zero after the given index.
// this needs to be done in reverse order, since the index // this needs to be done in reverse order, since the index
// is relative to the most significant limbs. // is relative to the most significant limbs.
FASTFLOAT_CONSTEXPR14 bool nonzero(size_t index) const noexcept { FASTFLOAT_CONSTEXPR14 bool nonzero(limb_t index) const noexcept {
while (index < len()) { while (index < len()) {
if (rindex(index) != 0) { if (rindex(index) != 0) {
return true; return true;
} }
index++; ++index;
} }
return false; return false;
} }
@ -156,7 +152,7 @@ template <uint16_t size> struct stackvec {
// normalize the big integer, so most-significant zero limbs are removed. // normalize the big integer, so most-significant zero limbs are removed.
FASTFLOAT_CONSTEXPR14 void normalize() noexcept { FASTFLOAT_CONSTEXPR14 void normalize() noexcept {
while (len() > 0 && rindex(0) == 0) { while (len() > 0 && rindex(0) == 0) {
length--; --length;
} }
} }
}; };
@ -258,16 +254,15 @@ scalar_mul(limb x, limb y, limb &carry) noexcept {
// add scalar value to bigint starting from offset. // add scalar value to bigint starting from offset.
// used in grade school multiplication // used in grade school multiplication
template <uint16_t size> template <limb_t size>
inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec<size> &vec, limb y, inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec<size> &vec, limb y,
size_t start) noexcept { limb_t start) noexcept {
size_t index = start;
limb carry = y; limb carry = y;
bool overflow; bool overflow;
while (carry != 0 && index < vec.len()) { while (carry != 0 && start < vec.len()) {
vec[index] = scalar_add(vec[index], carry, overflow); vec[start] = scalar_add(vec[start], carry, overflow);
carry = limb(overflow); carry = limb(overflow);
index += 1; ++start;
} }
if (carry != 0) { if (carry != 0) {
FASTFLOAT_TRY(vec.try_push(carry)); FASTFLOAT_TRY(vec.try_push(carry));
@ -276,18 +271,18 @@ inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec<size> &vec, limb y,
} }
// add scalar value to bigint. // add scalar value to bigint.
template <uint16_t size> template <limb_t size>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
small_add(stackvec<size> &vec, limb y) noexcept { small_add(stackvec<size> &vec, limb y) noexcept {
return small_add_from(vec, y, 0); return small_add_from(vec, y, 0);
} }
// multiply bigint by scalar value. // multiply bigint by scalar value.
template <uint16_t size> template <limb_t size>
inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec<size> &vec, inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec<size> &vec,
limb y) noexcept { limb y) noexcept {
limb carry = 0; limb carry = 0;
for (size_t index = 0; index < vec.len(); index++) { for (limb_t index = 0; index != vec.len(); ++index) {
vec[index] = scalar_mul(vec[index], y, carry); vec[index] = scalar_mul(vec[index], y, carry);
} }
if (carry != 0) { if (carry != 0) {
@ -298,17 +293,17 @@ inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec<size> &vec,
// add bigint to bigint starting from index. // add bigint to bigint starting from index.
// used in grade school multiplication // used in grade school multiplication
template <uint16_t size> template <limb_t size>
FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec<size> &x, limb_span y, FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec<size> &x, limb_span y,
size_t start) noexcept { limb_t start) noexcept {
// the effective x buffer is from `xstart..x.len()`, so exit early // the effective x buffer is from `xstart..x.len()`, so exit early
// if we can't get that current range. // if we can't get that current range.
if (x.len() < start || y.len() > x.len() - start) { if (x.len() < start || y.len() > limb_t(x.len() - start)) {
FASTFLOAT_TRY(x.try_resize(y.len() + start, 0)); FASTFLOAT_TRY(x.try_resize(limb_t(y.len() + start), 0));
} }
bool carry = false; bool carry = false;
for (size_t index = 0; index < y.len(); index++) { for (limb_t index = 0; index < y.len(); ++index) {
limb xi = x[index + start]; limb xi = x[index + start];
limb yi = y[index]; limb yi = y[index];
bool c1 = false; bool c1 = false;
@ -323,20 +318,20 @@ FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec<size> &x, limb_span y,
// handle overflow // handle overflow
if (carry) { if (carry) {
FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start)); FASTFLOAT_TRY(small_add_from(x, 1, limb_t(y.len() + start)));
} }
return true; return true;
} }
// add bigint to bigint. // add bigint to bigint.
template <uint16_t size> template <limb_t size>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
large_add_from(stackvec<size> &x, limb_span y) noexcept { large_add_from(stackvec<size> &x, limb_span y) noexcept {
return large_add_from(x, y, 0); return large_add_from(x, y, 0);
} }
// grade-school multiplication algorithm // grade-school multiplication algorithm
template <uint16_t size> template <limb_t size>
FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept { FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept {
limb_span xs = limb_span(x.data, x.len()); limb_span xs = limb_span(x.data, x.len());
stackvec<size> z(xs); stackvec<size> z(xs);
@ -345,7 +340,7 @@ FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept {
if (y.len() != 0) { if (y.len() != 0) {
limb y0 = y[0]; limb y0 = y[0];
FASTFLOAT_TRY(small_mul(x, y0)); FASTFLOAT_TRY(small_mul(x, y0));
for (size_t index = 1; index < y.len(); index++) { for (limb_t index = 1; index != y.len(); ++index) {
limb yi = y[index]; limb yi = y[index];
stackvec<size> zi; stackvec<size> zi;
if (yi != 0) { if (yi != 0) {
@ -364,7 +359,7 @@ FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept {
} }
// grade-school multiplication algorithm // grade-school multiplication algorithm
template <uint16_t size> template <limb_t size>
FASTFLOAT_CONSTEXPR20 bool large_mul(stackvec<size> &x, limb_span y) noexcept { FASTFLOAT_CONSTEXPR20 bool large_mul(stackvec<size> &x, limb_span y) noexcept {
if (y.len() == 1) { if (y.len() == 1) {
FASTFLOAT_TRY(small_mul(x, y[0])); FASTFLOAT_TRY(small_mul(x, y[0]));
@ -375,7 +370,7 @@ FASTFLOAT_CONSTEXPR20 bool large_mul(stackvec<size> &x, limb_span y) noexcept {
} }
template <typename = void> struct pow5_tables { template <typename = void> struct pow5_tables {
static constexpr uint32_t large_step = 135; static constexpr uint8_t large_step = 135;
static constexpr uint64_t small_power_of_5[] = { static constexpr uint64_t small_power_of_5[] = {
1UL, 1UL,
5UL, 5UL,
@ -419,7 +414,7 @@ template <typename = void> struct pow5_tables {
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
template <typename T> constexpr uint32_t pow5_tables<T>::large_step; template <typename T> constexpr uint8_t pow5_tables<T>::large_step;
template <typename T> constexpr uint64_t pow5_tables<T>::small_power_of_5[]; template <typename T> constexpr uint64_t pow5_tables<T>::small_power_of_5[];
@ -435,14 +430,14 @@ struct bigint : pow5_tables<> {
// storage of the limbs, in little-endian order. // storage of the limbs, in little-endian order.
stackvec<bigint_limbs> vec; stackvec<bigint_limbs> vec;
FASTFLOAT_CONSTEXPR20 bigint() : vec() {} FASTFLOAT_CONSTEXPR20 bigint() noexcept : vec() {}
bigint(bigint const &) = delete; bigint(bigint const &) = delete;
bigint &operator=(bigint const &) = delete; bigint &operator=(bigint const &) = delete;
bigint(bigint &&) = delete; bigint(bigint &&) = delete;
bigint &operator=(bigint &&other) = delete; bigint &operator=(bigint &&other) = delete;
FASTFLOAT_CONSTEXPR20 bigint(uint64_t value) : vec() { FASTFLOAT_CONSTEXPR20 bigint(uint64_t value) noexcept : vec() {
#ifdef FASTFLOAT_64BIT_LIMB #ifdef FASTFLOAT_64BIT_LIMB
vec.push_unchecked(value); vec.push_unchecked(value);
#else #else
@ -493,7 +488,7 @@ struct bigint : pow5_tables<> {
} else if (vec.len() < other.vec.len()) { } else if (vec.len() < other.vec.len()) {
return -1; return -1;
} else { } else {
for (size_t index = vec.len(); index > 0; index--) { for (limb_t index = vec.len(); index != 0; --index) {
limb xi = vec[index - 1]; limb xi = vec[index - 1];
limb yi = other.vec[index - 1]; limb yi = other.vec[index - 1];
if (xi > yi) { if (xi > yi) {
@ -508,7 +503,7 @@ struct bigint : pow5_tables<> {
// shift left each limb n bits, carrying over to the new limb // shift left each limb n bits, carrying over to the new limb
// returns true if we were able to shift all the digits. // returns true if we were able to shift all the digits.
FASTFLOAT_CONSTEXPR20 bool shl_bits(size_t n) noexcept { FASTFLOAT_CONSTEXPR20 bool shl_bits(bigint_bits_t n) noexcept {
// Internally, for each item, we shift left by n, and add the previous // Internally, for each item, we shift left by n, and add the previous
// right shifted limb-bits. // right shifted limb-bits.
// For example, we transform (for u8) shifted left 2, to: // For example, we transform (for u8) shifted left 2, to:
@ -517,10 +512,10 @@ struct bigint : pow5_tables<> {
FASTFLOAT_DEBUG_ASSERT(n != 0); FASTFLOAT_DEBUG_ASSERT(n != 0);
FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8); FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8);
size_t shl = n; bigint_bits_t const shl = n;
size_t shr = limb_bits - shl; bigint_bits_t const shr = limb_bits - shl;
limb prev = 0; limb prev = 0;
for (size_t index = 0; index < vec.len(); index++) { for (limb_t index = 0; index != vec.len(); ++index) {
limb xi = vec[index]; limb xi = vec[index];
vec[index] = (xi << shl) | (prev >> shr); vec[index] = (xi << shl) | (prev >> shr);
prev = xi; prev = xi;
@ -534,11 +529,16 @@ struct bigint : pow5_tables<> {
} }
// move the limbs left by `n` limbs. // move the limbs left by `n` limbs.
FASTFLOAT_CONSTEXPR20 bool shl_limbs(size_t n) noexcept { FASTFLOAT_CONSTEXPR20 bool shl_limbs(bigint_bits_t n) noexcept {
FASTFLOAT_DEBUG_ASSERT(n != 0); FASTFLOAT_DEBUG_ASSERT(n != 0);
if (n + vec.len() > vec.capacity()) { if (n + vec.len() > vec.capacity()) {
// we can't shift more than the capacity of the vector.
return false; return false;
} else if (!vec.is_empty()) { }
if (vec.is_empty()) {
// nothing to do
return true;
}
// move limbs // move limbs
limb *dst = vec.data + n; limb *dst = vec.data + n;
limb const *src = vec.data; limb const *src = vec.data;
@ -547,17 +547,14 @@ struct bigint : pow5_tables<> {
limb *first = vec.data; limb *first = vec.data;
limb *last = first + n; limb *last = first + n;
::std::fill(first, last, 0); ::std::fill(first, last, 0);
vec.set_len(n + vec.len()); vec.set_len(limb_t(n + vec.len()));
return true; return true;
} else {
return true;
}
} }
// move the limbs left by `n` bits. // move the limbs left by `n` bits.
FASTFLOAT_CONSTEXPR20 bool shl(size_t n) noexcept { FASTFLOAT_CONSTEXPR20 bool shl(bigint_bits_t n) noexcept {
size_t rem = n % limb_bits; bigint_bits_t const rem = n % limb_bits;
size_t div = n / limb_bits; bigint_bits_t const div = n / limb_bits;
if (rem != 0) { if (rem != 0) {
FASTFLOAT_TRY(shl_bits(rem)); FASTFLOAT_TRY(shl_bits(rem));
} }
@ -568,10 +565,11 @@ struct bigint : pow5_tables<> {
} }
// get the number of leading zeros in the bigint. // get the number of leading zeros in the bigint.
FASTFLOAT_CONSTEXPR20 int ctlz() const noexcept { FASTFLOAT_CONSTEXPR20 limb_t ctlz() const noexcept {
if (vec.is_empty()) { if (vec.is_empty()) {
// empty vector, no bits, no zeros.
return 0; return 0;
} else { }
#ifdef FASTFLOAT_64BIT_LIMB #ifdef FASTFLOAT_64BIT_LIMB
return leading_zeroes(vec.rindex(0)); return leading_zeroes(vec.rindex(0));
#else #else
@ -580,12 +578,11 @@ struct bigint : pow5_tables<> {
return leading_zeroes(r0 << 32); return leading_zeroes(r0 << 32);
#endif #endif
} }
}
// get the number of bits in the bigint. // get the number of bits in the bigint.
FASTFLOAT_CONSTEXPR20 int bit_length() const noexcept { FASTFLOAT_CONSTEXPR20 bigint_bits_t bit_length() const noexcept {
int lz = ctlz(); limb_t lz = ctlz();
return int(limb_bits * vec.len()) - lz; return static_cast<fast_float::bigint_bits_t>(limb_bits * vec.len() - lz);
} }
FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept { return small_mul(vec, y); } FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept { return small_mul(vec, y); }
@ -593,23 +590,25 @@ struct bigint : pow5_tables<> {
FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept { return small_add(vec, y); } FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept { return small_add(vec, y); }
// multiply as if by 2 raised to a power. // multiply as if by 2 raised to a power.
FASTFLOAT_CONSTEXPR20 bool pow2(uint32_t exp) noexcept { return shl(exp); } FASTFLOAT_CONSTEXPR20 bool pow2(am_pow_t exp) noexcept {
return shl(static_cast<fast_float::bigint_bits_t>(exp));
}
// multiply as if by 5 raised to a power. // multiply as if by 5 raised to a power.
FASTFLOAT_CONSTEXPR20 bool pow5(uint32_t exp) noexcept { FASTFLOAT_CONSTEXPR20 bool pow5(am_pow_t exp) noexcept {
// multiply by a power of 5 // multiply by a power of 5
size_t large_length = sizeof(large_power_of_5) / sizeof(limb); limb_t const large_length = sizeof(large_power_of_5) / sizeof(limb);
limb_span large = limb_span(large_power_of_5, large_length); limb_span const large = limb_span(large_power_of_5, large_length);
while (exp >= large_step) { while (exp >= large_step) {
FASTFLOAT_TRY(large_mul(vec, large)); FASTFLOAT_TRY(large_mul(vec, large));
exp -= large_step; exp -= large_step;
} }
#ifdef FASTFLOAT_64BIT_LIMB #ifdef FASTFLOAT_64BIT_LIMB
uint32_t small_step = 27; limb_t constexpr small_step = 27;
limb max_native = 7450580596923828125UL; limb constexpr max_native = 7450580596923828125UL;
#else #else
uint32_t small_step = 13; limb_t constexpr small_step = 13;
limb max_native = 1220703125U; limb constexpr max_native = 1220703125U;
#endif #endif
while (exp >= small_step) { while (exp >= small_step) {
FASTFLOAT_TRY(small_mul(vec, max_native)); FASTFLOAT_TRY(small_mul(vec, max_native));
@ -627,7 +626,7 @@ struct bigint : pow5_tables<> {
} }
// multiply as if by 10 raised to a power. // multiply as if by 10 raised to a power.
FASTFLOAT_CONSTEXPR20 bool pow10(uint32_t exp) noexcept { FASTFLOAT_CONSTEXPR20 bool pow10(am_pow_t exp) noexcept {
FASTFLOAT_TRY(pow5(exp)); FASTFLOAT_TRY(pow5(exp));
return pow2(exp); return pow2(exp);
} }

View File

@ -23,8 +23,16 @@
#if defined(__cpp_lib_is_constant_evaluated) && \ #if defined(__cpp_lib_is_constant_evaluated) && \
__cpp_lib_is_constant_evaluated >= 201811L __cpp_lib_is_constant_evaluated >= 201811L
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1 #define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1
#define FASTFLOAT_CONSTEVAL consteval
#else #else
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0 #define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0
#define FASTFLOAT_CONSTEVAL FASTFLOAT_CONSTEXPR14
#endif
#if defined(__cpp_lib_byteswap)
#define FASTFLOAT_HAS_BYTESWAP 1
#else
#define FASTFLOAT_HAS_BYTESWAP 0
#endif #endif
#if defined(__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L #if defined(__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L
@ -50,4 +58,11 @@
#define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 1 #define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 1
#endif #endif
// For support attribute [[assume]] is declared in P1774
#if defined(__cpp_attrubute_assume)
#define FASTFLOAT_ASSUME(expr) [[assume(expr)]]
#else
#define FASTFLOAT_ASSUME(expr)
#endif
#endif // FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H #endif // FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H

View File

@ -17,17 +17,17 @@ namespace fast_float {
// most significant bits and the low part corresponding to the least significant // most significant bits and the low part corresponding to the least significant
// bits. // bits.
// //
template <int bit_precision> template <limb_t bit_precision>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128
compute_product_approximation(int64_t q, uint64_t w) { compute_product_approximation(int64_t q, uint64_t w) noexcept {
int const index = 2 * int(q - powers::smallest_power_of_five); am_pow_t const index = 2 * am_pow_t(q - powers::smallest_power_of_five);
// For small values of q, e.g., q in [0,27], the answer is always exact // For small values of q, e.g., q in [0,27], the answer is always exact
// because The line value128 firstproduct = full_multiplication(w, // because The line value128 firstproduct = full_multiplication(w,
// power_of_five_128[index]); gives the exact answer. // power_of_five_128[index]); gives the exact answer.
value128 firstproduct = value128 firstproduct =
full_multiplication(w, powers::power_of_five_128[index]); full_multiplication(w, powers::power_of_five_128[index]);
static_assert((bit_precision >= 0) && (bit_precision <= 64), static_assert((bit_precision >= 0) && (bit_precision <= 64),
" precision should be in (0,64]"); " precision should be in [0,64]");
constexpr uint64_t precision_mask = constexpr uint64_t precision_mask =
(bit_precision < 64) ? (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision) (bit_precision < 64) ? (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision)
: uint64_t(0xFFFFFFFFFFFFFFFF); : uint64_t(0xFFFFFFFFFFFFFFFF);
@ -40,7 +40,7 @@ compute_product_approximation(int64_t q, uint64_t w) {
full_multiplication(w, powers::power_of_five_128[index + 1]); full_multiplication(w, powers::power_of_five_128[index + 1]);
firstproduct.low += secondproduct.high; firstproduct.low += secondproduct.high;
if (secondproduct.high > firstproduct.low) { if (secondproduct.high > firstproduct.low) {
firstproduct.high++; ++firstproduct.high;
} }
} }
return firstproduct; return firstproduct;
@ -62,7 +62,7 @@ namespace detail {
* where * where
* p = log(5**-q)/log(2) = -q * log(5)/log(2) * p = log(5**-q)/log(2) = -q * log(5)/log(2)
*/ */
constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept { constexpr fastfloat_really_inline am_pow_t power(am_pow_t q) noexcept {
return (((152170 + 65536) * q) >> 16) + 63; return (((152170 + 65536) * q) >> 16) + 63;
} }
} // namespace detail } // namespace detail
@ -71,12 +71,13 @@ constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept {
// for significant digits already multiplied by 10 ** q. // for significant digits already multiplied by 10 ** q.
template <typename binary> template <typename binary>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 adjusted_mantissa fastfloat_really_inline FASTFLOAT_CONSTEXPR14 adjusted_mantissa
compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept { compute_error_scaled(int64_t q, uint64_t w, limb_t lz) noexcept {
int hilz = int(w >> 63) ^ 1; limb_t const hilz = static_cast<limb_t>((w >> 63) ^ 1);
adjusted_mantissa answer; adjusted_mantissa answer;
answer.mantissa = w << hilz; answer.mantissa = w << hilz;
int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent(); constexpr am_pow_t bias =
answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 + binary::mantissa_explicit_bits() - binary::minimum_exponent();
answer.power2 = am_pow_t(detail::power(am_pow_t(q)) + bias - hilz - lz - 62 +
invalid_am_bias); invalid_am_bias);
return answer; return answer;
} }
@ -86,7 +87,7 @@ compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
template <typename binary> template <typename binary>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
compute_error(int64_t q, uint64_t w) noexcept { compute_error(int64_t q, uint64_t w) noexcept {
int lz = leading_zeroes(w); limb_t const lz = leading_zeroes(w);
w <<= lz; w <<= lz;
value128 product = value128 product =
compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w); compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
@ -118,7 +119,7 @@ compute_float(int64_t q, uint64_t w) noexcept {
// powers::largest_power_of_five]. // powers::largest_power_of_five].
// We want the most significant bit of i to be 1. Shift if needed. // We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(w); limb_t const lz = leading_zeroes(w);
w <<= lz; w <<= lz;
// The required precision is binary::mantissa_explicit_bits() + 3 because // The required precision is binary::mantissa_explicit_bits() + 3 because
@ -138,14 +139,15 @@ compute_float(int64_t q, uint64_t w) noexcept {
// branchless approach: value128 product = compute_product(q, w); but in // branchless approach: value128 product = compute_product(q, w); but in
// practice, we can win big with the compute_product_approximation if its // practice, we can win big with the compute_product_approximation if its
// additional branch is easily predicted. Which is best is data specific. // additional branch is easily predicted. Which is best is data specific.
int upperbit = int(product.high >> 63); limb_t const upperbit = limb_t(product.high >> 63);
int shift = upperbit + 64 - binary::mantissa_explicit_bits() - 3; limb_t const shift =
limb_t(upperbit + 64 - binary::mantissa_explicit_bits() - 3);
answer.mantissa = product.high >> shift; answer.mantissa = product.high >> shift;
answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz - answer.power2 = am_pow_t(detail::power(am_pow_t(q)) + upperbit - lz -
binary::minimum_exponent()); binary::minimum_exponent());
if (answer.power2 <= 0) { // we have a subnormal? if (answer.power2 <= 0) { // we have a subnormal or very small value.
// Here have that answer.power2 <= 0 so -answer.power2 >= 0 // Here have that answer.power2 <= 0 so -answer.power2 >= 0
if (-answer.power2 + 1 >= if (-answer.power2 + 1 >=
64) { // if we have more than 64 bits below the minimum exponent, you 64) { // if we have more than 64 bits below the minimum exponent, you
@ -155,6 +157,7 @@ compute_float(int64_t q, uint64_t w) noexcept {
// result should be zero // result should be zero
return answer; return answer;
} }
// We have a subnormal number. We need to shift the mantissa to the right
// next line is safe because -answer.power2 + 1 < 64 // next line is safe because -answer.power2 + 1 < 64
answer.mantissa >>= -answer.power2 + 1; answer.mantissa >>= -answer.power2 + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because // Thankfully, we can't have both "round-to-even" and subnormals because
@ -170,7 +173,7 @@ compute_float(int64_t q, uint64_t w) noexcept {
// subnormal, but we can only know this after rounding. // subnormal, but we can only know this after rounding.
// So we only declare a subnormal if we are smaller than the threshold. // So we only declare a subnormal if we are smaller than the threshold.
answer.power2 = answer.power2 =
(answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) (answer.mantissa < (am_mant_t(1) << binary::mantissa_explicit_bits()))
? 0 ? 0
: 1; : 1;
return answer; return answer;
@ -188,18 +191,18 @@ compute_float(int64_t q, uint64_t w) noexcept {
// ... we dropped out only zeroes. But if this happened, then we can go // ... we dropped out only zeroes. But if this happened, then we can go
// back!!! // back!!!
if ((answer.mantissa << shift) == product.high) { if ((answer.mantissa << shift) == product.high) {
answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up answer.mantissa &= ~am_mant_t(1); // flip it so that we do not round up
} }
} }
answer.mantissa += (answer.mantissa & 1); // round up answer.mantissa += (answer.mantissa & 1); // round up
answer.mantissa >>= 1; answer.mantissa >>= 1;
if (answer.mantissa >= (uint64_t(2) << binary::mantissa_explicit_bits())) { if (answer.mantissa >= (am_mant_t(2) << binary::mantissa_explicit_bits())) {
answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits()); answer.mantissa = (am_mant_t(1) << binary::mantissa_explicit_bits());
answer.power2++; // undo previous addition ++answer.power2; // undo previous addition
} }
answer.mantissa &= ~(uint64_t(1) << binary::mantissa_explicit_bits()); answer.mantissa &= ~(am_mant_t(1) << binary::mantissa_explicit_bits());
if (answer.power2 >= binary::infinite_power()) { // infinity if (answer.power2 >= binary::infinite_power()) { // infinity
answer.power2 = binary::infinite_power(); answer.power2 = binary::infinite_power();
answer.mantissa = 0; answer.mantissa = 0;

View File

@ -38,8 +38,8 @@ 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.
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int32_t fastfloat_really_inline FASTFLOAT_CONSTEXPR14 am_pow_t
scientific_exponent(uint64_t mantissa, int32_t exponent) noexcept { scientific_exponent(am_mant_t mantissa, am_pow_t exponent) noexcept {
while (mantissa >= 10000) { while (mantissa >= 10000) {
mantissa /= 10000; mantissa /= 10000;
exponent += 4; exponent += 4;
@ -58,18 +58,22 @@ scientific_exponent(uint64_t mantissa, int32_t exponent) noexcept {
// this converts a native floating-point number to an extended-precision float. // this converts a native floating-point number to an extended-precision float.
template <typename T> template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
to_extended(T value) noexcept { to_extended(T const &value) noexcept {
using equiv_uint = equiv_uint_t<T>; using equiv_uint = equiv_uint_t<T>;
constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask(); constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask(); constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask(); constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
adjusted_mantissa am; adjusted_mantissa am;
int32_t bias = binary_format<T>::mantissa_explicit_bits() - constexpr am_pow_t bias = binary_format<T>::mantissa_explicit_bits() -
binary_format<T>::minimum_exponent(); binary_format<T>::minimum_exponent();
equiv_uint bits; equiv_uint bits;
#if FASTFLOAT_HAS_BIT_CAST #if FASTFLOAT_HAS_BIT_CAST
bits = std::bit_cast<equiv_uint>(value); bits =
#if FASTFLOAT_HAS_BIT_CAST == 1
std::
#endif
bit_cast<equiv_uint>(value);
#else #else
::memcpy(&bits, &value, sizeof(T)); ::memcpy(&bits, &value, sizeof(T));
#endif #endif
@ -79,7 +83,7 @@ to_extended(T value) noexcept {
am.mantissa = bits & mantissa_mask; am.mantissa = bits & mantissa_mask;
} else { } else {
// normal // normal
am.power2 = int32_t((bits & exponent_mask) >> am.power2 = am_pow_t((bits & exponent_mask) >>
binary_format<T>::mantissa_explicit_bits()); binary_format<T>::mantissa_explicit_bits());
am.power2 -= bias; am.power2 -= bias;
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
@ -93,7 +97,7 @@ to_extended(T value) noexcept {
// halfway between b and b+u. // halfway between b and b+u.
template <typename T> template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
to_extended_halfway(T value) noexcept { to_extended_halfway(T const &value) noexcept {
adjusted_mantissa am = to_extended(value); adjusted_mantissa am = to_extended(value);
am.mantissa <<= 1; am.mantissa <<= 1;
am.mantissa += 1; am.mantissa += 1;
@ -105,14 +109,15 @@ to_extended_halfway(T value) noexcept {
template <typename T, typename callback> template <typename T, typename callback>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am, fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
callback cb) noexcept { callback cb) noexcept {
int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1; constexpr am_pow_t mantissa_shift =
64 - binary_format<T>::mantissa_explicit_bits() - 1;
if (-am.power2 >= mantissa_shift) { if (-am.power2 >= mantissa_shift) {
// have a denormal float // have a denormal float
int32_t shift = -am.power2 + 1; am_pow_t shift = -am.power2 + 1;
cb(am, std::min<int32_t>(shift, 64)); cb(am, std::min<am_pow_t>(shift, 64));
// check for round-up: if rounding-nearest carried us to the hidden bit. // check for round-up: if rounding-nearest carried us to the hidden bit.
am.power2 = (am.mantissa < am.power2 = (am.mantissa <
(uint64_t(1) << binary_format<T>::mantissa_explicit_bits())) (am_mant_t(1) << binary_format<T>::mantissa_explicit_bits()))
? 0 ? 0
: 1; : 1;
return; return;
@ -123,13 +128,13 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
// check for carry // check for carry
if (am.mantissa >= if (am.mantissa >=
(uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) { (am_mant_t(2) << binary_format<T>::mantissa_explicit_bits())) {
am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits()); am.mantissa = (am_mant_t(1) << binary_format<T>::mantissa_explicit_bits());
am.power2++; ++am.power2;
} }
// check for infinite: we could have carried to an infinite power // check for infinite: we could have carried to an infinite power
am.mantissa &= ~(uint64_t(1) << binary_format<T>::mantissa_explicit_bits()); am.mantissa &= ~(am_mant_t(1) << binary_format<T>::mantissa_explicit_bits());
if (am.power2 >= binary_format<T>::infinite_power()) { if (am.power2 >= binary_format<T>::infinite_power()) {
am.power2 = binary_format<T>::infinite_power(); am.power2 = binary_format<T>::infinite_power();
am.mantissa = 0; am.mantissa = 0;
@ -138,11 +143,12 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
template <typename callback> template <typename callback>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
round_nearest_tie_even(adjusted_mantissa &am, int32_t shift, round_nearest_tie_even(adjusted_mantissa &am, am_pow_t shift,
callback cb) noexcept { callback cb) noexcept {
uint64_t const mask = (shift == 64) ? UINT64_MAX : (uint64_t(1) << shift) - 1; am_mant_t const mask =
uint64_t const halfway = (shift == 0) ? 0 : uint64_t(1) << (shift - 1); (shift == 64) ? UINT64_MAX : (am_mant_t(1) << shift) - 1;
uint64_t truncated_bits = am.mantissa & mask; am_mant_t const halfway = (shift == 0) ? 0 : am_mant_t(1) << (shift - 1);
am_mant_t truncated_bits = am.mantissa & mask;
bool is_above = truncated_bits > halfway; bool is_above = truncated_bits > halfway;
bool is_halfway = truncated_bits == halfway; bool is_halfway = truncated_bits == halfway;
@ -155,11 +161,11 @@ round_nearest_tie_even(adjusted_mantissa &am, int32_t shift,
am.power2 += shift; am.power2 += shift;
bool is_odd = (am.mantissa & 1) == 1; bool is_odd = (am.mantissa & 1) == 1;
am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above)); am.mantissa += am_mant_t(cb(is_odd, is_halfway, is_above));
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
round_down(adjusted_mantissa &am, int32_t shift) noexcept { round_down(adjusted_mantissa &am, am_pow_t shift) noexcept {
if (shift == 64) { if (shift == 64) {
am.mantissa = 0; am.mantissa = 0;
} else { } else {
@ -171,9 +177,9 @@ round_down(adjusted_mantissa &am, int32_t shift) noexcept {
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
skip_zeros(UC const *&first, UC const *last) noexcept { skip_zeros(UC const *&first, UC const *last) noexcept {
uint64_t val;
while (!cpp20_and_in_constexpr() && while (!cpp20_and_in_constexpr() &&
std::distance(first, last) >= int_cmp_len<UC>()) { std::distance(first, last) >= int_cmp_len<UC>()) {
uint64_t val;
::memcpy(&val, first, sizeof(uint64_t)); ::memcpy(&val, first, sizeof(uint64_t));
if (val != int_cmp_zeros<UC>()) { if (val != int_cmp_zeros<UC>()) {
break; break;
@ -184,7 +190,7 @@ skip_zeros(UC const *&first, UC const *last) noexcept {
if (*first != UC('0')) { if (*first != UC('0')) {
break; break;
} }
first++; ++first;
} }
} }
@ -194,9 +200,9 @@ template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
is_truncated(UC const *first, UC const *last) noexcept { is_truncated(UC const *first, UC const *last) noexcept {
// do 8-bit optimizations, can just compare to 8 literal 0s. // do 8-bit optimizations, can just compare to 8 literal 0s.
uint64_t val;
while (!cpp20_and_in_constexpr() && while (!cpp20_and_in_constexpr() &&
std::distance(first, last) >= int_cmp_len<UC>()) { std::distance(first, last) >= int_cmp_len<UC>()) {
uint64_t val;
::memcpy(&val, first, sizeof(uint64_t)); ::memcpy(&val, first, sizeof(uint64_t));
if (val != int_cmp_zeros<UC>()) { if (val != int_cmp_zeros<UC>()) {
return true; return true;
@ -220,8 +226,8 @@ is_truncated(span<UC const> s) noexcept {
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
parse_eight_digits(UC const *&p, limb &value, size_t &counter, parse_eight_digits(UC const *&p, limb &value, am_digits &counter,
size_t &count) noexcept { am_digits &count) noexcept {
value = value * 100000000 + parse_eight_digits_unrolled(p); value = value * 100000000 + parse_eight_digits_unrolled(p);
p += 8; p += 8;
counter += 8; counter += 8;
@ -230,12 +236,12 @@ parse_eight_digits(UC const *&p, limb &value, size_t &counter,
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
parse_one_digit(UC const *&p, limb &value, size_t &counter, parse_one_digit(UC const *&p, limb &value, am_digits &counter,
size_t &count) noexcept { am_digits &count) noexcept {
value = value * 10 + limb(*p - UC('0')); value = value * 10 + limb(*p - UC('0'));
p++; ++p;
counter++; ++counter;
count++; ++count;
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
@ -245,28 +251,28 @@ add_native(bigint &big, limb power, limb value) noexcept {
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
round_up_bigint(bigint &big, size_t &count) noexcept { round_up_bigint(bigint &big, am_digits &count) noexcept {
// need to round-up the digits, but need to avoid rounding // need to round-up the digits, but need to avoid rounding
// ....9999 to ...10000, which could cause a false halfway point. // ....9999 to ...10000, which could cause a false halfway point.
add_native(big, 10, 1); add_native(big, 10, 1);
count++; ++count;
} }
// parse the significant digits into a big integer // parse the significant digits into a big integer
template <typename UC> template <typename T, typename UC>
inline FASTFLOAT_CONSTEXPR20 void fastfloat_really_inline FASTFLOAT_CONSTEXPR20 am_digits
parse_mantissa(bigint &result, parsed_number_string_t<UC> &num, parse_mantissa(bigint &result, const parsed_number_string_t<UC> &num) noexcept {
size_t max_digits, size_t &digits) noexcept {
// try to minimize the number of big integer and scalar multiplication. // try to minimize the number of big integer and scalar multiplication.
// therefore, try to parse 8 digits at a time, and multiply by the largest // therefore, try to parse 8 digits at a time, and multiply by the largest
// scalar value (9 or 19 digits) for each step. // scalar value (9 or 19 digits) for each step.
size_t counter = 0; constexpr am_digits max_digits = binary_format<T>::max_digits();
digits = 0; am_digits counter = 0;
am_digits digits = 0;
limb value = 0; limb value = 0;
#ifdef FASTFLOAT_64BIT_LIMB #ifdef FASTFLOAT_64BIT_LIMB
size_t step = 19; constexpr am_digits step = 19;
#else #else
size_t step = 9; constexpr am_digits step = 9;
#endif #endif
// process all integer digits. // process all integer digits.
@ -277,10 +283,10 @@ parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
while (p != pend) { while (p != pend) {
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && while ((std::distance(p, pend) >= 8) && (step - counter >= 8) &&
(max_digits - digits >= 8)) { (max_digits - digits >= 8)) {
parse_eight_digits(p, value, counter, digits); parse_eight_digits<UC>(p, value, counter, digits);
} }
while (counter < step && p != pend && digits < max_digits) { while (counter < step && p != pend && digits < max_digits) {
parse_one_digit(p, value, counter, digits); parse_one_digit<UC>(p, value, counter, digits);
} }
if (digits == max_digits) { if (digits == max_digits) {
// add the temporary value, then check if we've truncated any digits // add the temporary value, then check if we've truncated any digits
@ -292,7 +298,7 @@ parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
if (truncated) { if (truncated) {
round_up_bigint(result, digits); round_up_bigint(result, digits);
} }
return; return digits;
} else { } else {
add_native(result, limb(powers_of_ten_uint64[counter]), value); add_native(result, limb(powers_of_ten_uint64[counter]), value);
counter = 0; counter = 0;
@ -311,10 +317,10 @@ parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
while (p != pend) { while (p != pend) {
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && while ((std::distance(p, pend) >= 8) && (step - counter >= 8) &&
(max_digits - digits >= 8)) { (max_digits - digits >= 8)) {
parse_eight_digits(p, value, counter, digits); parse_eight_digits<UC>(p, value, counter, digits);
} }
while (counter < step && p != pend && digits < max_digits) { while (counter < step && p != pend && digits < max_digits) {
parse_one_digit(p, value, counter, digits); parse_one_digit<UC>(p, value, counter, digits);
} }
if (digits == max_digits) { if (digits == max_digits) {
// add the temporary value, then check if we've truncated any digits // add the temporary value, then check if we've truncated any digits
@ -323,7 +329,7 @@ parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
if (truncated) { if (truncated) {
round_up_bigint(result, digits); round_up_bigint(result, digits);
} }
return; return digits;
} else { } else {
add_native(result, limb(powers_of_ten_uint64[counter]), value); add_native(result, limb(powers_of_ten_uint64[counter]), value);
counter = 0; counter = 0;
@ -335,20 +341,21 @@ parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
if (counter != 0) { if (counter != 0) {
add_native(result, limb(powers_of_ten_uint64[counter]), value); add_native(result, limb(powers_of_ten_uint64[counter]), value);
} }
return digits;
} }
template <typename T> template <typename T>
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa positive_digit_comp(
positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept { bigint &bigmant, adjusted_mantissa am, am_pow_t const exponent) noexcept {
FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent))); FASTFLOAT_ASSERT(bigmant.pow10(exponent));
adjusted_mantissa answer;
bool truncated; bool truncated;
answer.mantissa = bigmant.hi64(truncated); am.mantissa = bigmant.hi64(truncated);
int bias = binary_format<T>::mantissa_explicit_bits() - constexpr am_pow_t bias = binary_format<T>::mantissa_explicit_bits() -
binary_format<T>::minimum_exponent(); binary_format<T>::minimum_exponent();
answer.power2 = bigmant.bit_length() - 64 + bias; am.power2 =
static_cast<fast_float::am_pow_t>(bigmant.bit_length() - 64 + bias);
round<T>(answer, [truncated](adjusted_mantissa &a, int32_t shift) { round<T>(am, [truncated](adjusted_mantissa &a, am_pow_t shift) {
round_nearest_tie_even( round_nearest_tie_even(
a, shift, a, shift,
[truncated](bool is_odd, bool is_halfway, bool is_above) -> bool { [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool {
@ -357,7 +364,7 @@ positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept {
}); });
}); });
return answer; return am;
} }
// the scaling here is quite simple: we have, for the real digits `m * 10^e`, // the scaling here is quite simple: we have, for the real digits `m * 10^e`,
@ -367,38 +374,42 @@ positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept {
// are of the same magnitude. // are of the same magnitude.
template <typename T> template <typename T>
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp( inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
bigint &bigmant, adjusted_mantissa am, int32_t exponent) noexcept { bigint &bigmant, adjusted_mantissa am, am_pow_t const exponent) noexcept {
bigint &real_digits = bigmant; bigint &real_digits = bigmant;
int32_t real_exp = exponent; am_pow_t const &real_exp = exponent;
// get the value of `b`, rounded down, and get a bigint representation of b+h // get the value of `b`, rounded down, and get a bigint representation of
// b+h
adjusted_mantissa am_b = am; adjusted_mantissa am_b = am;
// gcc7 buf: use a lambda to remove the noexcept qualifier bug with // gcc7 bug: use a lambda to remove the noexcept qualifier bug with
// -Wnoexcept-type. // -Wnoexcept-type.
round<T>(am_b, round<T>(am_b,
[](adjusted_mantissa &a, int32_t shift) { round_down(a, shift); }); [](adjusted_mantissa &a, am_pow_t shift) { round_down(a, shift); });
T b; T b;
to_float(false, am_b, b); to_float(
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
false,
#endif
am_b, b);
adjusted_mantissa theor = to_extended_halfway(b); adjusted_mantissa theor = to_extended_halfway(b);
bigint theor_digits(theor.mantissa); bigint theor_digits(theor.mantissa);
int32_t theor_exp = theor.power2; am_pow_t theor_exp = theor.power2;
// scale real digits and theor digits to be same power. // scale real digits and theor digits to be same power.
int32_t pow2_exp = theor_exp - real_exp; am_pow_t pow2_exp = theor_exp - real_exp;
uint32_t pow5_exp = uint32_t(-real_exp); am_pow_t pow5_exp = -real_exp;
if (pow5_exp != 0) { if (pow5_exp != 0) {
FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp)); FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp));
} }
if (pow2_exp > 0) { if (pow2_exp > 0) {
FASTFLOAT_ASSERT(theor_digits.pow2(uint32_t(pow2_exp))); FASTFLOAT_ASSERT(theor_digits.pow2(pow2_exp));
} else if (pow2_exp < 0) { } else if (pow2_exp < 0) {
FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp))); FASTFLOAT_ASSERT(real_digits.pow2(-pow2_exp));
} }
// compare digits, and use it to direct rounding // compare digits, and use it to direct rounding
int ord = real_digits.compare(theor_digits); int ord = real_digits.compare(theor_digits);
adjusted_mantissa answer = am; round<T>(am, [ord](adjusted_mantissa &a, am_pow_t shift) {
round<T>(answer, [ord](adjusted_mantissa &a, int32_t shift) {
round_nearest_tie_even( round_nearest_tie_even(
a, shift, [ord](bool is_odd, bool _, bool __) -> bool { a, shift, [ord](bool is_odd, bool _, bool __) -> bool {
(void)_; // not needed, since we've done our comparison (void)_; // not needed, since we've done our comparison
@ -413,7 +424,7 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
}); });
}); });
return answer; return am;
} }
// parse the significant digits as a big integer to unambiguously round // parse the significant digits as a big integer to unambiguously round
@ -430,21 +441,19 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
// the actual digits. we then compare the big integer representations // the actual digits. we then compare the big integer representations
// of both, and use that to direct rounding. // of both, and use that to direct rounding.
template <typename T, typename UC> template <typename T, typename UC>
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa digit_comp(
digit_comp(parsed_number_string_t<UC> &num, adjusted_mantissa am) noexcept { parsed_number_string_t<UC> const &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 = am_pow_t const sci_exp = scientific_exponent(num.mantissa, num.exponent);
scientific_exponent(num.mantissa, static_cast<int32_t>(num.exponent));
size_t max_digits = binary_format<T>::max_digits();
size_t digits = 0;
bigint bigmant; bigint bigmant;
parse_mantissa(bigmant, num, max_digits, digits); am_digits const digits = parse_mantissa<T, UC>(bigmant, num);
// can't underflow, since digits is at most max_digits. // can't underflow, since digits is at most max_digits.
int32_t exponent = sci_exp + 1 - int32_t(digits); am_pow_t const exponent =
static_cast<am_pow_t>(sci_exp + 1 - static_cast<am_pow_t>(digits));
if (exponent >= 0) { if (exponent >= 0) {
return positive_digit_comp<T>(bigmant, exponent); return positive_digit_comp<T>(bigmant, am, exponent);
} else { } else {
return negative_digit_comp<T>(bigmant, am, exponent); return negative_digit_comp<T>(bigmant, am, exponent);
} }

View File

@ -34,7 +34,7 @@ template <typename T, typename UC = char,
typename = FASTFLOAT_ENABLE_IF(is_supported_float_type<T>::value)> typename = FASTFLOAT_ENABLE_IF(is_supported_float_type<T>::value)>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars(UC const *first, UC const *last, T &value, from_chars(UC const *first, UC const *last, T &value,
chars_format fmt = chars_format::general) noexcept; chars_format const fmt = chars_format::general) noexcept;
/** /**
* Like from_chars, but accepts an `options` argument to govern number parsing. * Like from_chars, but accepts an `options` argument to govern number parsing.
@ -43,7 +43,7 @@ from_chars(UC const *first, UC const *last, T &value,
template <typename T, typename UC = char> template <typename T, typename UC = char>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> 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> options) noexcept; parse_options_t<UC> const &options) noexcept;
/** /**
* This function multiplies an integer number by a power of 10 and returns * This function multiplies an integer number by a power of 10 and returns
@ -59,9 +59,11 @@ from_chars_advanced(UC const *first, UC const *last, T &value,
* `new` or `malloc`). * `new` or `malloc`).
*/ */
FASTFLOAT_CONSTEXPR20 inline double FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept; integer_times_pow10(uint64_t const mantissa,
int16_t const 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 const mantissa,
int16_t const decimal_exponent) noexcept;
/** /**
* This function is a template overload of `integer_times_pow10()` * This function is a template overload of `integer_times_pow10()`
@ -71,11 +73,13 @@ integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept;
template <typename T> template <typename T>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept; integer_times_pow10(uint64_t const mantissa,
int16_t const decimal_exponent) noexcept;
template <typename T> template <typename T>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept; integer_times_pow10(int64_t const mantissa,
int16_t const decimal_exponent) noexcept;
/** /**
* from_chars for integer types. * from_chars for integer types.
@ -83,7 +87,8 @@ FASTFLOAT_CONSTEXPR20
template <typename T, typename UC = char, template <typename T, typename UC = char,
typename = FASTFLOAT_ENABLE_IF(is_supported_integer_type<T>::value)> typename = FASTFLOAT_ENABLE_IF(is_supported_integer_type<T>::value)>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars(UC const *first, UC const *last, T &value, int base = 10) noexcept; from_chars(UC const *first, UC const *last, T &value,
int const base = 10) noexcept;
} // namespace fast_float } // namespace fast_float

View File

@ -31,14 +31,14 @@ namespace fast_float {
*/ */
template <class unused = void> struct powers_template { template <class unused = void> struct powers_template {
constexpr static int smallest_power_of_five = constexpr static am_pow_t smallest_power_of_five =
binary_format<double>::smallest_power_of_ten(); binary_format<double>::smallest_power_of_ten();
constexpr static int largest_power_of_five = constexpr static am_pow_t largest_power_of_five =
binary_format<double>::largest_power_of_ten(); binary_format<double>::largest_power_of_ten();
constexpr static int number_of_entries = constexpr static am_digits number_of_entries =
2 * (largest_power_of_five - smallest_power_of_five + 1); 2 * (largest_power_of_five - smallest_power_of_five + 1);
// Powers of five from 5^-342 all the way to 5^308 rounded toward one. // Powers of five from 5^-342 all the way to 5^308 rounded toward one.
constexpr static uint64_t power_of_five_128[number_of_entries] = { constexpr static am_mant_t power_of_five_128[number_of_entries] = {
0xeef453d6923bd65a, 0x113faa2906a13b3f, 0xeef453d6923bd65a, 0x113faa2906a13b3f,
0x9558b4661b6565f8, 0x4ac7ca59a424c507, 0x9558b4661b6565f8, 0x4ac7ca59a424c507,
0xbaaee17fa23ebf76, 0x5d79bcf00d2df649, 0xbaaee17fa23ebf76, 0x5d79bcf00d2df649,
@ -696,7 +696,7 @@ template <class unused = void> struct powers_template {
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
template <class unused> template <class unused>
constexpr uint64_t constexpr am_mant_t
powers_template<unused>::power_of_five_128[number_of_entries]; powers_template<unused>::power_of_five_128[number_of_entries];
#endif #endif

View File

@ -33,26 +33,38 @@
namespace fast_float { namespace fast_float {
enum class chars_format : uint64_t; // The number of digits in the mantissa.
typedef uint_fast16_t am_digits;
// The number of bits in the limb.
typedef uint_fast8_t limb_t;
typedef uint_fast8_t chars_format_t;
enum class chars_format : chars_format_t;
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
namespace detail { namespace detail {
constexpr chars_format basic_json_fmt = chars_format(1 << 5); constexpr chars_format basic_json_fmt = chars_format(1 << 4);
constexpr chars_format basic_fortran_fmt = chars_format(1 << 6); constexpr chars_format basic_fortran_fmt = chars_format(1 << 5);
} // namespace detail } // namespace detail
#endif
enum class chars_format : uint64_t { enum class chars_format : chars_format_t {
scientific = 1 << 0, scientific = 1 << 0,
fixed = 1 << 2, fixed = 1 << 1,
hex = 1 << 3,
no_infnan = 1 << 4,
// RFC 8259: https://datatracker.ietf.org/doc/html/rfc8259#section-6
json = uint64_t(detail::basic_json_fmt) | fixed | scientific | no_infnan,
// Extension of RFC 8259 where, e.g., "inf" and "nan" are allowed.
json_or_infnan = uint64_t(detail::basic_json_fmt) | fixed | scientific,
fortran = uint64_t(detail::basic_fortran_fmt) | fixed | scientific,
general = fixed | scientific, general = fixed | scientific,
allow_leading_plus = 1 << 7, hex = 1 << 2,
skip_white_space = 1 << 8, #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
no_infnan = 1 << 3,
// RFC 8259: https://datatracker.ietf.org/doc/html/rfc8259#section-6
json = chars_format_t(detail::basic_json_fmt) | general | no_infnan,
// Extension of RFC 8259 where, e.g., "inf" and "nan" are allowed.
json_or_infnan = chars_format_t(detail::basic_json_fmt) | general,
fortran = chars_format_t(detail::basic_fortran_fmt) | general,
allow_leading_plus = 1 << 6,
skip_white_space = 1 << 7,
#endif
}; };
template <typename UC> struct from_chars_result_t { template <typename UC> struct from_chars_result_t {
@ -68,16 +80,19 @@ template <typename UC> struct from_chars_result_t {
using from_chars_result = from_chars_result_t<char>; using from_chars_result = from_chars_result_t<char>;
template <typename UC> struct parse_options_t { template <typename UC> struct parse_options_t {
constexpr explicit parse_options_t(chars_format fmt = chars_format::general, constexpr explicit parse_options_t(
UC dot = UC('.'), int b = 10) chars_format const fmt = chars_format::general, UC const dot = UC('.'),
: format(fmt), decimal_point(dot), base(b) {} uint_fast8_t const b = 10) noexcept
: format(fmt), decimal_point(dot), base(b) {
FASTFLOAT_ASSUME(base >= 2 && base <= 36);
}
/** Which number formats are accepted */ /** Which number formats are accepted */
chars_format format; chars_format format;
/** The character used as decimal point */ /** The character used as decimal point */
UC decimal_point; UC decimal_point;
/** The base used for integers */ /** The base used for integers */
int base; uint_fast8_t base; /* only allowed from 2 to 36 */
}; };
using parse_options = parse_options_t<char>; using parse_options = parse_options_t<char>;
@ -218,7 +233,7 @@ using parse_options = parse_options_t<char>;
namespace fast_float { namespace fast_float {
fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() { fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() noexcept {
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED #if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED
return std::is_constant_evaluated(); return std::is_constant_evaluated();
#else #else
@ -267,12 +282,13 @@ struct is_supported_char_type
> { > {
}; };
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
// Compares two ASCII strings in a case insensitive manner. // Compares two ASCII strings in a case insensitive manner.
template <typename UC> template <typename UC>
inline FASTFLOAT_CONSTEXPR14 bool inline FASTFLOAT_CONSTEXPR14 bool
fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase, fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
size_t length) { uint8_t const length) noexcept {
for (size_t i = 0; i < length; ++i) { for (uint8_t i = 0; i != length; ++i) {
UC const actual = actual_mixedcase[i]; UC const actual = actual_mixedcase[i];
if ((actual < 256 ? actual | 32 : actual) != expected_lowercase[i]) { if ((actual < 256 ? actual | 32 : actual) != expected_lowercase[i]) {
return false; return false;
@ -280,6 +296,7 @@ fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
} }
return true; return true;
} }
#endif
#ifndef FLT_EVAL_METHOD #ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat." #error "FLT_EVAL_METHOD should be defined, please include cfloat."
@ -288,15 +305,16 @@ fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
// a pointer and a length to a contiguous block of memory // a pointer and a length to a contiguous block of memory
template <typename T> struct span { template <typename T> struct span {
T const *ptr; T const *ptr;
size_t length; am_digits length;
constexpr span(T const *_ptr, size_t _length) : ptr(_ptr), length(_length) {} constexpr span(T const *_ptr, am_digits _length) noexcept
: ptr(_ptr), length(_length) {}
constexpr span() : ptr(nullptr), length(0) {} constexpr span() noexcept : ptr(nullptr), length(0) {}
constexpr size_t len() const noexcept { return length; } constexpr am_digits len() const noexcept { return length; }
FASTFLOAT_CONSTEXPR14 const T &operator[](size_t index) const noexcept { FASTFLOAT_CONSTEXPR14 const T &operator[](am_digits index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
return ptr[index]; return ptr[index];
} }
@ -306,14 +324,15 @@ struct value128 {
uint64_t low; uint64_t low;
uint64_t high; uint64_t high;
constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {} constexpr value128(uint64_t _low, uint64_t _high) noexcept
: low(_low), high(_high) {}
constexpr value128() : low(0), high(0) {} constexpr value128() noexcept : low(0), high(0) {}
}; };
/* Helper C++14 constexpr generic implementation of leading_zeroes */ /* Helper C++14 constexpr generic implementation of leading_zeroes */
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int fastfloat_really_inline FASTFLOAT_CONSTEXPR14 limb_t
leading_zeroes_generic(uint64_t input_num, int last_bit = 0) { leading_zeroes_generic(uint64_t input_num, uint32_t last_bit = 0) noexcept {
if (input_num & uint64_t(0xffffffff00000000)) { if (input_num & uint64_t(0xffffffff00000000)) {
input_num >>= 32; input_num >>= 32;
last_bit |= 32; last_bit |= 32;
@ -337,13 +356,14 @@ leading_zeroes_generic(uint64_t input_num, int last_bit = 0) {
if (input_num & uint64_t(0x2)) { /* input_num >>= 1; */ if (input_num & uint64_t(0x2)) { /* input_num >>= 1; */
last_bit |= 1; last_bit |= 1;
} }
return 63 - last_bit; return 63 - (limb_t)last_bit;
} }
/* result might be undefined when input_num is zero */ /* result might be undefined when input_num is zero */
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 int fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb_t
leading_zeroes(uint64_t input_num) { leading_zeroes(uint64_t input_num) noexcept {
assert(input_num > 0); assert(input_num > 0);
FASTFLOAT_ASSUME(input_num > 0);
if (cpp20_and_in_constexpr()) { if (cpp20_and_in_constexpr()) {
return leading_zeroes_generic(input_num); return leading_zeroes_generic(input_num);
} }
@ -353,22 +373,23 @@ leading_zeroes(uint64_t input_num) {
// Search the mask data from most significant bit (MSB) // Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1). // to least significant bit (LSB) for a set bit (1).
_BitScanReverse64(&leading_zero, input_num); _BitScanReverse64(&leading_zero, input_num);
return (int)(63 - leading_zero); return (limb_t)(63 - leading_zero);
#else #else
return leading_zeroes_generic(input_num); return (limb_t)leading_zeroes_generic(input_num);
#endif #endif
#else #else
return __builtin_clzll(input_num); return (limb_t)__builtin_clzll(input_num);
#endif #endif
} }
// slow emulation routine for 32-bit // slow emulation routine for 32-bit
fastfloat_really_inline constexpr uint64_t emulu(uint32_t x, uint32_t y) { fastfloat_really_inline constexpr uint64_t emulu(uint32_t x,
uint32_t y) noexcept {
return x * (uint64_t)y; return x * (uint64_t)y;
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t
umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) { umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) noexcept {
uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd); uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd); uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32)); uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
@ -383,9 +404,8 @@ umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) {
// slow emulation routine for 32-bit // slow emulation routine for 32-bit
#if !defined(__MINGW64__) #if !defined(__MINGW64__)
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t _umul128(uint64_t ab, fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t
uint64_t cd, _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) noexcept {
uint64_t *hi) {
return umul128_generic(ab, cd, hi); return umul128_generic(ab, cd, hi);
} }
#endif // !__MINGW64__ #endif // !__MINGW64__
@ -394,7 +414,7 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t _umul128(uint64_t ab,
// compute 64-bit a*b // compute 64-bit a*b
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128
full_multiplication(uint64_t a, uint64_t b) { full_multiplication(uint64_t a, uint64_t b) noexcept {
if (cpp20_and_in_constexpr()) { if (cpp20_and_in_constexpr()) {
value128 answer; value128 answer;
answer.low = umul128_generic(a, b, &answer.high); answer.low = umul128_generic(a, b, &answer.high);
@ -419,47 +439,56 @@ full_multiplication(uint64_t a, uint64_t b) {
return answer; return answer;
} }
struct adjusted_mantissa { // Value of the mantissa.
uint64_t mantissa{0}; typedef uint_fast64_t am_mant_t;
int32_t power2{0}; // a negative value indicates an invalid result // Size of bits in the mantissa and path and rounding shifts
adjusted_mantissa() = default; typedef int_fast8_t am_bits_t;
constexpr bool operator==(adjusted_mantissa const &o) const { // Power bias is signed for handling a denormal float
// or an invalid mantissa.
typedef int_fast16_t am_pow_t;
// Bias so we can get the real exponent with an invalid adjusted_mantissa.
constexpr static am_pow_t invalid_am_bias = -0x8000;
struct adjusted_mantissa {
am_mant_t mantissa;
am_pow_t power2;
adjusted_mantissa() noexcept = default;
constexpr bool operator==(adjusted_mantissa const &o) const noexcept {
return mantissa == o.mantissa && power2 == o.power2; return mantissa == o.mantissa && power2 == o.power2;
} }
constexpr bool operator!=(adjusted_mantissa const &o) const { constexpr bool operator!=(adjusted_mantissa const &o) const noexcept {
return mantissa != o.mantissa || power2 != o.power2; return mantissa != o.mantissa || power2 != o.power2;
} }
}; };
// Bias so we can get the real exponent with an invalid adjusted_mantissa.
constexpr static int32_t invalid_am_bias = -0x8000;
// used for binary_format_lookup_tables<T>::max_mantissa // used for binary_format_lookup_tables<T>::max_mantissa
constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5; constexpr am_mant_t constant_55555 = 5 * 5 * 5 * 5 * 5;
template <typename T, typename U = void> struct binary_format_lookup_tables; template <typename T, typename U = void> struct binary_format_lookup_tables;
template <typename T> struct binary_format : binary_format_lookup_tables<T> { template <typename T> struct binary_format : binary_format_lookup_tables<T> {
using equiv_uint = equiv_uint_t<T>; using equiv_uint = equiv_uint_t<T>;
static constexpr int mantissa_explicit_bits(); static constexpr limb_t mantissa_explicit_bits();
static constexpr int minimum_exponent(); static constexpr am_pow_t minimum_exponent();
static constexpr int infinite_power(); static constexpr am_pow_t infinite_power();
static constexpr int sign_index(); static constexpr am_bits_t sign_index();
static constexpr int static constexpr am_bits_t
min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST
static constexpr int max_exponent_fast_path(); static constexpr am_bits_t max_exponent_fast_path();
static constexpr int max_exponent_round_to_even(); static constexpr am_bits_t max_exponent_round_to_even();
static constexpr int min_exponent_round_to_even(); static constexpr am_bits_t min_exponent_round_to_even();
static constexpr uint64_t max_mantissa_fast_path(int64_t power); static constexpr equiv_uint max_mantissa_fast_path(am_pow_t power);
static constexpr uint64_t static constexpr equiv_uint
max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST
static constexpr int largest_power_of_ten(); static constexpr am_pow_t largest_power_of_ten();
static constexpr int smallest_power_of_ten(); static constexpr am_pow_t smallest_power_of_ten();
static constexpr T exact_power_of_ten(int64_t power); static constexpr T exact_power_of_ten(am_pow_t power);
static constexpr size_t max_digits(); static constexpr am_digits max_digits();
static constexpr equiv_uint exponent_mask(); static constexpr equiv_uint exponent_mask();
static constexpr equiv_uint mantissa_mask(); static constexpr equiv_uint mantissa_mask();
static constexpr equiv_uint hidden_bit_mask(); static constexpr equiv_uint hidden_bit_mask();
@ -523,7 +552,7 @@ template <typename U> struct binary_format_lookup_tables<float, U> {
// Largest integer value v so that (5**index * v) <= 1<<24. // Largest integer value v so that (5**index * v) <= 1<<24.
// 0x1000000 == 1<<24 // 0x1000000 == 1<<24
static constexpr uint64_t max_mantissa[] = { static constexpr uint32_t max_mantissa[] = {
0x1000000, 0x1000000,
0x1000000 / 5, 0x1000000 / 5,
0x1000000 / (5 * 5), 0x1000000 / (5 * 5),
@ -544,12 +573,12 @@ template <typename U>
constexpr float binary_format_lookup_tables<float, U>::powers_of_ten[]; constexpr float binary_format_lookup_tables<float, U>::powers_of_ten[];
template <typename U> template <typename U>
constexpr uint64_t binary_format_lookup_tables<float, U>::max_mantissa[]; constexpr uint32_t binary_format_lookup_tables<float, U>::max_mantissa[];
#endif #endif
template <> template <>
inline constexpr int binary_format<double>::min_exponent_fast_path() { inline constexpr am_bits_t binary_format<double>::min_exponent_fast_path() {
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return 0; return 0;
#else #else
@ -558,7 +587,7 @@ inline constexpr int binary_format<double>::min_exponent_fast_path() {
} }
template <> template <>
inline constexpr int binary_format<float>::min_exponent_fast_path() { inline constexpr am_bits_t binary_format<float>::min_exponent_fast_path() {
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return 0; return 0;
#else #else
@ -567,77 +596,78 @@ inline constexpr int binary_format<float>::min_exponent_fast_path() {
} }
template <> template <>
inline constexpr int binary_format<double>::mantissa_explicit_bits() { inline constexpr limb_t binary_format<double>::mantissa_explicit_bits() {
return 52; return 52;
} }
template <> template <>
inline constexpr int binary_format<float>::mantissa_explicit_bits() { inline constexpr limb_t binary_format<float>::mantissa_explicit_bits() {
return 23; return 23;
} }
template <> template <>
inline constexpr int binary_format<double>::max_exponent_round_to_even() { inline constexpr am_bits_t binary_format<double>::max_exponent_round_to_even() {
return 23; return 23;
} }
template <> template <>
inline constexpr int binary_format<float>::max_exponent_round_to_even() { inline constexpr am_bits_t binary_format<float>::max_exponent_round_to_even() {
return 10; return 10;
} }
template <> template <>
inline constexpr int binary_format<double>::min_exponent_round_to_even() { inline constexpr am_bits_t binary_format<double>::min_exponent_round_to_even() {
return -4; return -4;
} }
template <> template <>
inline constexpr int binary_format<float>::min_exponent_round_to_even() { inline constexpr am_bits_t binary_format<float>::min_exponent_round_to_even() {
return -17; return -17;
} }
template <> inline constexpr int binary_format<double>::minimum_exponent() { template <>
inline constexpr am_pow_t binary_format<double>::minimum_exponent() {
return -1023; return -1023;
} }
template <> inline constexpr int binary_format<float>::minimum_exponent() { template <> inline constexpr am_pow_t binary_format<float>::minimum_exponent() {
return -127; return -127;
} }
template <> inline constexpr int binary_format<double>::infinite_power() { template <> inline constexpr am_pow_t binary_format<double>::infinite_power() {
return 0x7FF; return 0x7FF;
} }
template <> inline constexpr int binary_format<float>::infinite_power() { template <> inline constexpr am_pow_t binary_format<float>::infinite_power() {
return 0xFF; return 0xFF;
} }
template <> inline constexpr int binary_format<double>::sign_index() { #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
template <> inline constexpr am_bits_t binary_format<double>::sign_index() {
return 63; return 63;
} }
template <> inline constexpr int binary_format<float>::sign_index() { template <> inline constexpr am_bits_t binary_format<float>::sign_index() {
return 31; return 31;
} }
#endif
template <> template <>
inline constexpr int binary_format<double>::max_exponent_fast_path() { inline constexpr am_bits_t binary_format<double>::max_exponent_fast_path() {
return 22; return 22;
} }
template <> template <>
inline constexpr int binary_format<float>::max_exponent_fast_path() { inline constexpr am_bits_t binary_format<float>::max_exponent_fast_path() {
return 10; return 10;
} }
template <> template <typename T>
inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() { inline constexpr typename binary_format<T>::equiv_uint
return uint64_t(2) << mantissa_explicit_bits(); binary_format<T>::max_mantissa_fast_path() {
} return binary_format<T>::equiv_uint(2) << mantissa_explicit_bits();
template <>
inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() {
return uint64_t(2) << mantissa_explicit_bits();
} }
// credit: Jakub Jelínek // credit: Jakub Jelínek
@ -648,7 +678,7 @@ template <typename U> struct binary_format_lookup_tables<std::float16_t, U> {
// Largest integer value v so that (5**index * v) <= 1<<11. // Largest integer value v so that (5**index * v) <= 1<<11.
// 0x800 == 1<<11 // 0x800 == 1<<11
static constexpr uint64_t max_mantissa[] = {0x800, static constexpr uint16_t max_mantissa[] = {0x800,
0x800 / 5, 0x800 / 5,
0x800 / (5 * 5), 0x800 / (5 * 5),
0x800 / (5 * 5 * 5), 0x800 / (5 * 5 * 5),
@ -663,14 +693,14 @@ constexpr std::float16_t
binary_format_lookup_tables<std::float16_t, U>::powers_of_ten[]; binary_format_lookup_tables<std::float16_t, U>::powers_of_ten[];
template <typename U> template <typename U>
constexpr uint64_t constexpr uint16_t
binary_format_lookup_tables<std::float16_t, U>::max_mantissa[]; binary_format_lookup_tables<std::float16_t, U>::max_mantissa[];
#endif #endif
template <> template <>
inline constexpr std::float16_t inline constexpr std::float16_t
binary_format<std::float16_t>::exact_power_of_ten(int64_t power) { binary_format<std::float16_t>::exact_power_of_ten(am_pow_t power) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc // Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)powers_of_ten[0], powers_of_ten[power]; return (void)powers_of_ten[0], powers_of_ten[power];
} }
@ -694,74 +724,78 @@ binary_format<std::float16_t>::hidden_bit_mask() {
} }
template <> template <>
inline constexpr int binary_format<std::float16_t>::max_exponent_fast_path() { inline constexpr am_bits_t
binary_format<std::float16_t>::max_exponent_fast_path() {
return 4; return 4;
} }
template <> template <>
inline constexpr int binary_format<std::float16_t>::mantissa_explicit_bits() { inline constexpr limb_t
binary_format<std::float16_t>::mantissa_explicit_bits() {
return 10; return 10;
} }
template <> template <>
inline constexpr uint64_t inline constexpr binary_format<std::float16_t>::equiv_uint
binary_format<std::float16_t>::max_mantissa_fast_path() { binary_format<std::float16_t>::max_mantissa_fast_path(am_pow_t power) {
return uint64_t(2) << mantissa_explicit_bits();
}
template <>
inline constexpr uint64_t
binary_format<std::float16_t>::max_mantissa_fast_path(int64_t power) {
// caller is responsible to ensure that // caller is responsible to ensure that
// power >= 0 && power <= 4 FASTFLOAT_ASSUME(power >= 0 && power <= 4);
// //
// Work around clang bug https://godbolt.org/z/zedh7rrhc // Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)max_mantissa[0], max_mantissa[power]; return (void)max_mantissa[0], max_mantissa[power];
} }
template <> template <>
inline constexpr int binary_format<std::float16_t>::min_exponent_fast_path() { inline constexpr am_bits_t
binary_format<std::float16_t>::min_exponent_fast_path() {
return 0; return 0;
} }
template <> template <>
inline constexpr int inline constexpr am_bits_t
binary_format<std::float16_t>::max_exponent_round_to_even() { binary_format<std::float16_t>::max_exponent_round_to_even() {
return 5; return 5;
} }
template <> template <>
inline constexpr int inline constexpr am_bits_t
binary_format<std::float16_t>::min_exponent_round_to_even() { binary_format<std::float16_t>::min_exponent_round_to_even() {
return -22; return -22;
} }
template <> template <>
inline constexpr int binary_format<std::float16_t>::minimum_exponent() { inline constexpr am_pow_t binary_format<std::float16_t>::minimum_exponent() {
return -15; return -15;
} }
template <> template <>
inline constexpr int binary_format<std::float16_t>::infinite_power() { inline constexpr am_pow_t binary_format<std::float16_t>::infinite_power() {
return 0x1F; return 0x1F;
} }
template <> inline constexpr int binary_format<std::float16_t>::sign_index() { #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
template <>
inline constexpr am_bits_t binary_format<std::float16_t>::sign_index() {
return 15; return 15;
} }
#endif
template <> template <>
inline constexpr int binary_format<std::float16_t>::largest_power_of_ten() { inline constexpr am_pow_t
binary_format<std::float16_t>::largest_power_of_ten() {
return 4; return 4;
} }
template <> template <>
inline constexpr int binary_format<std::float16_t>::smallest_power_of_ten() { inline constexpr am_pow_t
binary_format<std::float16_t>::smallest_power_of_ten() {
return -27; return -27;
} }
template <> template <>
inline constexpr size_t binary_format<std::float16_t>::max_digits() { inline constexpr am_digits binary_format<std::float16_t>::max_digits() {
return 22; return 22;
} }
#endif // __STDCPP_FLOAT16_T__ #endif // __STDCPP_FLOAT16_T__
@ -774,7 +808,7 @@ template <typename U> struct binary_format_lookup_tables<std::bfloat16_t, U> {
// Largest integer value v so that (5**index * v) <= 1<<8. // Largest integer value v so that (5**index * v) <= 1<<8.
// 0x100 == 1<<8 // 0x100 == 1<<8
static constexpr uint64_t max_mantissa[] = {0x100, 0x100 / 5, 0x100 / (5 * 5), static constexpr uint16_t max_mantissa[] = {0x100, 0x100 / 5, 0x100 / (5 * 5),
0x100 / (5 * 5 * 5), 0x100 / (5 * 5 * 5),
0x100 / (5 * 5 * 5 * 5)}; 0x100 / (5 * 5 * 5 * 5)};
}; };
@ -793,13 +827,14 @@ constexpr uint64_t
template <> template <>
inline constexpr std::bfloat16_t inline constexpr std::bfloat16_t
binary_format<std::bfloat16_t>::exact_power_of_ten(int64_t power) { binary_format<std::bfloat16_t>::exact_power_of_ten(am_pow_t power) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc // Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)powers_of_ten[0], powers_of_ten[power]; return (void)powers_of_ten[0], powers_of_ten[power];
} }
template <> template <>
inline constexpr int binary_format<std::bfloat16_t>::max_exponent_fast_path() { inline constexpr am_bits_t
binary_format<std::bfloat16_t>::max_exponent_fast_path() {
return 3; return 3;
} }
@ -822,88 +857,91 @@ binary_format<std::bfloat16_t>::hidden_bit_mask() {
} }
template <> template <>
inline constexpr int binary_format<std::bfloat16_t>::mantissa_explicit_bits() { inline constexpr limb_t
binary_format<std::bfloat16_t>::mantissa_explicit_bits() {
return 7; return 7;
} }
template <> template <>
inline constexpr uint64_t inline constexpr binary_format<std::bfloat16_t>::equiv_uint
binary_format<std::bfloat16_t>::max_mantissa_fast_path() { binary_format<std::bfloat16_t>::max_mantissa_fast_path(am_pow_t power) {
return uint64_t(2) << mantissa_explicit_bits();
}
template <>
inline constexpr uint64_t
binary_format<std::bfloat16_t>::max_mantissa_fast_path(int64_t power) {
// caller is responsible to ensure that // caller is responsible to ensure that
// power >= 0 && power <= 3 FASTFLOAT_ASSUME(power >= 0 && power <= 3);
// //
// Work around clang bug https://godbolt.org/z/zedh7rrhc // Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)max_mantissa[0], max_mantissa[power]; return (void)max_mantissa[0], max_mantissa[power];
} }
template <> template <>
inline constexpr int binary_format<std::bfloat16_t>::min_exponent_fast_path() { inline constexpr am_bits_t
binary_format<std::bfloat16_t>::min_exponent_fast_path() {
return 0; return 0;
} }
template <> template <>
inline constexpr int inline constexpr am_bits_t
binary_format<std::bfloat16_t>::max_exponent_round_to_even() { binary_format<std::bfloat16_t>::max_exponent_round_to_even() {
return 3; return 3;
} }
template <> template <>
inline constexpr int inline constexpr am_bits_t
binary_format<std::bfloat16_t>::min_exponent_round_to_even() { binary_format<std::bfloat16_t>::min_exponent_round_to_even() {
return -24; return -24;
} }
template <> template <>
inline constexpr int binary_format<std::bfloat16_t>::minimum_exponent() { inline constexpr am_pow_t binary_format<std::bfloat16_t>::minimum_exponent() {
return -127; return -127;
} }
template <> template <>
inline constexpr int binary_format<std::bfloat16_t>::infinite_power() { inline constexpr am_pow_t binary_format<std::bfloat16_t>::infinite_power() {
return 0xFF; return 0xFF;
} }
template <> inline constexpr int binary_format<std::bfloat16_t>::sign_index() { #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
template <>
inline constexpr am_bits_t binary_format<std::bfloat16_t>::sign_index() {
return 15; return 15;
} }
#endif
template <> template <>
inline constexpr int binary_format<std::bfloat16_t>::largest_power_of_ten() { inline constexpr am_pow_t
binary_format<std::bfloat16_t>::largest_power_of_ten() {
return 38; return 38;
} }
template <> template <>
inline constexpr int binary_format<std::bfloat16_t>::smallest_power_of_ten() { inline constexpr am_pow_t
binary_format<std::bfloat16_t>::smallest_power_of_ten() {
return -60; return -60;
} }
template <> template <>
inline constexpr size_t binary_format<std::bfloat16_t>::max_digits() { inline constexpr am_digits binary_format<std::bfloat16_t>::max_digits() {
return 98; return 98;
} }
#endif // __STDCPP_BFLOAT16_T__ #endif // __STDCPP_BFLOAT16_T__
template <> template <>
inline constexpr uint64_t inline constexpr binary_format<double>::equiv_uint
binary_format<double>::max_mantissa_fast_path(int64_t power) { binary_format<double>::max_mantissa_fast_path(am_pow_t power) {
// caller is responsible to ensure that // caller is responsible to ensure that
// power >= 0 && power <= 22 FASTFLOAT_ASSUME(power >= 0 && power <= 22);
// //
// Work around clang bug https://godbolt.org/z/zedh7rrhc // Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)max_mantissa[0], max_mantissa[power]; return (void)max_mantissa[0], max_mantissa[power];
} }
template <> template <>
inline constexpr uint64_t inline constexpr binary_format<float>::equiv_uint
binary_format<float>::max_mantissa_fast_path(int64_t power) { binary_format<float>::max_mantissa_fast_path(am_pow_t power) {
// caller is responsible to ensure that // caller is responsible to ensure that
// power >= 0 && power <= 10 FASTFLOAT_ASSUME(power >= 0 && power <= 10);
// //
// Work around clang bug https://godbolt.org/z/zedh7rrhc // Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)max_mantissa[0], max_mantissa[power]; return (void)max_mantissa[0], max_mantissa[power];
@ -911,39 +949,49 @@ binary_format<float>::max_mantissa_fast_path(int64_t power) {
template <> template <>
inline constexpr double inline constexpr double
binary_format<double>::exact_power_of_ten(int64_t power) { binary_format<double>::exact_power_of_ten(am_pow_t power) {
// caller is responsible to ensure that
FASTFLOAT_ASSUME(power >= 0 && power <= 22);
//
// Work around clang bug https://godbolt.org/z/zedh7rrhc // Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)powers_of_ten[0], powers_of_ten[power]; return (void)powers_of_ten[0], powers_of_ten[power];
} }
template <> template <>
inline constexpr float binary_format<float>::exact_power_of_ten(int64_t power) { inline constexpr float
binary_format<float>::exact_power_of_ten(am_pow_t power) {
// caller is responsible to ensure that
FASTFLOAT_ASSUME(power >= 0 && power <= 10);
//
// Work around clang bug https://godbolt.org/z/zedh7rrhc // Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)powers_of_ten[0], powers_of_ten[power]; return (void)powers_of_ten[0], powers_of_ten[power];
} }
template <> inline constexpr int binary_format<double>::largest_power_of_ten() { template <>
inline constexpr am_pow_t binary_format<double>::largest_power_of_ten() {
return 308; return 308;
} }
template <> inline constexpr int binary_format<float>::largest_power_of_ten() { template <>
inline constexpr am_pow_t binary_format<float>::largest_power_of_ten() {
return 38; return 38;
} }
template <> template <>
inline constexpr int binary_format<double>::smallest_power_of_ten() { inline constexpr am_pow_t binary_format<double>::smallest_power_of_ten() {
return -342; return -342;
} }
template <> inline constexpr int binary_format<float>::smallest_power_of_ten() { template <>
inline constexpr am_pow_t binary_format<float>::smallest_power_of_ten() {
return -64; return -64;
} }
template <> inline constexpr size_t binary_format<double>::max_digits() { template <> inline constexpr am_digits binary_format<double>::max_digits() {
return 769; return 769;
} }
template <> inline constexpr size_t binary_format<float>::max_digits() { template <> inline constexpr am_digits binary_format<float>::max_digits() {
return 114; return 114;
} }
@ -984,23 +1032,34 @@ binary_format<double>::hidden_bit_mask() {
} }
template <typename T> template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void to_float(
to_float(bool negative, adjusted_mantissa am, T &value) { #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
bool const negative,
#endif
adjusted_mantissa const &am, T &value) noexcept {
using equiv_uint = equiv_uint_t<T>; using equiv_uint = equiv_uint_t<T>;
equiv_uint word = equiv_uint(am.mantissa); equiv_uint word = equiv_uint(am.mantissa);
word = equiv_uint(word | equiv_uint(am.power2) word = equiv_uint(word | equiv_uint(am.power2)
<< binary_format<T>::mantissa_explicit_bits()); << binary_format<T>::mantissa_explicit_bits());
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
word = word =
equiv_uint(word | equiv_uint(negative) << binary_format<T>::sign_index()); equiv_uint(word | equiv_uint(negative) << binary_format<T>::sign_index());
#endif
#if FASTFLOAT_HAS_BIT_CAST #if FASTFLOAT_HAS_BIT_CAST
value = std::bit_cast<T>(word); value =
#if FASTFLOAT_HAS_BIT_CAST == 1
std::
#endif
bit_cast<T>(word);
#else #else
::memcpy(&value, &word, sizeof(T)); ::memcpy(&value, &word, sizeof(T));
#endif #endif
} }
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
template <typename = void> struct space_lut { template <typename = void> struct space_lut {
static constexpr bool value[] = { static constexpr uint8_t value[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@ -1016,7 +1075,7 @@ template <typename = void> struct space_lut {
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
template <typename T> constexpr bool space_lut<T>::value[]; template <typename T> constexpr uint8_t space_lut<T>::value[];
#endif #endif
@ -1024,6 +1083,8 @@ template <typename UC> constexpr bool is_space(UC c) {
return c < 256 && space_lut<>::value[uint8_t(c)]; return c < 256 && space_lut<>::value[uint8_t(c)];
} }
#endif
template <typename UC> static constexpr uint64_t int_cmp_zeros() { template <typename UC> static constexpr uint64_t int_cmp_zeros() {
static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4), static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4),
"Unsupported character size"); "Unsupported character size");
@ -1038,6 +1099,8 @@ template <typename UC> static constexpr int int_cmp_len() {
return sizeof(uint64_t) / sizeof(UC); return sizeof(uint64_t) / sizeof(UC);
} }
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
template <typename UC> constexpr UC const *str_const_nan(); template <typename UC> constexpr UC const *str_const_nan();
template <> constexpr char const *str_const_nan<char>() { return "nan"; } template <> constexpr char const *str_const_nan<char>() { return "nan"; }
@ -1080,6 +1143,8 @@ template <> constexpr char8_t const *str_const_inf<char8_t>() {
} }
#endif #endif
#endif
template <typename = void> struct int_luts { template <typename = void> struct int_luts {
static constexpr uint8_t chdigit[] = { static constexpr uint8_t chdigit[] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
@ -1101,7 +1166,7 @@ template <typename = void> struct int_luts {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255}; 255};
static constexpr size_t maxdigits_u64[] = { static constexpr uint8_t maxdigits_u64[] = {
64, 41, 32, 28, 25, 23, 22, 21, 20, 19, 18, 18, 17, 17, 16, 16, 16, 16, 64, 41, 32, 28, 25, 23, 22, 21, 20, 19, 18, 18, 17, 17, 16, 16, 16, 16,
15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13}; 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13};
@ -1124,14 +1189,14 @@ template <typename = void> struct int_luts {
template <typename T> constexpr uint8_t int_luts<T>::chdigit[]; template <typename T> constexpr uint8_t int_luts<T>::chdigit[];
template <typename T> constexpr size_t int_luts<T>::maxdigits_u64[]; template <typename T> constexpr uint8_t int_luts<T>::maxdigits_u64[];
template <typename T> constexpr uint64_t int_luts<T>::min_safe_u64[]; template <typename T> constexpr uint64_t int_luts<T>::min_safe_u64[];
#endif #endif
template <typename UC> template <typename UC>
fastfloat_really_inline constexpr uint8_t ch_to_digit(UC c) { fastfloat_really_inline constexpr uint_fast8_t ch_to_digit(UC c) noexcept {
// wchar_t and char can be signed, so we need to be careful. // wchar_t and char can be signed, so we need to be careful.
using UnsignedUC = typename std::make_unsigned<UC>::type; using UnsignedUC = typename std::make_unsigned<UC>::type;
return int_luts<>::chdigit[static_cast<unsigned char>( return int_luts<>::chdigit[static_cast<unsigned char>(
@ -1140,13 +1205,15 @@ fastfloat_really_inline constexpr uint8_t ch_to_digit(UC c) {
-((static_cast<UnsignedUC>(c) & ~0xFFull) == 0)))]; -((static_cast<UnsignedUC>(c) & ~0xFFull) == 0)))];
} }
fastfloat_really_inline constexpr size_t max_digits_u64(int base) { fastfloat_really_inline constexpr uint_fast8_t
max_digits_u64(uint_fast8_t base) noexcept {
return int_luts<>::maxdigits_u64[base - 2]; return int_luts<>::maxdigits_u64[base - 2];
} }
// If a u64 is exactly max_digits_u64() in length, this is // If a u64 is exactly max_digits_u64() in length, this is
// the value below which it has definitely overflowed. // the value below which it has definitely overflowed.
fastfloat_really_inline constexpr uint64_t min_safe_u64(int base) { fastfloat_really_inline constexpr uint64_t
min_safe_u64(uint_fast8_t base) noexcept {
return int_luts<>::min_safe_u64[base - 2]; return int_luts<>::min_safe_u64[base - 2];
} }
@ -1238,19 +1305,6 @@ operator^=(chars_format &lhs, chars_format rhs) noexcept {
return lhs = (lhs ^ rhs); return lhs = (lhs ^ rhs);
} }
namespace detail {
// adjust for deprecated feature macros
constexpr chars_format adjust_for_feature_macros(chars_format fmt) {
return fmt
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS
| chars_format::allow_leading_plus
#endif
#ifdef FASTFLOAT_SKIP_WHITE_SPACE
| chars_format::skip_white_space
#endif
;
}
} // namespace detail
} // namespace fast_float } // namespace fast_float
#endif #endif

View File

@ -14,6 +14,7 @@
namespace fast_float { namespace fast_float {
namespace detail { namespace detail {
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
/** /**
* Special case +inf, -inf, nan, infinity, -infinity. * Special case +inf, -inf, nan, infinity, -infinity.
* The case comparisons could be made much faster given that we know that the * The case comparisons could be made much faster given that we know that the
@ -22,18 +23,21 @@ namespace detail {
template <typename T, typename UC> template <typename T, typename UC>
from_chars_result_t<UC> from_chars_result_t<UC>
FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, UC const *last, FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, UC const *last,
T &value, chars_format fmt) noexcept { T &value,
const chars_format fmt) noexcept {
from_chars_result_t<UC> answer{}; from_chars_result_t<UC> answer{};
answer.ptr = first; answer.ptr = first;
answer.ec = std::errc(); // be optimistic answer.ec = std::errc(); // be optimistic
// assume first < last, so dereference without checks; FASTFLOAT_ASSUME(first < last); // so dereference without checks
bool const minusSign = (*first == UC('-')); bool const minusSign = (*first == UC('-'));
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
if ((*first == UC('-')) || if ((*first == UC('-')) ||
(uint64_t(fmt & chars_format::allow_leading_plus) && ((chars_format_t(fmt & chars_format::allow_leading_plus)) &&
(*first == UC('+')))) { (*first == UC('+')))) {
++first; ++first;
} }
if (last - first >= 3) { if (last - first >= 3) {
if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) { if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
answer.ptr = (first += 3); answer.ptr = (first += 3);
@ -69,7 +73,9 @@ from_chars_result_t<UC>
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
return answer; return answer;
} }
#endif
#ifndef FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
/** /**
* Returns true if the floating-pointing rounding mode is to 'nearest'. * Returns true if the floating-pointing rounding mode is to 'nearest'.
* It is the default on most system. This function is meant to be inexpensive. * It is the default on most system. This function is meant to be inexpensive.
@ -134,6 +140,7 @@ fastfloat_really_inline bool rounds_to_nearest() noexcept {
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
} }
#endif
} // namespace detail } // namespace detail
@ -141,7 +148,7 @@ template <typename T> struct from_chars_caller {
template <typename UC> template <typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
call(UC const *first, UC const *last, T &value, call(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept { parse_options_t<UC> const &options) noexcept {
return from_chars_advanced(first, last, value, options); return from_chars_advanced(first, last, value, options);
} }
}; };
@ -151,7 +158,7 @@ template <> struct from_chars_caller<std::float32_t> {
template <typename UC> template <typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
call(UC const *first, UC const *last, std::float32_t &value, call(UC const *first, UC const *last, std::float32_t &value,
parse_options_t<UC> options) noexcept { parse_options_t<UC> const options) noexcept {
// if std::float32_t is defined, and we are in C++23 mode; macro set for // if std::float32_t is defined, and we are in C++23 mode; macro set for
// float32; set value to float due to equivalence between float and // float32; set value to float due to equivalence between float and
// float32_t // float32_t
@ -168,7 +175,7 @@ template <> struct from_chars_caller<std::float64_t> {
template <typename UC> template <typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
call(UC const *first, UC const *last, std::float64_t &value, call(UC const *first, UC const *last, std::float64_t &value,
parse_options_t<UC> options) noexcept { parse_options_t<UC> const options) noexcept {
// if std::float64_t is defined, and we are in C++23 mode; macro set for // if std::float64_t is defined, and we are in C++23 mode; macro set for
// float64; set value as double due to equivalence between double and // float64; set value as double due to equivalence between double and
// float64_t // float64_t
@ -183,15 +190,19 @@ template <> struct from_chars_caller<std::float64_t> {
template <typename T, typename UC, typename> template <typename T, typename UC, typename>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars(UC const *first, UC const *last, T &value, from_chars(UC const *first, UC const *last, T &value,
chars_format fmt /*= chars_format::general*/) noexcept { chars_format const fmt /*= chars_format::general*/) noexcept {
return from_chars_caller<T>::call(first, last, value, return from_chars_caller<T>::call(first, last, value,
parse_options_t<UC>(fmt)); parse_options_t<UC>(fmt));
} }
template <typename T> template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative, clinger_fast_path_impl(am_mant_t const mantissa, am_pow_t const exponent,
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
bool const is_negative,
#endif
T &value) noexcept { T &value) noexcept {
// 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.
@ -206,7 +217,9 @@ clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
// We could check it first (before the previous branch), but // We could check it first (before the previous branch), but
// there might be performance advantages at having the check // there might be performance advantages at having the check
// be last. // be last.
#ifndef FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) { if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) {
#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 (mantissa <= binary_format<T>::max_mantissa_fast_path()) { if (mantissa <= binary_format<T>::max_mantissa_fast_path()) {
@ -216,11 +229,14 @@ clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
} else { } else {
value = value * binary_format<T>::exact_power_of_ten(exponent); value = value * binary_format<T>::exact_power_of_ten(exponent);
} }
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (is_negative) { if (is_negative) {
value = -value; value = -value;
} }
#endif
return true; return true;
} }
#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
@ -230,17 +246,24 @@ clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
#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 (mantissa == 0) { if (mantissa == 0) {
value = is_negative ? T(-0.) : T(0.); value =
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
is_negative ? T(-0.) :
#endif
T(0.);
return true; return true;
} }
#endif #endif
value = T(mantissa) * binary_format<T>::exact_power_of_ten(exponent); value = T(mantissa) * binary_format<T>::exact_power_of_ten(exponent);
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (is_negative) { if (is_negative) {
value = -value; value = -value;
} }
#endif
return true; return true;
} }
} }
#endif
} }
return false; return false;
} }
@ -252,7 +275,7 @@ clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
*/ */
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_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept { from_chars_advanced(parsed_number_string_t<UC> const &pns, T &value) noexcept {
static_assert(is_supported_float_type<T>::value, static_assert(is_supported_float_type<T>::value,
"only some floating-point types are supported"); "only some floating-point types are supported");
static_assert(is_supported_char_type<UC>::value, static_assert(is_supported_char_type<UC>::value,
@ -263,8 +286,11 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
answer.ec = std::errc(); // be optimistic answer.ec = std::errc(); // be optimistic
answer.ptr = pns.lastmatch; answer.ptr = pns.lastmatch;
if (!pns.too_many_digits && if (!pns.too_many_digits && clinger_fast_path_impl(pns.mantissa, pns.exponent,
clinger_fast_path_impl(pns.mantissa, pns.exponent, pns.negative, value)) #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
pns.negative,
#endif
value))
return answer; return answer;
adjusted_mantissa am = adjusted_mantissa am =
@ -280,7 +306,11 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
if (am.power2 < 0) { if (am.power2 < 0) {
am = digit_comp<T>(pns, am); am = digit_comp<T>(pns, am);
} }
to_float(pns.negative, am, value); to_float(
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
pns.negative,
#endif
am, value);
// Test for over/underflow. // Test for over/underflow.
if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) || if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) ||
am.power2 == binary_format<T>::infinite_power()) { am.power2 == binary_format<T>::infinite_power()) {
@ -292,19 +322,18 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
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_float_advanced(UC const *first, UC const *last, T &value, from_chars_float_advanced(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept { parse_options_t<UC> const &options) noexcept {
static_assert(is_supported_float_type<T>::value, static_assert(is_supported_float_type<T>::value,
"only some floating-point types are supported"); "only some floating-point types are supported");
static_assert(is_supported_char_type<UC>::value, static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported"); "only char, wchar_t, char16_t and char32_t are supported");
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
from_chars_result_t<UC> answer; from_chars_result_t<UC> answer;
if (uint64_t(fmt & chars_format::skip_white_space)) { #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (chars_format_t(options.format & chars_format::skip_white_space)) {
while ((first != last) && fast_float::is_space(*first)) { while ((first != last) && fast_float::is_space(*first)) {
first++; ++first;
} }
} }
if (first == last) { if (first == last) {
@ -312,18 +341,29 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value,
answer.ptr = first; answer.ptr = first;
return answer; return answer;
} }
parsed_number_string_t<UC> pns = #else
uint64_t(fmt & detail::basic_json_fmt) // We are in parser code with external loop that checks bounds.
FASTFLOAT_ASSUME(first < last);
#endif
parsed_number_string_t<UC> const pns =
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
(chars_format_t(options.format & detail::basic_json_fmt))
? parse_number_string<true, UC>(first, last, options) ? parse_number_string<true, UC>(first, last, options)
: parse_number_string<false, UC>(first, last, options); :
if (!pns.valid) { #endif
if (uint64_t(fmt & chars_format::no_infnan)) { parse_number_string<false, UC>(first, last, options);
if (pns.invalid) {
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (chars_format_t(options.format & chars_format::no_infnan)) {
#endif
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
answer.ptr = first; answer.ptr = first;
return answer; return answer;
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
} else { } else {
return detail::parse_infnan(first, last, value, fmt); return detail::parse_infnan(first, last, value, options.format);
} }
#endif
} }
// call overload that takes parsed_number_string_t directly. // call overload that takes parsed_number_string_t directly.
@ -332,55 +372,81 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value,
template <typename T, typename UC, typename> template <typename T, typename UC, typename>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars(UC const *first, UC const *last, T &value, int base) noexcept { from_chars(UC const *first, UC const *last, T &value, int const base) noexcept {
static_assert(is_supported_integer_type<T>::value, static_assert(is_supported_integer_type<T>::value,
"only integer types are supported"); "only integer types are supported");
static_assert(is_supported_char_type<UC>::value, static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported"); "only char, wchar_t, char16_t and char32_t are supported");
parse_options_t<UC> options; parse_options_t<UC> const options(chars_format::general, UC('.'),
options.base = base; static_cast<uint_fast8_t>(base));
return from_chars_advanced(first, last, value, options); return from_chars_advanced(first, last, value, options);
} }
template <typename T> template <typename T>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept { integer_times_pow10(uint64_t const mantissa,
int16_t const decimal_exponent) noexcept {
T value; T value;
if (clinger_fast_path_impl(mantissa, decimal_exponent, false, value)) if (clinger_fast_path_impl(mantissa, decimal_exponent,
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
false,
#endif
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(
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
false,
#endif
am, value);
return value; return value;
} }
template <typename T> template <typename T>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept { integer_times_pow10(int64_t const mantissa,
int16_t const decimal_exponent) noexcept {
#ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
FASTFLOAT_ASSUME(mantissa > 0);
const am_mant_t m = static_cast<am_mant_t>(mantissa);
#else
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 am_mant_t m =
static_cast<am_mant_t>(is_negative ? -mantissa : mantissa);
#endif
T value; T value;
if (clinger_fast_path_impl(m, decimal_exponent, is_negative, value)) if (clinger_fast_path_impl(m, decimal_exponent,
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
is_negative,
#endif
value))
return value; return value;
adjusted_mantissa am = compute_float<binary_format<T>>(decimal_exponent, m); adjusted_mantissa am =
to_float(is_negative, am, value); 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; return value;
} }
FASTFLOAT_CONSTEXPR20 inline double FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept { integer_times_pow10(uint64_t const mantissa,
int16_t const decimal_exponent) noexcept {
return integer_times_pow10<double>(mantissa, decimal_exponent); return integer_times_pow10<double>(mantissa, decimal_exponent);
} }
FASTFLOAT_CONSTEXPR20 inline double FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept { integer_times_pow10(int64_t const mantissa,
int16_t const decimal_exponent) noexcept {
return integer_times_pow10<double>(mantissa, decimal_exponent); return integer_times_pow10<double>(mantissa, decimal_exponent);
} }
@ -392,7 +458,7 @@ FASTFLOAT_CONSTEXPR20
std::is_integral<Int>::value && std::is_integral<Int>::value &&
!std::is_signed<Int>::value, !std::is_signed<Int>::value,
T>::type T>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept { integer_times_pow10(Int mantissa, int16_t decimal_exponent) noexcept {
return integer_times_pow10<T>(static_cast<uint64_t>(mantissa), return integer_times_pow10<T>(static_cast<uint64_t>(mantissa),
decimal_exponent); decimal_exponent);
} }
@ -403,7 +469,7 @@ FASTFLOAT_CONSTEXPR20
std::is_integral<Int>::value && std::is_integral<Int>::value &&
std::is_signed<Int>::value, std::is_signed<Int>::value,
T>::type T>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept { integer_times_pow10(Int mantissa, int16_t decimal_exponent) noexcept {
return integer_times_pow10<T>(static_cast<int64_t>(mantissa), return integer_times_pow10<T>(static_cast<int64_t>(mantissa),
decimal_exponent); decimal_exponent);
} }
@ -411,37 +477,44 @@ FASTFLOAT_CONSTEXPR20
template <typename Int> template <typename Int>
FASTFLOAT_CONSTEXPR20 typename std::enable_if< FASTFLOAT_CONSTEXPR20 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, int16_t 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 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, int16_t 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);
} }
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,
parse_options_t<UC> options) noexcept { parse_options_t<UC> const &options) noexcept {
static_assert(is_supported_integer_type<T>::value, static_assert(is_supported_integer_type<T>::value,
"only integer types are supported"); "only integer types are supported");
static_assert(is_supported_char_type<UC>::value, static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported"); "only char, wchar_t, char16_t and char32_t are supported");
chars_format const fmt = detail::adjust_for_feature_macros(options.format); #ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
int const base = options.base; // We are in parser code with external loop that checks bounds.
FASTFLOAT_ASSUME(first < last);
from_chars_result_t<UC> answer; // base is already checked in the parse_options_t constructor.
if (uint64_t(fmt & chars_format::skip_white_space)) { #else
if (chars_format_t(options.format & chars_format::skip_white_space)) {
while ((first != last) && fast_float::is_space(*first)) { while ((first != last) && fast_float::is_space(*first)) {
first++; ++first;
} }
} }
if (first == last || base < 2 || base > 36) { #endif
if (
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
first == last ||
#endif
options.base < 2 || options.base > 36) {
from_chars_result_t<UC> answer;
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
answer.ptr = first; answer.ptr = first;
return answer; return answer;
@ -458,7 +531,7 @@ template <> struct from_chars_advanced_caller<1> {
template <typename T, typename UC> template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
call(UC const *first, UC const *last, T &value, call(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept { parse_options_t<UC> const &options) noexcept {
return from_chars_float_advanced(first, last, value, options); return from_chars_float_advanced(first, last, value, options);
} }
}; };
@ -467,7 +540,7 @@ template <> struct from_chars_advanced_caller<2> {
template <typename T, typename UC> template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
call(UC const *first, UC const *last, T &value, call(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept { parse_options_t<UC> const &options) noexcept {
return from_chars_int_advanced(first, last, value, options); return from_chars_int_advanced(first, last, value, options);
} }
}; };
@ -475,7 +548,7 @@ template <> struct from_chars_advanced_caller<2> {
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_advanced(UC const *first, UC const *last, T &value, from_chars_advanced(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept { parse_options_t<UC> const &options) noexcept {
return from_chars_advanced_caller< return from_chars_advanced_caller<
size_t(is_supported_float_type<T>::value) + size_t(is_supported_float_type<T>::value) +
2 * size_t(is_supported_integer_type<T>::value)>::call(first, last, value, 2 * size_t(is_supported_integer_type<T>::value)>::call(first, last, value,

View File

@ -69,7 +69,7 @@ template <typename T> std::string fHexAndDec(T v) {
return ss.str(); return ss.str();
} }
char const *round_name(int d) { const std::string round_name(int const d) {
switch (d) { switch (d) {
case FE_UPWARD: case FE_UPWARD:
return "FE_UPWARD"; return "FE_UPWARD";
@ -2126,8 +2126,9 @@ TEST_CASE("bfloat16.general") {
#endif #endif
template <typename Int, typename T, typename U> template <typename Int, typename T, typename U>
void verify_integer_times_pow10_result(Int mantissa, int decimal_exponent, void verify_integer_times_pow10_result(Int const mantissa,
T actual, U expected) { int16_t const decimal_exponent,
T const actual, U const expected) {
static_assert(std::is_same<T, U>::value, static_assert(std::is_same<T, U>::value,
"expected and actual types must match"); "expected and actual types must match");
@ -2144,8 +2145,8 @@ void verify_integer_times_pow10_result(Int mantissa, int decimal_exponent,
} }
template <typename T, typename Int> template <typename T, typename Int>
T calculate_integer_times_pow10_expected_result(Int mantissa, T calculate_integer_times_pow10_expected_result(
int decimal_exponent) { Int const mantissa, int16_t const 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; T expected_result;
@ -2158,8 +2159,9 @@ T calculate_integer_times_pow10_expected_result(Int mantissa,
} }
template <typename Int> template <typename Int>
void verify_integer_times_pow10_dflt(Int mantissa, int decimal_exponent, void verify_integer_times_pow10_dflt(Int const mantissa,
double expected) { int16_t const decimal_exponent,
double const expected) {
static_assert(std::is_integral<Int>::value); static_assert(std::is_integral<Int>::value);
// the "default" overload // the "default" overload
@ -2171,7 +2173,8 @@ void verify_integer_times_pow10_dflt(Int mantissa, int decimal_exponent,
} }
template <typename Int> template <typename Int>
void verify_integer_times_pow10_dflt(Int mantissa, int decimal_exponent) { void verify_integer_times_pow10_dflt(Int const mantissa,
int16_t const decimal_exponent) {
static_assert(std::is_integral<Int>::value); static_assert(std::is_integral<Int>::value);
const auto expected_result = const auto expected_result =
@ -2182,8 +2185,9 @@ void verify_integer_times_pow10_dflt(Int mantissa, int decimal_exponent) {
} }
template <typename T, typename Int> template <typename T, typename Int>
void verify_integer_times_pow10(Int mantissa, int decimal_exponent, void verify_integer_times_pow10(Int const mantissa,
T expected) { int16_t const decimal_exponent,
T const expected) {
static_assert(std::is_floating_point<T>::value); static_assert(std::is_floating_point<T>::value);
static_assert(std::is_integral<Int>::value); static_assert(std::is_integral<Int>::value);
@ -2196,7 +2200,8 @@ void verify_integer_times_pow10(Int mantissa, int decimal_exponent,
} }
template <typename T, typename Int> template <typename T, typename Int>
void verify_integer_times_pow10(Int mantissa, int decimal_exponent) { void verify_integer_times_pow10(Int const mantissa,
int16_t const decimal_exponent) {
static_assert(std::is_floating_point<T>::value); static_assert(std::is_floating_point<T>::value);
static_assert(std::is_integral<Int>::value); static_assert(std::is_integral<Int>::value);
@ -2208,7 +2213,8 @@ void verify_integer_times_pow10(Int mantissa, int decimal_exponent) {
namespace all_supported_types { namespace all_supported_types {
template <typename Int> template <typename Int>
void verify_integer_times_pow10(Int mantissa, int decimal_exponent) { void verify_integer_times_pow10(Int const mantissa,
int16_t const decimal_exponent) {
static_assert(std::is_integral<Int>::value); static_assert(std::is_integral<Int>::value);
// verify the "default" overload // verify the "default" overload
@ -2313,7 +2319,7 @@ TEST_CASE("integer_times_pow10") {
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);
INFO("fesetround(): " << std::string{round_name(mode)}); INFO("fesetround(): " << round_name(mode));
struct Guard { struct Guard {
~Guard() { fesetround(FE_TONEAREST); } ~Guard() { fesetround(FE_TONEAREST); }

View File

@ -31,6 +31,7 @@ int main() {
"1d-1", "1d-2", "1d-3", "1d-4"}; "1d-1", "1d-2", "1d-3", "1d-4"};
std::vector<std::string> const fmt3{"+1+4", "+1+3", "+1+2", "+1+1", "+1+0", std::vector<std::string> const fmt3{"+1+4", "+1+3", "+1+2", "+1+1", "+1+0",
"+1-1", "+1-2", "+1-3", "+1-4"}; "+1-1", "+1-2", "+1-3", "+1-4"};
fast_float::parse_options const options{ fast_float::parse_options const options{
fast_float::chars_format::fortran | fast_float::chars_format::fortran |
fast_float::chars_format::allow_leading_plus}; fast_float::chars_format::allow_leading_plus};

View File

@ -10,7 +10,7 @@ int main_readme() {
fast_float::parse_options options{ fast_float::parse_options options{
fast_float::chars_format::json | fast_float::chars_format::json |
fast_float::chars_format::allow_leading_plus}; // should be ignored fast_float::chars_format::allow_leading_plus}; // should be ignored
auto answer = fast_float::from_chars_advanced( auto const answer = fast_float::from_chars_advanced(
input.data(), input.data() + input.size(), result, options); input.data(), input.data() + input.size(), result, options);
if (answer.ec == std::errc()) { if (answer.ec == std::errc()) {
std::cerr << "should have failed\n"; std::cerr << "should have failed\n";
@ -25,7 +25,7 @@ int main_readme2() {
fast_float::parse_options options{ fast_float::parse_options options{
fast_float::chars_format::json | fast_float::chars_format::json |
fast_float::chars_format::allow_leading_plus}; // should be ignored fast_float::chars_format::allow_leading_plus}; // should be ignored
auto answer = fast_float::from_chars_advanced( auto const answer = fast_float::from_chars_advanced(
input.data(), input.data() + input.size(), result, options); input.data(), input.data() + input.size(), result, options);
if (answer.ec == std::errc()) { if (answer.ec == std::errc()) {
std::cerr << "should have failed\n"; std::cerr << "should have failed\n";
@ -41,7 +41,7 @@ int main_readme3() {
fast_float::parse_options options{ fast_float::parse_options options{
fast_float::chars_format::json_or_infnan | fast_float::chars_format::json_or_infnan |
fast_float::chars_format::allow_leading_plus}; // should be ignored fast_float::chars_format::allow_leading_plus}; // should be ignored
auto answer = fast_float::from_chars_advanced( auto const answer = fast_float::from_chars_advanced(
input.data(), input.data() + input.size(), result, options); input.data(), input.data() + input.size(), result, options);
if (answer.ec != std::errc() || (!std::isinf(result))) { if (answer.ec != std::errc() || (!std::isinf(result))) {
std::cerr << "should have parsed infinity\n"; std::cerr << "should have parsed infinity\n";
@ -97,7 +97,7 @@ int main() {
auto const &s = accept[i].input; auto const &s = accept[i].input;
auto const &expected = accept[i].expected; auto const &expected = accept[i].expected;
double result; double result;
auto answer = auto const answer =
fast_float::from_chars(s.data(), s.data() + s.size(), result, fast_float::from_chars(s.data(), s.data() + s.size(), result,
fast_float::chars_format::json_or_infnan); fast_float::chars_format::json_or_infnan);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
@ -120,8 +120,8 @@ int main() {
for (std::size_t i = 0; i < reject.size(); ++i) { for (std::size_t i = 0; i < reject.size(); ++i) {
auto const &s = reject[i].input; auto const &s = reject[i].input;
double result; double result;
auto answer = fast_float::from_chars(s.data(), s.data() + s.size(), result, auto const answer = fast_float::from_chars(
fast_float::chars_format::json); s.data(), s.data() + s.size(), result, fast_float::chars_format::json);
if (answer.ec == std::errc()) { if (answer.ec == std::errc()) {
std::cerr << "json fmt accepted invalid json " << s << std::endl; std::cerr << "json fmt accepted invalid json " << s << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
@ -131,12 +131,13 @@ int main() {
for (std::size_t i = 0; i < reject.size(); ++i) { for (std::size_t i = 0; i < reject.size(); ++i) {
auto const &f = reject[i].input; auto const &f = reject[i].input;
auto const &expected_reason = reject[i].reason; auto const &expected_reason = reject[i].reason;
auto answer = fast_float::parse_number_string<true>( auto const answer = fast_float::parse_number_string<true>(
f.data(), f.data() + f.size(), f.data(), f.data() + f.size(),
fast_float::parse_options( fast_float::parse_options(
fast_float::chars_format::json | fast_float::chars_format::json |
fast_float::chars_format::allow_leading_plus)); // should be ignored fast_float::chars_format::allow_leading_plus)); // should be
if (answer.valid) { // ignored
if (!answer.invalid) {
std::cerr << "json parse accepted invalid json " << f << std::endl; std::cerr << "json parse accepted invalid json " << f << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }