Merge 0c4171eb26b4e5cb931dddb3d0525ef1afe50a92 into e77e2bca7ec343bb10999b8438e69cd2185228d7

This commit is contained in:
HedgehogInTheCPP 2025-10-21 23:56:44 +00:00 committed by GitHub
commit 58df41a1b0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 955 additions and 663 deletions

View File

@ -27,7 +27,7 @@ jobs:
path: ./out/artifacts
- name: Upload Sarif
if: always() && steps.build.outcome == 'success'
uses: github/codeql-action/upload-sarif@v3
uses: github/codeql-action/upload-sarif@v4
with:
# Path to SARIF file relative to the root of the repository
sarif_file: cifuzz-sarif/results.sarif

View File

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

View File

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

View File

@ -11,7 +11,7 @@ jobs:
matrix:
include:
- {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: Debug}
steps:

View File

@ -9,3 +9,4 @@ Jan Pharago
Maya Warrier
Taha Khokhar
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
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
@ -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.
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.
@ -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
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
An integer `W` can be multiplied by a power of ten `10^Q` and
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
signed and unsigned integer types: `int64_t`, `uint64_t`, etc.
## Users and Related Work
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__)
#define USING_COUNTERS
#endif
#include "event_counter.h"
#include <algorithm>
#include "fast_float/fast_float.h"
#include <chrono>
#include <climits>
#include <cmath>
#include <cstdint>
@ -19,15 +21,17 @@
#include <sstream>
#include <stdio.h>
#include <string>
#include <vector>
#include <locale.h>
template <typename CharT>
double findmax_fastfloat64(std::vector<std::basic_string<CharT>> &s) {
double answer = 0;
double x = 0;
#include "fast_float/fast_float.h"
template <typename CharT, typename Value>
Value findmax_fastfloat(std::vector<std::basic_string<CharT>> &s) {
Value answer = 0;
Value 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");
}
@ -36,23 +40,10 @@ double findmax_fastfloat64(std::vector<std::basic_string<CharT>> &s) {
return answer;
}
template <typename CharT>
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;
}
#ifdef USING_COUNTERS
event_collector collector{};
#ifdef USING_COUNTERS
template <class T, class CharT>
std::vector<event_count>
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;
for (size_t i = 0; i < repeat; i++) {
collector.start();
double ts = function(lines);
auto const ts = function(lines);
if (ts == 0 && !printed_bug) {
printf("bug\n");
printed_bug = true;
@ -71,7 +62,7 @@ time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
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) {
double volumeMB = volume / (1024. * 1024.);
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;
for (size_t i = 0; i < repeat; i++) {
t1 = std::chrono::high_resolution_clock::now();
double ts = function(lines);
auto const ts = function(lines);
if (ts == 0 && !printed_bug) {
printf("bug\n");
printed_bug = true;
}
t2 = std::chrono::high_resolution_clock::now();
double dif =
std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count();
double const dif = static_cast<double>(
std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count());
average += 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);
}
void pretty_print(double volume, size_t number_of_floats, std::string name,
std::pair<double, double> result) {
void pretty_print(size_t volume, size_t number_of_floats,
std::string const &name, std::pair<double, double> result) {
double volumeMB = volume / (1024. * 1024.);
printf("%-40s: %8.2f MB/s (+/- %.1f %%) ", name.data(),
volumeMB * 1000000000 / result.first,
@ -168,7 +159,7 @@ void pretty_print(double volume, size_t number_of_floats, std::string name,
#endif
// 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;
u16line.resize(line.size());
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;
u16lines.reserve(lines.size());
for (auto const &line : lines) {
u16lines.push_back(widen(line));
u16lines.emplace_back(widen(line));
}
return u16lines;
}
void process(std::vector<std::string> &lines, size_t volume) {
size_t repeat = 1000;
size_t const repeat = 1000;
double volumeMB = volume / (1024. * 1024.);
std::cout << "ASCII volume = " << volumeMB << " MB " << std::endl;
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)",
time_it_ns(lines, findmax_fastfloat32<char>, repeat));
time_it_ns(lines, findmax_fastfloat<char, float>, repeat));
std::vector<std::u16string> lines16 = widen(lines);
volume = 2 * volume;
volumeMB = volume / (1024. * 1024.);
std::cout << "UTF-16 volume = " << volumeMB << " MB " << std::endl;
pretty_print(volume, lines.size(), "fastfloat (64)",
time_it_ns(lines16, findmax_fastfloat64<char16_t>, repeat));
pretty_print(
volume, lines.size(), "fastfloat (64)",
time_it_ns(lines16, findmax_fastfloat<char16_t, double>, repeat));
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) {
@ -216,17 +208,31 @@ void fileload(std::string filename) {
std::cout << "#### " << std::endl;
std::string line;
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;
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();
lines.push_back(line);
}
std::cout << "# read " << lines.size() << " lines " << std::endl;
process(lines, volume);
}
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()) {
std::cout << "# Using hardware counters" << std::endl;
} else {
@ -236,10 +242,12 @@ int main(int argc, char **argv) {
<< std::endl;
#endif
}
#endif
if (argc > 1) {
fileload(argv[1]);
return EXIT_SUCCESS;
}
fileload(std::string(BENCHMARK_DATA_DIR) + "/canada.txt");
fileload(std::string(BENCHMARK_DATA_DIR) + "/mesh.txt");
return EXIT_SUCCESS;

View File

@ -10,7 +10,7 @@
#include <cstring>
#include <chrono>
#include <vector>
#include <array>
#include "linux-perf-events.h"
#ifdef __linux__
@ -22,26 +22,28 @@
#endif
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)
enum event_counter_types {
CPU_CYCLES = 0,
INSTRUCTIONS = 1,
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 {
return std::chrono::duration<double>(elapsed).count();
}
@ -79,7 +81,6 @@ struct event_count {
event_counts[1] + other.event_counts[1],
event_counts[2] + other.event_counts[2],
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;
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_BRANCH_INSTRUCTIONS, // Retired branch instructions
PERF_COUNT_HW_BRANCH_MISSES}) {}
@ -142,7 +143,7 @@ struct event_collector {
bool has_events() { return setup_performance_counters(); }
#else
event_collector() {}
event_collector() = default;
bool has_events() { return false; }
#endif
@ -171,7 +172,6 @@ struct event_collector {
count.event_counts[1] = diff.instructions;
count.event_counts[2] = diff.branches;
count.event_counts[3] = diff.missed_branches;
count.event_counts[4] = 0;
#endif
count.elapsed = end_clock - start_clock;
return count;

View File

@ -10,7 +10,7 @@
#include <cstring> // for memset
#include <stdexcept>
#include <iostream>
#include <array>
#include <vector>
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{};
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));
attribs.type = TYPE;
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 (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) {
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"
#ifdef FASTFLOAT_SSE2
#if defined(FASTFLOAT_SSE2)
#include <emmintrin.h>
#endif
#ifdef FASTFLOAT_NEON
#elif defined(FASTFLOAT_NEON)
#include <arm_neon.h>
#endif
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
return std::is_same<UC, char16_t>::value;
#else
@ -35,12 +34,14 @@ fastfloat_really_inline constexpr bool is_integer(UC c) noexcept {
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 |
(val & 0x0000FF0000000000) >> 24 | (val & 0x000000FF00000000) >> 8 |
(val & 0x00000000FF000000) << 8 | (val & 0x0000000000FF0000) << 24 |
(val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56;
}
#endif
// Read 8 UC into a u64. Truncates UC if not char.
template <typename UC>
@ -48,7 +49,7 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
read8_to_u64(UC const *chars) {
if (cpp20_and_in_constexpr() || !std::is_same<UC, char>::value) {
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);
++chars;
}
@ -58,18 +59,23 @@ read8_to_u64(UC const *chars) {
::memcpy(&val, chars, sizeof(uint64_t));
#if FASTFLOAT_IS_BIG_ENDIAN == 1
// 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
return val;
}
#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
// _mm_packus_epi16 is SSE2+, converts 8×u16 → 8×u8
__m128i const packed = _mm_packus_epi16(data, data);
#ifdef FASTFLOAT_64BIT
return uint64_t(_mm_cvtsi128_si64(packed));
return static_cast<uint64_t>(_mm_cvtsi128_si64(packed));
#else
uint64_t value;
// 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)
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
uint8x8_t utf8_packed = vmovn_u16(data);
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
}
#endif // FASTFLOAT_SSE2
#endif
// MSVC SFINAE is broken pre-VS2017
#if defined(_MSC_VER) && _MSC_VER <= 1900
@ -116,20 +122,20 @@ uint64_t simd_read8_to_u64(UC const *) {
}
// credit @aqrit
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t
parse_eight_digits_unrolled(uint64_t val) {
uint64_t const mask = 0x000000FF000000FF;
uint64_t const mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
uint64_t const mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t
parse_eight_digits_unrolled(uint64_t val) noexcept {
constexpr uint64_t mask = 0x000000FF000000FF;
constexpr uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
constexpr uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
val -= 0x3030303030303030;
val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
return uint32_t(val);
return val;
}
// Call this if chars are definitely 8 digits.
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint32_t
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
parse_eight_digits_unrolled(UC const *chars) noexcept {
if (cpp20_and_in_constexpr() || !has_simd_opt<UC>()) {
return parse_eight_digits_unrolled(read8_to_u64(chars)); // truncation okay
@ -157,19 +163,22 @@ simd_parse_if_eight_digits_unrolled(char16_t const *chars,
}
#ifdef FASTFLOAT_SSE2
FASTFLOAT_SIMD_DISABLE_WARNINGS
// Load 8 UTF-16 characters (16 bytes)
__m128i const data =
_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
__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));
return true;
} else
return false;
}
FASTFLOAT_SIMD_RESTORE_WARNINGS
#elif defined(FASTFLOAT_NEON)
FASTFLOAT_SIMD_DISABLE_WARNINGS
@ -183,17 +192,16 @@ simd_parse_if_eight_digits_unrolled(char16_t const *chars,
if (vminvq_u16(mask) == 0xFFFF) {
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
return true;
} else
return false;
}
FASTFLOAT_SIMD_RESTORE_WARNINGS
#else
(void)chars;
(void)i;
#endif
return false;
#endif // FASTFLOAT_SSE2
}
#endif // FASTFLOAT_HAS_SIMD
#endif
// MSVC SFINAE is broken pre-VS2017
#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,
// [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.
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.
leading_zeros_in_integer_part,
// [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
// fractional part.
no_digits_in_fractional_part,
// The mantissa must have at least one digit.
no_digits_in_mantissa,
// Scientific notation requires an exponential part.
missing_exponential_part,
#endif
};
template <typename UC> struct parsed_number_string_t {
int64_t exponent{0};
uint64_t mantissa{0};
// an unsigned int avoids signed overflows (which are bad)
am_mant_t mantissa{0};
am_pow_t exponent{0};
UC const *lastmatch{nullptr};
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
bool negative{false};
bool valid{false};
#endif
bool invalid{false};
bool too_many_digits{false};
// contains the range of the significant digits
span<UC const> integer{}; // non-nullable
@ -269,9 +282,9 @@ using parsed_number_string = parsed_number_string_t<char>;
template <typename 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;
answer.valid = false;
answer.invalid = true;
answer.lastmatch = p;
answer.error = error;
return answer;
@ -282,53 +295,60 @@ report_parse_error(UC const *p, parse_error error) {
template <bool basic_json_fmt, typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
parse_number_string(UC const *p, UC const *pend,
parse_options_t<UC> options) noexcept {
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
UC const decimal_point = options.decimal_point;
parse_options_t<UC> const &options) noexcept {
// Cyclomatic complexity https://en.wikipedia.org/wiki/Cyclomatic_complexity
// Consider refactoring the 'parse_number_string' function.
// FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN fix this.
parsed_number_string_t<UC> answer;
answer.valid = false;
answer.too_many_digits = false;
// assume p < pend, so dereference without checks;
// so dereference without checks
FASTFLOAT_ASSUME(p < pend);
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
answer.negative = (*p == UC('-'));
if (answer.negative ||
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here
if ((*p == UC('-')) || (uint64_t(fmt & chars_format::allow_leading_plus) &&
!basic_json_fmt && *p == UC('+'))) {
((chars_format_t(options.format & chars_format::allow_leading_plus)) &&
(!basic_json_fmt && *p == UC('+')))) {
++p;
if (p == pend) {
return report_parse_error<UC>(
p, parse_error::missing_integer_or_dot_after_sign);
}
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,
parse_error::missing_integer_after_sign);
}
}
else {
if (!is_integer(*p) &&
(*p !=
decimal_point)) { // a sign must be followed by an integer or the dot
// a sign must be followed by an integer or the dot
if (!is_integer(*p) && (*p != options.decimal_point)) {
return report_parse_error<UC>(
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)) {
// a multiplication by 10 is cheaper than an arbitrary integer
// multiplication
i = 10 * i +
uint64_t(*p -
UC('0')); // might overflow, we will handle the overflow later
answer.mantissa = static_cast<fast_float::am_mant_t>(
answer.mantissa * 10 +
static_cast<fast_float::am_mant_t>(
*p - UC('0'))); // might overflow, we will handle the overflow later
++p;
}
UC const *const end_of_integer_part = p;
int64_t digit_count = int64_t(end_of_integer_part - start_digits);
answer.integer = span<UC const>(start_digits, size_t(digit_count));
am_digits 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) {
// at least 1 digit in integer part, without leading zeros
if (digit_count == 0) {
@ -339,148 +359,180 @@ parse_number_string(UC const *p, UC const *pend,
parse_error::leading_zeros_in_integer_part);
}
}
#endif
int64_t exponent = 0;
bool const has_decimal_point = (p != pend) && (*p == decimal_point);
if (has_decimal_point) {
// We can now parse the fraction part of the mantissa.
if ((p != pend) && (*p == options.decimal_point)) {
++p;
UC const *before = p;
UC const *const before = p;
// can occur at most twice without overflowing, but let it occur more, since
// 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)) {
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;
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) {
// at least 1 digit in fractional part
if (has_decimal_point && exponent == 0) {
return report_parse_error<UC>(p,
parse_error::no_digits_in_fractional_part);
if (answer.exponent == 0) {
return report_parse_error<UC>(
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);
}
int64_t exp_number = 0; // explicit exponential part
if ((uint64_t(fmt & chars_format::scientific) && (p != pend) &&
((UC('e') == *p) || (UC('E') == *p))) ||
(uint64_t(fmt & detail::basic_fortran_fmt) && (p != pend) &&
// We have now parsed the integer and the fraction part of the mantissa.
// Now we can parse the explicit exponential part.
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('D') == *p)))) {
(UC('D') == *p)))
#endif
)) {
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) ||
(UC('D') == *p)) {
++p;
}
#endif
bool neg_exp = false;
if ((p != pend) && (UC('-') == *p)) {
if (p != pend) {
if (UC('-') == *p) {
neg_exp = true;
++p;
} else if ((p != pend) &&
(UC('+') ==
*p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
} else if (UC('+') == *p) {
// '+' on exponent is allowed by C++17 20.19.3.(7.1)
++p;
}
}
// We have now parsed the sign of the exponent.
if ((p == pend) || !is_integer(*p)) {
if (!uint64_t(fmt & chars_format::fixed)) {
// The exponential part is invalid for scientific notation, so it must
// be a trailing token for fixed notation. However, fixed notation is
// disabled, so report a scientific notation error.
if (!(chars_format_t(options.format & chars_format::fixed))) {
// The exponential part is invalid for scientific notation, so it
// must be a trailing token for fixed notation. However, fixed
// notation is disabled, so report a scientific notation error.
return report_parse_error<UC>(p, parse_error::missing_exponential_part);
}
// Otherwise, we will be ignoring the 'e'.
p = location_of_e;
} else {
// Now let's parse the explicit exponent.
while ((p != pend) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - UC('0'));
if (exp_number < 0x10000000) {
exp_number = 10 * exp_number + digit;
if (exp_number < 0x1000) {
// check for exponent overflow if we have too many digits.
UC const digit = UC(*p - UC('0'));
exp_number = 10 * exp_number + static_cast<am_pow_t>(digit);
}
++p;
}
if (neg_exp) {
exp_number = -exp_number;
}
exponent += exp_number;
answer.exponent += exp_number;
}
} else {
// If it scientific and not fixed, we have to bail out.
if (uint64_t(fmt & chars_format::scientific) &&
!uint64_t(fmt & chars_format::fixed)) {
if ((chars_format_t(options.format & chars_format::scientific)) &&
!(chars_format_t(options.format & chars_format::fixed))) {
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
// of a 64-bit integer. However, this is uncommon.
//
// 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.
// We have to handle the case where we have 0.0000somenumber.
// We need to be mindful of the case where we only have zeroes...
// E.g., 0.000000000...000.
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')) {
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) {
answer.too_many_digits = true;
// Let us start again, this time, avoiding overflows.
// We don't need to call if is_integer, since we use the
// pre-tokenized spans from above.
i = 0;
answer.mantissa = 0;
p = answer.integer.ptr;
UC const *int_end = p + answer.integer.len();
uint64_t const minimal_nineteen_digit_integer{1000000000000000000};
while ((i < minimal_nineteen_digit_integer) && (p != int_end)) {
i = i * 10 + uint64_t(*p - UC('0'));
constexpr am_mant_t minimal_nineteen_digit_integer{1000000000000000000};
while ((answer.mantissa < minimal_nineteen_digit_integer) &&
(p != int_end)) {
answer.mantissa = static_cast<am_mant_t>(
answer.mantissa * 10 + static_cast<am_mant_t>(*p - UC('0')));
++p;
}
if (i >= minimal_nineteen_digit_integer) { // We have a big integer
exponent = end_of_integer_part - p + exp_number;
} else { // We have a value with a fractional component.
if (answer.mantissa >= minimal_nineteen_digit_integer) {
// We have a big integers, so skip the fraction part completely.
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;
UC const *frac_end = p + answer.fraction.len();
while ((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
i = i * 10 + uint64_t(*p - UC('0'));
UC const *const frac_end = p + answer.fraction.len();
while ((answer.mantissa < minimal_nineteen_digit_integer) &&
(p != frac_end)) {
answer.mantissa = static_cast<am_mant_t>(
answer.mantissa * 10 + static_cast<am_mant_t>(*p - UC('0')));
++p;
}
exponent = answer.fraction.ptr - p + exp_number;
}
// We have now corrected both exponent and i, to a truncated value
answer.exponent = am_pow_t(answer.fraction.ptr - p) + exp_number;
}
}
answer.exponent = exponent;
answer.mantissa = i;
// We have now corrected both exponent and mantissa, to a truncated value
}
return answer;
}
template <typename T, typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
parse_int_string(UC const *p, UC const *pend, T &value,
parse_options_t<UC> options) {
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
int const base = options.base;
parse_options_t<UC> const &options) noexcept {
from_chars_result_t<UC> answer;
UC const *const first = p;
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
// Read sign
bool const negative = (*p == UC('-'));
#ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(push)
@ -495,12 +547,15 @@ parse_int_string(UC const *p, UC const *pend, T &value,
return answer;
}
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;
}
#endif
UC const *const start_num = p;
// Skip leading zeros
while (p != pend && *p == UC('0')) {
++p;
}
@ -509,20 +564,21 @@ parse_int_string(UC const *p, UC const *pend, T &value,
UC const *const start_digits = p;
// Parse digits
uint64_t i = 0;
if (base == 10) {
if (options.base == 10) {
loop_parse_if_eight_digits(p, pend, i); // use SIMD if possible
}
while (p != pend) {
uint8_t digit = ch_to_digit(*p);
if (digit >= base) {
uint_fast8_t const digit = ch_to_digit(*p);
if (digit >= options.base) {
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++;
}
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 (has_leading_zeros) {
@ -539,26 +595,33 @@ parse_int_string(UC const *p, UC const *pend, T &value,
answer.ptr = p;
// 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) {
answer.ec = std::errc::result_out_of_range;
return answer;
}
// this check can be eliminated for all other types, but they will all require
// 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;
return answer;
}
// check other types overflow
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;
return answer;
}
}
#ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
value = T(i);
#else
if (negative) {
#ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(push)
@ -578,6 +641,7 @@ parse_int_string(UC const *p, UC const *pend, T &value,
} else {
value = T(i);
}
#endif
answer.ec = std::errc();
return answer;

View File

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

View File

@ -23,8 +23,16 @@
#if defined(__cpp_lib_is_constant_evaluated) && \
__cpp_lib_is_constant_evaluated >= 201811L
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1
#define FASTFLOAT_CONSTEVAL consteval
#else
#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
#if defined(__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L
@ -50,4 +58,11 @@
#define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 1
#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

View File

@ -17,9 +17,9 @@ namespace fast_float {
// most significant bits and the low part corresponding to the least significant
// bits.
//
template <int bit_precision>
template <limb_t bit_precision>
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);
// For small values of q, e.g., q in [0,27], the answer is always exact
// because The line value128 firstproduct = full_multiplication(w,
@ -62,7 +62,7 @@ namespace detail {
* where
* 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;
}
} // 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.
template <typename binary>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 adjusted_mantissa
compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
int hilz = int(w >> 63) ^ 1;
compute_error_scaled(int64_t q, uint64_t w, int32_t lz) noexcept {
am_pow_t hilz = static_cast<am_pow_t>(uint64_t(w >> 63) ^ 1);
adjusted_mantissa answer;
answer.mantissa = w << hilz;
int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent();
answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 +
constexpr am_pow_t bias =
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);
return answer;
}
@ -86,7 +87,7 @@ compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
template <typename binary>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
compute_error(int64_t q, uint64_t w) noexcept {
int lz = leading_zeroes(w);
limb_t lz = leading_zeroes(w);
w <<= lz;
value128 product =
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].
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(w);
limb_t lz = leading_zeroes(w);
w <<= lz;
// The required precision is binary::mantissa_explicit_bits() + 3 because
@ -138,14 +139,14 @@ compute_float(int64_t q, uint64_t w) noexcept {
// branchless approach: value128 product = compute_product(q, w); but in
// practice, we can win big with the compute_product_approximation if its
// additional branch is easily predicted. Which is best is data specific.
int upperbit = int(product.high >> 63);
int shift = upperbit + 64 - binary::mantissa_explicit_bits() - 3;
limb_t upperbit = limb_t(product.high >> 63);
limb_t shift = limb_t(upperbit + 64 - binary::mantissa_explicit_bits() - 3);
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());
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
if (-answer.power2 + 1 >=
64) { // if we have more than 64 bits below the minimum exponent, you
@ -155,6 +156,7 @@ compute_float(int64_t q, uint64_t w) noexcept {
// result should be zero
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
answer.mantissa >>= -answer.power2 + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
@ -170,7 +172,7 @@ compute_float(int64_t q, uint64_t w) noexcept {
// subnormal, but we can only know this after rounding.
// So we only declare a subnormal if we are smaller than the threshold.
answer.power2 =
(answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits()))
(answer.mantissa < (am_mant_t(1) << binary::mantissa_explicit_bits()))
? 0
: 1;
return answer;
@ -188,18 +190,18 @@ compute_float(int64_t q, uint64_t w) noexcept {
// ... we dropped out only zeroes. But if this happened, then we can go
// back!!!
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 >>= 1;
if (answer.mantissa >= (uint64_t(2) << binary::mantissa_explicit_bits())) {
answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits());
answer.power2++; // undo previous addition
if (answer.mantissa >= (am_mant_t(2) << binary::mantissa_explicit_bits())) {
answer.mantissa = (am_mant_t(1) << binary::mantissa_explicit_bits());
++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
answer.power2 = binary::infinite_power();
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
// 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.
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int32_t
scientific_exponent(uint64_t mantissa, int32_t exponent) noexcept {
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 am_pow_t
scientific_exponent(am_mant_t mantissa, am_pow_t exponent) noexcept {
while (mantissa >= 10000) {
mantissa /= 10000;
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.
template <typename T>
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>;
constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
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();
equiv_uint bits;
#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
::memcpy(&bits, &value, sizeof(T));
#endif
@ -79,7 +83,7 @@ to_extended(T value) noexcept {
am.mantissa = bits & mantissa_mask;
} else {
// normal
am.power2 = int32_t((bits & exponent_mask) >>
am.power2 = am_pow_t((bits & exponent_mask) >>
binary_format<T>::mantissa_explicit_bits());
am.power2 -= bias;
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
@ -93,7 +97,7 @@ to_extended(T value) noexcept {
// halfway between b and b+u.
template <typename T>
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);
am.mantissa <<= 1;
am.mantissa += 1;
@ -105,14 +109,15 @@ to_extended_halfway(T value) noexcept {
template <typename T, typename callback>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
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) {
// have a denormal float
int32_t shift = -am.power2 + 1;
cb(am, std::min<int32_t>(shift, 64));
am_pow_t shift = -am.power2 + 1;
cb(am, std::min<am_pow_t>(shift, 64));
// check for round-up: if rounding-nearest carried us to the hidden bit.
am.power2 = (am.mantissa <
(uint64_t(1) << binary_format<T>::mantissa_explicit_bits()))
(am_mant_t(1) << binary_format<T>::mantissa_explicit_bits()))
? 0
: 1;
return;
@ -123,13 +128,13 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
// check for carry
if (am.mantissa >=
(uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) {
am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
am.power2++;
(am_mant_t(2) << binary_format<T>::mantissa_explicit_bits())) {
am.mantissa = (am_mant_t(1) << binary_format<T>::mantissa_explicit_bits());
++am.power2;
}
// 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()) {
am.power2 = binary_format<T>::infinite_power();
am.mantissa = 0;
@ -138,11 +143,12 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
template <typename callback>
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 {
uint64_t const mask = (shift == 64) ? UINT64_MAX : (uint64_t(1) << shift) - 1;
uint64_t const halfway = (shift == 0) ? 0 : uint64_t(1) << (shift - 1);
uint64_t truncated_bits = am.mantissa & mask;
am_mant_t const mask =
(shift == 64) ? UINT64_MAX : (am_mant_t(1) << shift) - 1;
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_halfway = truncated_bits == halfway;
@ -155,11 +161,11 @@ round_nearest_tie_even(adjusted_mantissa &am, int32_t shift,
am.power2 += shift;
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
round_down(adjusted_mantissa &am, int32_t shift) noexcept {
round_down(adjusted_mantissa &am, am_pow_t shift) noexcept {
if (shift == 64) {
am.mantissa = 0;
} else {
@ -171,9 +177,9 @@ round_down(adjusted_mantissa &am, int32_t shift) noexcept {
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
skip_zeros(UC const *&first, UC const *last) noexcept {
uint64_t val;
while (!cpp20_and_in_constexpr() &&
std::distance(first, last) >= int_cmp_len<UC>()) {
uint64_t val;
::memcpy(&val, first, sizeof(uint64_t));
if (val != int_cmp_zeros<UC>()) {
break;
@ -184,7 +190,7 @@ skip_zeros(UC const *&first, UC const *last) noexcept {
if (*first != UC('0')) {
break;
}
first++;
++first;
}
}
@ -194,9 +200,9 @@ template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
is_truncated(UC const *first, UC const *last) noexcept {
// do 8-bit optimizations, can just compare to 8 literal 0s.
uint64_t val;
while (!cpp20_and_in_constexpr() &&
std::distance(first, last) >= int_cmp_len<UC>()) {
uint64_t val;
::memcpy(&val, first, sizeof(uint64_t));
if (val != int_cmp_zeros<UC>()) {
return true;
@ -220,8 +226,8 @@ is_truncated(span<UC const> s) noexcept {
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
parse_eight_digits(UC const *&p, limb &value, size_t &counter,
size_t &count) noexcept {
parse_eight_digits(UC const *&p, limb &value, am_digits &counter,
am_digits &count) noexcept {
value = value * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
counter += 8;
@ -230,12 +236,12 @@ parse_eight_digits(UC const *&p, limb &value, size_t &counter,
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
parse_one_digit(UC const *&p, limb &value, size_t &counter,
size_t &count) noexcept {
parse_one_digit(UC const *&p, limb &value, am_digits &counter,
am_digits &count) noexcept {
value = value * 10 + limb(*p - UC('0'));
p++;
counter++;
count++;
++p;
++counter;
++count;
}
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
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
// ....9999 to ...10000, which could cause a false halfway point.
add_native(big, 10, 1);
count++;
++count;
}
// parse the significant digits into a big integer
template <typename UC>
inline FASTFLOAT_CONSTEXPR20 void
parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
size_t max_digits, size_t &digits) noexcept {
template <typename T, typename UC>
inline FASTFLOAT_CONSTEXPR20 am_digits
parse_mantissa(bigint &result, const parsed_number_string_t<UC> &num) noexcept {
// 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
// scalar value (9 or 19 digits) for each step.
size_t counter = 0;
digits = 0;
constexpr am_digits max_digits = binary_format<T>::max_digits();
am_digits counter = 0;
am_digits digits = 0;
limb value = 0;
#ifdef FASTFLOAT_64BIT_LIMB
size_t step = 19;
constexpr am_digits step = 19;
#else
size_t step = 9;
constexpr am_digits step = 9;
#endif
// process all integer digits.
@ -277,10 +283,10 @@ parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
while (p != pend) {
while ((std::distance(p, pend) >= 8) && (step - counter >= 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) {
parse_one_digit(p, value, counter, digits);
parse_one_digit<UC>(p, value, counter, digits);
}
if (digits == max_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) {
round_up_bigint(result, digits);
}
return;
return digits;
} else {
add_native(result, limb(powers_of_ten_uint64[counter]), value);
counter = 0;
@ -311,10 +317,10 @@ parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
while (p != pend) {
while ((std::distance(p, pend) >= 8) && (step - counter >= 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) {
parse_one_digit(p, value, counter, digits);
parse_one_digit<UC>(p, value, counter, digits);
}
if (digits == max_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) {
round_up_bigint(result, digits);
}
return;
return digits;
} else {
add_native(result, limb(powers_of_ten_uint64[counter]), value);
counter = 0;
@ -335,20 +341,21 @@ parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
if (counter != 0) {
add_native(result, limb(powers_of_ten_uint64[counter]), value);
}
return digits;
}
template <typename T>
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept {
FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent)));
adjusted_mantissa answer;
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa positive_digit_comp(
bigint &bigmant, adjusted_mantissa am, am_pow_t const exponent) noexcept {
FASTFLOAT_ASSERT(bigmant.pow10(exponent));
bool truncated;
answer.mantissa = bigmant.hi64(truncated);
int bias = binary_format<T>::mantissa_explicit_bits() -
am.mantissa = bigmant.hi64(truncated);
constexpr am_pow_t bias = binary_format<T>::mantissa_explicit_bits() -
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(
a, shift,
[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`,
@ -367,38 +374,42 @@ positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept {
// are of the same magnitude.
template <typename T>
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;
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;
// 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.
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;
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);
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.
int32_t pow2_exp = theor_exp - real_exp;
uint32_t pow5_exp = uint32_t(-real_exp);
am_pow_t pow2_exp = theor_exp - real_exp;
am_pow_t pow5_exp = -real_exp;
if (pow5_exp != 0) {
FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp));
}
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) {
FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp)));
FASTFLOAT_ASSERT(real_digits.pow2(-pow2_exp));
}
// compare digits, and use it to direct rounding
int ord = real_digits.compare(theor_digits);
adjusted_mantissa answer = am;
round<T>(answer, [ord](adjusted_mantissa &a, int32_t shift) {
round<T>(am, [ord](adjusted_mantissa &a, am_pow_t shift) {
round_nearest_tie_even(
a, shift, [ord](bool is_odd, bool _, bool __) -> bool {
(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
@ -430,21 +441,19 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
// the actual digits. we then compare the big integer representations
// of both, and use that to direct rounding.
template <typename T, typename UC>
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
digit_comp(parsed_number_string_t<UC> &num, adjusted_mantissa am) noexcept {
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa digit_comp(
parsed_number_string_t<UC> const &num, adjusted_mantissa am) noexcept {
// remove the invalid exponent bias
am.power2 -= invalid_am_bias;
int32_t sci_exp =
scientific_exponent(num.mantissa, static_cast<int32_t>(num.exponent));
size_t max_digits = binary_format<T>::max_digits();
size_t digits = 0;
am_pow_t const sci_exp = scientific_exponent(num.mantissa, num.exponent);
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.
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) {
return positive_digit_comp<T>(bigmant, exponent);
return positive_digit_comp<T>(bigmant, am, exponent);
} else {
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)>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
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.
@ -43,7 +43,7 @@ from_chars(UC const *first, UC const *last, T &value,
template <typename T, typename UC = char>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
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
@ -59,9 +59,11 @@ from_chars_advanced(UC const *first, UC const *last, T &value,
* `new` or `malloc`).
*/
FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept;
integer_times_pow10(uint64_t const mantissa,
int const decimal_exponent) noexcept;
FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept;
integer_times_pow10(int64_t const mantissa,
int const decimal_exponent) noexcept;
/**
* 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>
FASTFLOAT_CONSTEXPR20
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,
int const decimal_exponent) noexcept;
template <typename T>
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept;
integer_times_pow10(int64_t const mantissa,
int const decimal_exponent) noexcept;
/**
* from_chars for integer types.
@ -83,7 +87,8 @@ FASTFLOAT_CONSTEXPR20
template <typename T, typename UC = char,
typename = FASTFLOAT_ENABLE_IF(is_supported_integer_type<T>::value)>
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

View File

@ -31,14 +31,14 @@ namespace fast_float {
*/
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();
constexpr static int largest_power_of_five =
constexpr static am_pow_t largest_power_of_five =
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);
// 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,
0x9558b4661b6565f8, 0x4ac7ca59a424c507,
0xbaaee17fa23ebf76, 0x5d79bcf00d2df649,
@ -696,7 +696,7 @@ template <class unused = void> struct powers_template {
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
template <class unused>
constexpr uint64_t
constexpr am_mant_t
powers_template<unused>::power_of_five_128[number_of_entries];
#endif

View File

@ -33,26 +33,38 @@
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 {
constexpr chars_format basic_json_fmt = chars_format(1 << 5);
constexpr chars_format basic_fortran_fmt = chars_format(1 << 6);
constexpr chars_format basic_json_fmt = chars_format(1 << 4);
constexpr chars_format basic_fortran_fmt = chars_format(1 << 5);
} // namespace detail
#endif
enum class chars_format : uint64_t {
enum class chars_format : chars_format_t {
scientific = 1 << 0,
fixed = 1 << 2,
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,
fixed = 1 << 1,
general = fixed | scientific,
allow_leading_plus = 1 << 7,
skip_white_space = 1 << 8,
hex = 1 << 2,
#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 {
@ -68,16 +80,19 @@ template <typename UC> struct from_chars_result_t {
using from_chars_result = from_chars_result_t<char>;
template <typename UC> struct parse_options_t {
constexpr explicit parse_options_t(chars_format fmt = chars_format::general,
UC dot = UC('.'), int b = 10)
: format(fmt), decimal_point(dot), base(b) {}
constexpr explicit parse_options_t(
chars_format const fmt = chars_format::general, UC const dot = UC('.'),
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 */
chars_format format;
/** The character used as decimal point */
UC decimal_point;
/** The base used for integers */
int base;
uint_fast8_t base; /* only allowed from 2 to 36 */
};
using parse_options = parse_options_t<char>;
@ -218,7 +233,7 @@ using parse_options = parse_options_t<char>;
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
return std::is_constant_evaluated();
#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.
template <typename UC>
inline FASTFLOAT_CONSTEXPR14 bool
fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
size_t length) {
for (size_t i = 0; i < length; ++i) {
uint8_t const length) noexcept {
for (uint8_t i = 0; i != length; ++i) {
UC const actual = actual_mixedcase[i];
if ((actual < 256 ? actual | 32 : actual) != expected_lowercase[i]) {
return false;
@ -280,6 +296,7 @@ fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
}
return true;
}
#endif
#ifndef FLT_EVAL_METHOD
#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
template <typename T> struct span {
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);
return ptr[index];
}
@ -306,14 +324,15 @@ struct value128 {
uint64_t low;
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 */
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int
leading_zeroes_generic(uint64_t input_num, int last_bit = 0) {
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 limb_t
leading_zeroes_generic(uint64_t input_num, uint32_t last_bit = 0) noexcept {
if (input_num & uint64_t(0xffffffff00000000)) {
input_num >>= 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; */
last_bit |= 1;
}
return 63 - last_bit;
return 63 - (limb_t)last_bit;
}
/* result might be undefined when input_num is zero */
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 int
leading_zeroes(uint64_t input_num) {
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb_t
leading_zeroes(uint64_t input_num) noexcept {
assert(input_num > 0);
FASTFLOAT_ASSUME(input_num > 0);
if (cpp20_and_in_constexpr()) {
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)
// to least significant bit (LSB) for a set bit (1).
_BitScanReverse64(&leading_zero, input_num);
return (int)(63 - leading_zero);
return (limb_t)(63 - leading_zero);
#else
return leading_zeroes_generic(input_num);
return (limb_t)leading_zeroes_generic(input_num);
#endif
#else
return __builtin_clzll(input_num);
return (limb_t)__builtin_clzll(input_num);
#endif
}
// 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;
}
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 bd = emulu((uint32_t)ab, (uint32_t)cd);
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
#if !defined(__MINGW64__)
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t _umul128(uint64_t ab,
uint64_t cd,
uint64_t *hi) {
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t
_umul128(uint64_t ab, uint64_t cd, uint64_t *hi) noexcept {
return umul128_generic(ab, cd, hi);
}
#endif // !__MINGW64__
@ -394,7 +414,7 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t _umul128(uint64_t ab,
// compute 64-bit a*b
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()) {
value128 answer;
answer.low = umul128_generic(a, b, &answer.high);
@ -419,47 +439,56 @@ full_multiplication(uint64_t a, uint64_t b) {
return answer;
}
struct adjusted_mantissa {
uint64_t mantissa{0};
int32_t power2{0}; // a negative value indicates an invalid result
adjusted_mantissa() = default;
// Value of the mantissa.
typedef uint_fast64_t am_mant_t;
// Size of bits in the mantissa and path and rounding shifts
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;
}
constexpr bool operator!=(adjusted_mantissa const &o) const {
constexpr bool operator!=(adjusted_mantissa const &o) const noexcept {
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
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> struct binary_format : binary_format_lookup_tables<T> {
using equiv_uint = equiv_uint_t<T>;
static constexpr int mantissa_explicit_bits();
static constexpr int minimum_exponent();
static constexpr int infinite_power();
static constexpr int sign_index();
static constexpr int
static constexpr limb_t mantissa_explicit_bits();
static constexpr am_pow_t minimum_exponent();
static constexpr am_pow_t infinite_power();
static constexpr am_bits_t sign_index();
static constexpr am_bits_t
min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST
static constexpr int max_exponent_fast_path();
static constexpr int max_exponent_round_to_even();
static constexpr int min_exponent_round_to_even();
static constexpr uint64_t max_mantissa_fast_path(int64_t power);
static constexpr uint64_t
static constexpr am_bits_t max_exponent_fast_path();
static constexpr am_bits_t max_exponent_round_to_even();
static constexpr am_bits_t min_exponent_round_to_even();
static constexpr equiv_uint max_mantissa_fast_path(am_pow_t power);
static constexpr equiv_uint
max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST
static constexpr int largest_power_of_ten();
static constexpr int smallest_power_of_ten();
static constexpr T exact_power_of_ten(int64_t power);
static constexpr size_t max_digits();
static constexpr am_pow_t largest_power_of_ten();
static constexpr am_pow_t smallest_power_of_ten();
static constexpr T exact_power_of_ten(am_pow_t power);
static constexpr am_digits max_digits();
static constexpr equiv_uint exponent_mask();
static constexpr equiv_uint mantissa_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.
// 0x1000000 == 1<<24
static constexpr uint64_t max_mantissa[] = {
static constexpr uint32_t max_mantissa[] = {
0x1000000,
0x1000000 / 5,
0x1000000 / (5 * 5),
@ -544,12 +573,12 @@ template <typename U>
constexpr float binary_format_lookup_tables<float, U>::powers_of_ten[];
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
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)
return 0;
#else
@ -558,7 +587,7 @@ inline constexpr int binary_format<double>::min_exponent_fast_path() {
}
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)
return 0;
#else
@ -567,77 +596,78 @@ inline constexpr int binary_format<float>::min_exponent_fast_path() {
}
template <>
inline constexpr int binary_format<double>::mantissa_explicit_bits() {
inline constexpr limb_t binary_format<double>::mantissa_explicit_bits() {
return 52;
}
template <>
inline constexpr int binary_format<float>::mantissa_explicit_bits() {
inline constexpr limb_t binary_format<float>::mantissa_explicit_bits() {
return 23;
}
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;
}
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;
}
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;
}
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;
}
template <> inline constexpr int binary_format<double>::minimum_exponent() {
template <>
inline constexpr am_pow_t binary_format<double>::minimum_exponent() {
return -1023;
}
template <> inline constexpr int binary_format<float>::minimum_exponent() {
template <> inline constexpr am_pow_t binary_format<float>::minimum_exponent() {
return -127;
}
template <> inline constexpr int binary_format<double>::infinite_power() {
template <> inline constexpr am_pow_t binary_format<double>::infinite_power() {
return 0x7FF;
}
template <> inline constexpr int binary_format<float>::infinite_power() {
template <> inline constexpr am_pow_t binary_format<float>::infinite_power() {
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;
}
template <> inline constexpr int binary_format<float>::sign_index() {
template <> inline constexpr am_bits_t binary_format<float>::sign_index() {
return 31;
}
#endif
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;
}
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;
}
template <>
inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() {
return uint64_t(2) << mantissa_explicit_bits();
}
template <>
inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() {
return uint64_t(2) << mantissa_explicit_bits();
template <typename T>
inline constexpr typename binary_format<T>::equiv_uint
binary_format<T>::max_mantissa_fast_path() {
return binary_format<T>::equiv_uint(2) << mantissa_explicit_bits();
}
// 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.
// 0x800 == 1<<11
static constexpr uint64_t max_mantissa[] = {0x800,
static constexpr uint16_t max_mantissa[] = {0x800,
0x800 / 5,
0x800 / (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[];
template <typename U>
constexpr uint64_t
constexpr uint16_t
binary_format_lookup_tables<std::float16_t, U>::max_mantissa[];
#endif
template <>
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
return (void)powers_of_ten[0], powers_of_ten[power];
}
@ -694,74 +724,78 @@ binary_format<std::float16_t>::hidden_bit_mask() {
}
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;
}
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;
}
template <>
inline constexpr uint64_t
binary_format<std::float16_t>::max_mantissa_fast_path() {
return uint64_t(2) << mantissa_explicit_bits();
}
template <>
inline constexpr uint64_t
binary_format<std::float16_t>::max_mantissa_fast_path(int64_t power) {
inline constexpr binary_format<std::float16_t>::equiv_uint
binary_format<std::float16_t>::max_mantissa_fast_path(am_pow_t power) {
// 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
return (void)max_mantissa[0], max_mantissa[power];
}
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;
}
template <>
inline constexpr int
inline constexpr am_bits_t
binary_format<std::float16_t>::max_exponent_round_to_even() {
return 5;
}
template <>
inline constexpr int
inline constexpr am_bits_t
binary_format<std::float16_t>::min_exponent_round_to_even() {
return -22;
}
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;
}
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;
}
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;
}
#endif
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;
}
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;
}
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;
}
#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.
// 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 * 5)};
};
@ -793,13 +827,14 @@ constexpr uint64_t
template <>
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
return (void)powers_of_ten[0], powers_of_ten[power];
}
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;
}
@ -822,88 +857,91 @@ binary_format<std::bfloat16_t>::hidden_bit_mask() {
}
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;
}
template <>
inline constexpr uint64_t
binary_format<std::bfloat16_t>::max_mantissa_fast_path() {
return uint64_t(2) << mantissa_explicit_bits();
}
template <>
inline constexpr uint64_t
binary_format<std::bfloat16_t>::max_mantissa_fast_path(int64_t power) {
inline constexpr binary_format<std::bfloat16_t>::equiv_uint
binary_format<std::bfloat16_t>::max_mantissa_fast_path(am_pow_t power) {
// 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
return (void)max_mantissa[0], max_mantissa[power];
}
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;
}
template <>
inline constexpr int
inline constexpr am_bits_t
binary_format<std::bfloat16_t>::max_exponent_round_to_even() {
return 3;
}
template <>
inline constexpr int
inline constexpr am_bits_t
binary_format<std::bfloat16_t>::min_exponent_round_to_even() {
return -24;
}
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;
}
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;
}
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;
}
#endif
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;
}
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;
}
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;
}
#endif // __STDCPP_BFLOAT16_T__
template <>
inline constexpr uint64_t
binary_format<double>::max_mantissa_fast_path(int64_t power) {
inline constexpr binary_format<double>::equiv_uint
binary_format<double>::max_mantissa_fast_path(am_pow_t power) {
// 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
return (void)max_mantissa[0], max_mantissa[power];
}
template <>
inline constexpr uint64_t
binary_format<float>::max_mantissa_fast_path(int64_t power) {
inline constexpr binary_format<float>::equiv_uint
binary_format<float>::max_mantissa_fast_path(am_pow_t power) {
// 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
return (void)max_mantissa[0], max_mantissa[power];
@ -911,39 +949,49 @@ binary_format<float>::max_mantissa_fast_path(int64_t power) {
template <>
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
return (void)powers_of_ten[0], powers_of_ten[power];
}
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
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;
}
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;
}
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;
}
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;
}
template <> inline constexpr size_t binary_format<double>::max_digits() {
template <> inline constexpr am_digits binary_format<double>::max_digits() {
return 769;
}
template <> inline constexpr size_t binary_format<float>::max_digits() {
template <> inline constexpr am_digits binary_format<float>::max_digits() {
return 114;
}
@ -984,23 +1032,34 @@ binary_format<double>::hidden_bit_mask() {
}
template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
to_float(bool negative, adjusted_mantissa am, T &value) {
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void to_float(
#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>;
equiv_uint word = equiv_uint(am.mantissa);
word = equiv_uint(word | equiv_uint(am.power2)
<< binary_format<T>::mantissa_explicit_bits());
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
word =
equiv_uint(word | equiv_uint(negative) << binary_format<T>::sign_index());
#endif
#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
::memcpy(&value, &word, sizeof(T));
#endif
}
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
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, 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,
@ -1016,7 +1075,7 @@ template <typename = void> struct space_lut {
#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
@ -1024,6 +1083,8 @@ template <typename UC> constexpr bool is_space(UC c) {
return c < 256 && space_lut<>::value[uint8_t(c)];
}
#endif
template <typename UC> static constexpr uint64_t int_cmp_zeros() {
static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4),
"Unsupported character size");
@ -1038,6 +1099,8 @@ template <typename UC> static constexpr int int_cmp_len() {
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 <> 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
template <typename = void> struct int_luts {
static constexpr uint8_t chdigit[] = {
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};
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,
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 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[];
#endif
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.
using UnsignedUC = typename std::make_unsigned<UC>::type;
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)))];
}
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];
}
// If a u64 is exactly max_digits_u64() in length, this is
// 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];
}
@ -1238,19 +1305,6 @@ operator^=(chars_format &lhs, chars_format rhs) noexcept {
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
#endif

View File

@ -14,6 +14,7 @@
namespace fast_float {
namespace detail {
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
/**
* Special case +inf, -inf, nan, infinity, -infinity.
* 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>
from_chars_result_t<UC>
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{};
answer.ptr = first;
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('-'));
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here
if ((*first == UC('-')) ||
(uint64_t(fmt & chars_format::allow_leading_plus) &&
((chars_format_t(fmt & chars_format::allow_leading_plus)) &&
(*first == UC('+')))) {
++first;
}
if (last - first >= 3) {
if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
answer.ptr = (first += 3);
@ -69,7 +73,9 @@ from_chars_result_t<UC>
answer.ec = std::errc::invalid_argument;
return answer;
}
#endif
#ifndef FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
/**
* 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.
@ -134,6 +140,7 @@ fastfloat_really_inline bool rounds_to_nearest() noexcept {
#pragma GCC diagnostic pop
#endif
}
#endif
} // namespace detail
@ -141,7 +148,7 @@ template <typename T> struct from_chars_caller {
template <typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
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);
}
};
@ -151,7 +158,7 @@ template <> struct from_chars_caller<std::float32_t> {
template <typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
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
// float32; set value to float due to equivalence between float and
// float32_t
@ -168,7 +175,7 @@ template <> struct from_chars_caller<std::float64_t> {
template <typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
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
// float64; set value as double due to equivalence between double and
// float64_t
@ -183,15 +190,19 @@ template <> struct from_chars_caller<std::float64_t> {
template <typename T, typename UC, typename>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
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,
parse_options_t<UC>(fmt));
}
template <typename T>
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 {
// The implementation of the Clinger's fast path is convoluted because
// we want round-to-nearest in all cases, irrespective of the rounding mode
// selected on the thread.
@ -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
// there might be performance advantages at having the check
// be last.
#ifndef FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) {
#endif
// We have that fegetround() == FE_TONEAREST.
// Next is Clinger's 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 {
value = value * binary_format<T>::exact_power_of_ten(exponent);
}
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (is_negative) {
value = -value;
}
#endif
return true;
}
#ifndef FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
} else {
// We do not have that fegetround() == FE_TONEAREST.
// 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)
// Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
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;
}
#endif
value = T(mantissa) * binary_format<T>::exact_power_of_ten(exponent);
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (is_negative) {
value = -value;
}
#endif
return true;
}
}
#endif
}
return false;
}
@ -252,7 +275,7 @@ clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
*/
template <typename T, typename 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,
"only some floating-point types are supported");
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.ptr = pns.lastmatch;
if (!pns.too_many_digits &&
clinger_fast_path_impl(pns.mantissa, pns.exponent, pns.negative, value))
if (!pns.too_many_digits && clinger_fast_path_impl(pns.mantissa, pns.exponent,
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
pns.negative,
#endif
value))
return answer;
adjusted_mantissa am =
@ -280,7 +306,11 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
if (am.power2 < 0) {
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.
if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) ||
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>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
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,
"only some floating-point types are supported");
static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported");
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
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)) {
first++;
++first;
}
}
if (first == last) {
@ -312,18 +341,29 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value,
answer.ptr = first;
return answer;
}
parsed_number_string_t<UC> pns =
uint64_t(fmt & detail::basic_json_fmt)
#else
// 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<false, UC>(first, last, options);
if (!pns.valid) {
if (uint64_t(fmt & chars_format::no_infnan)) {
:
#endif
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.ptr = first;
return answer;
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
} 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.
@ -332,55 +372,81 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value,
template <typename T, typename UC, typename>
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,
"only integer types are supported");
static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported");
parse_options_t<UC> options;
options.base = base;
parse_options_t<UC> const options(chars_format::general, UC('.'),
static_cast<uint_fast8_t>(base));
return from_chars_advanced(first, last, value, options);
}
template <typename T>
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept {
integer_times_pow10(uint64_t const mantissa,
int const decimal_exponent) noexcept {
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;
adjusted_mantissa am =
compute_float<binary_format<T>>(decimal_exponent, mantissa);
to_float(false, am, value);
compute_float<binary_format<double>>(decimal_exponent, mantissa);
to_float(
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
false,
#endif
am, value);
return value;
}
template <typename T>
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept {
integer_times_pow10(int64_t const mantissa,
int const decimal_exponent) noexcept {
#ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
FASTFLOAT_ASSUME(mantissa > 0);
const am_mant_t m = static_cast<am_mant_t>(mantissa);
#else
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;
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;
adjusted_mantissa am = compute_float<binary_format<T>>(decimal_exponent, m);
to_float(is_negative, am, value);
adjusted_mantissa am =
compute_float<binary_format<double>>(decimal_exponent, m);
to_float(
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
is_negative,
#endif
am, value);
return value;
}
FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept {
integer_times_pow10(uint64_t const mantissa,
int const decimal_exponent) noexcept {
return integer_times_pow10<double>(mantissa, decimal_exponent);
}
FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept {
integer_times_pow10(int64_t const mantissa,
int const decimal_exponent) noexcept {
return integer_times_pow10<double>(mantissa, decimal_exponent);
}
@ -425,23 +491,30 @@ integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
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,
"only integer types are supported");
static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported");
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
int const base = options.base;
from_chars_result_t<UC> answer;
if (uint64_t(fmt & chars_format::skip_white_space)) {
#ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
// We are in parser code with external loop that checks bounds.
FASTFLOAT_ASSUME(first < last);
// base is already checked in the parse_options_t constructor.
#else
if (chars_format_t(options.format & chars_format::skip_white_space)) {
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.ptr = first;
return answer;
@ -458,7 +531,7 @@ template <> struct from_chars_advanced_caller<1> {
template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
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);
}
};
@ -467,7 +540,7 @@ template <> struct from_chars_advanced_caller<2> {
template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
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);
}
};
@ -475,7 +548,7 @@ template <> struct from_chars_advanced_caller<2> {
template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
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<
size_t(is_supported_float_type<T>::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();
}
char const *round_name(int d) {
const std::string_view round_name(int const d) {
switch (d) {
case FE_UPWARD:
return "FE_UPWARD";
@ -2126,8 +2126,9 @@ TEST_CASE("bfloat16.general") {
#endif
template <typename Int, typename T, typename U>
void verify_integer_times_pow10_result(Int mantissa, int decimal_exponent,
T actual, U expected) {
void verify_integer_times_pow10_result(Int const mantissa,
int const decimal_exponent,
T const actual, U const expected) {
static_assert(std::is_same<T, U>::value,
"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>
T calculate_integer_times_pow10_expected_result(Int mantissa,
int decimal_exponent) {
T calculate_integer_times_pow10_expected_result(Int const mantissa,
int const decimal_exponent) {
std::string constructed_string =
std::to_string(mantissa) + "e" + std::to_string(decimal_exponent);
T expected_result;
@ -2158,8 +2159,9 @@ T calculate_integer_times_pow10_expected_result(Int mantissa,
}
template <typename Int>
void verify_integer_times_pow10_dflt(Int mantissa, int decimal_exponent,
double expected) {
void verify_integer_times_pow10_dflt(Int const mantissa,
int const decimal_exponent,
double const expected) {
static_assert(std::is_integral<Int>::value);
// the "default" overload
@ -2171,7 +2173,8 @@ void verify_integer_times_pow10_dflt(Int mantissa, int decimal_exponent,
}
template <typename Int>
void verify_integer_times_pow10_dflt(Int mantissa, int decimal_exponent) {
void verify_integer_times_pow10_dflt(Int const mantissa,
int const decimal_exponent) {
static_assert(std::is_integral<Int>::value);
const auto expected_result =
@ -2182,8 +2185,8 @@ void verify_integer_times_pow10_dflt(Int mantissa, int decimal_exponent) {
}
template <typename T, typename Int>
void verify_integer_times_pow10(Int mantissa, int decimal_exponent,
T expected) {
void verify_integer_times_pow10(Int const mantissa, int const decimal_exponent,
T const expected) {
static_assert(std::is_floating_point<T>::value);
static_assert(std::is_integral<Int>::value);
@ -2196,7 +2199,8 @@ void verify_integer_times_pow10(Int mantissa, int decimal_exponent,
}
template <typename T, typename Int>
void verify_integer_times_pow10(Int mantissa, int decimal_exponent) {
void verify_integer_times_pow10(Int const mantissa,
int const decimal_exponent) {
static_assert(std::is_floating_point<T>::value);
static_assert(std::is_integral<Int>::value);
@ -2208,7 +2212,8 @@ void verify_integer_times_pow10(Int mantissa, int decimal_exponent) {
namespace all_supported_types {
template <typename Int>
void verify_integer_times_pow10(Int mantissa, int decimal_exponent) {
void verify_integer_times_pow10(Int const mantissa,
int const decimal_exponent) {
static_assert(std::is_integral<Int>::value);
// verify the "default" overload
@ -2313,7 +2318,7 @@ TEST_CASE("integer_times_pow10") {
for (int mode : {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, FE_TONEAREST}) {
fesetround(mode);
INFO("fesetround(): " << std::string{round_name(mode)});
INFO("fesetround(): " << round_name(mode));
struct Guard {
~Guard() { fesetround(FE_TONEAREST); }

View File

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

View File

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