mirror of
https://github.com/fastfloat/fast_float.git
synced 2025-12-06 16:56:57 +08:00
Merge 3ae6d3c2b3e93ec48af7f4ce7d540518efc3b323 into 7b21183a93c4a8943a2d384f207537d7330547e1
This commit is contained in:
commit
1396a88bb9
2
.github/workflows/lint_and_format_check.yml
vendored
2
.github/workflows/lint_and_format_check.yml
vendored
@ -27,7 +27,7 @@ jobs:
|
|||||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.1.7
|
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.1.7
|
||||||
|
|
||||||
- name: Run clang-format
|
- name: Run clang-format
|
||||||
uses: jidicula/clang-format-action@4726374d1aa3c6aecf132e5197e498979588ebc8 # v4.15.0
|
uses: jidicula/clang-format-action@6cd220de46c89139a0365edae93eee8eb30ca8fe # v4.16.0
|
||||||
with:
|
with:
|
||||||
clang-format-version: '17'
|
clang-format-version: '17'
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/vs17-ci.yml
vendored
2
.github/workflows/vs17-ci.yml
vendored
@ -11,7 +11,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- {gen: Visual Studio 17 2022, arch: Win32, cfg: Release}
|
- {gen: Visual Studio 17 2022, arch: Win32, cfg: Release}
|
||||||
#- {gen: Visual Studio 17 2022, arch: Win32, cfg: Debug}
|
- {gen: Visual Studio 17 2022, arch: Win32, cfg: Debug}
|
||||||
- {gen: Visual Studio 17 2022, arch: x64, cfg: Release}
|
- {gen: Visual Studio 17 2022, arch: x64, cfg: Release}
|
||||||
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
|
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@ -9,3 +9,4 @@ Jan Pharago
|
|||||||
Maya Warrier
|
Maya Warrier
|
||||||
Taha Khokhar
|
Taha Khokhar
|
||||||
Anders Dalvander
|
Anders Dalvander
|
||||||
|
Elle Solomina
|
||||||
|
|||||||
34
README.md
34
README.md
@ -35,7 +35,7 @@ struct from_chars_result {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
It parses the character sequence `[first, last)` for a number. It parses
|
It parses the character sequence `[first, last]` for a number. It parses
|
||||||
floating-point numbers expecting a locale-independent format equivalent to the
|
floating-point numbers expecting a locale-independent format equivalent to the
|
||||||
C++17 from_chars function. The resulting floating-point value is the closest
|
C++17 from_chars function. The resulting floating-point value is the closest
|
||||||
floating-point values (using either `float` or `double`), using the "round to
|
floating-point values (using either `float` or `double`), using the "round to
|
||||||
@ -48,7 +48,8 @@ parsed value. In case of error, the returned `ec` contains a representative
|
|||||||
error, otherwise the default (`std::errc()`) value is stored.
|
error, otherwise the default (`std::errc()`) value is stored.
|
||||||
|
|
||||||
The implementation does not throw and does not allocate memory (e.g., with `new`
|
The implementation does not throw and does not allocate memory (e.g., with `new`
|
||||||
or `malloc`).
|
or `malloc`) and can be usable in the kernel, embeded and other scenarious that
|
||||||
|
relays on such behavior.
|
||||||
|
|
||||||
It will parse infinity and nan values.
|
It will parse infinity and nan values.
|
||||||
|
|
||||||
@ -288,7 +289,7 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Advanced options: using commas as decimal separator, JSON and Fortran
|
## Advanced options: using commas as decimal separator, parse JSON, Fortran and more
|
||||||
|
|
||||||
The C++ standard stipulate that `from_chars` has to be locale-independent. In
|
The C++ standard stipulate that `from_chars` has to be locale-independent. In
|
||||||
particular, the decimal separator has to be the period (`.`). However, some
|
particular, the decimal separator has to be the period (`.`). However, some
|
||||||
@ -377,6 +378,32 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## You also can also use some additional options (currently only configure by macroses):
|
||||||
|
|
||||||
|
There is a really common use case in mathematical and other abstract syntax tree (AST) like parsers,
|
||||||
|
that already process sign and all other symbols before any number by itself. In this case you can
|
||||||
|
use FastFloat for only parse positive numbers in all supported formats with macros
|
||||||
|
`FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN`, that significantly reduce the code size and improve
|
||||||
|
performance. Additionally you can use macros `FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED` if you
|
||||||
|
only uneed `FE_TONEAREST` rounding mode in the parsing: this option is also improve performance a bit.
|
||||||
|
|
||||||
|
```C++
|
||||||
|
#define FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
#define FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
|
||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::string input = "23.14069263277926900572";
|
||||||
|
double result;
|
||||||
|
auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result);
|
||||||
|
if ((answer.ec != std::errc()) || ((result != 23.14069263277927 /*properly rounded value */)))
|
||||||
|
{ std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Multiplication of an integer by a power of 10
|
## Multiplication of an integer by a power of 10
|
||||||
An integer `W` can be multiplied by a power of ten `10^Q` and
|
An integer `W` can be multiplied by a power of ten `10^Q` and
|
||||||
converted to `double` with correctly rounded value
|
converted to `double` with correctly rounded value
|
||||||
@ -421,7 +448,6 @@ float: 12345678 * 10^23 = 1.23456782e+30 (==expected)
|
|||||||
Overloads of `fast_float::integer_times_pow10()` are provided for
|
Overloads of `fast_float::integer_times_pow10()` are provided for
|
||||||
signed and unsigned integer types: `int64_t`, `uint64_t`, etc.
|
signed and unsigned integer types: `int64_t`, `uint64_t`, etc.
|
||||||
|
|
||||||
|
|
||||||
## Users and Related Work
|
## Users and Related Work
|
||||||
|
|
||||||
The fast_float library is part of:
|
The fast_float library is part of:
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
|
|
||||||
|
// #define FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
// #define FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
|
||||||
|
|
||||||
#if defined(__linux__) || (__APPLE__ && __aarch64__)
|
#if defined(__linux__) || (__APPLE__ && __aarch64__)
|
||||||
#define USING_COUNTERS
|
#define USING_COUNTERS
|
||||||
#endif
|
#endif
|
||||||
#include "event_counter.h"
|
#include "event_counter.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "fast_float/fast_float.h"
|
|
||||||
#include <chrono>
|
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@ -19,15 +21,17 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
|
|
||||||
template <typename CharT>
|
#include "fast_float/fast_float.h"
|
||||||
double findmax_fastfloat64(std::vector<std::basic_string<CharT>> &s) {
|
|
||||||
double answer = 0;
|
template <typename CharT, typename Value>
|
||||||
double x = 0;
|
Value findmax_fastfloat(std::vector<std::basic_string<CharT>> &s) {
|
||||||
|
Value answer = 0;
|
||||||
|
Value x = 0;
|
||||||
for (auto &st : s) {
|
for (auto &st : s) {
|
||||||
auto [p, ec] = fast_float::from_chars(st.data(), st.data() + st.size(), x);
|
auto [p, ec] = fast_float::from_chars(st.data(), st.data() + st.size(), x);
|
||||||
|
|
||||||
if (p == st.data()) {
|
if (p == st.data()) {
|
||||||
throw std::runtime_error("bug in findmax_fastfloat");
|
throw std::runtime_error("bug in findmax_fastfloat");
|
||||||
}
|
}
|
||||||
@ -36,23 +40,10 @@ double findmax_fastfloat64(std::vector<std::basic_string<CharT>> &s) {
|
|||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename CharT>
|
#ifdef USING_COUNTERS
|
||||||
double findmax_fastfloat32(std::vector<std::basic_string<CharT>> &s) {
|
|
||||||
float answer = 0;
|
|
||||||
float x = 0;
|
|
||||||
for (auto &st : s) {
|
|
||||||
auto [p, ec] = fast_float::from_chars(st.data(), st.data() + st.size(), x);
|
|
||||||
if (p == st.data()) {
|
|
||||||
throw std::runtime_error("bug in findmax_fastfloat");
|
|
||||||
}
|
|
||||||
answer = answer > x ? answer : x;
|
|
||||||
}
|
|
||||||
return answer;
|
|
||||||
}
|
|
||||||
|
|
||||||
event_collector collector{};
|
event_collector collector{};
|
||||||
|
|
||||||
#ifdef USING_COUNTERS
|
|
||||||
template <class T, class CharT>
|
template <class T, class CharT>
|
||||||
std::vector<event_count>
|
std::vector<event_count>
|
||||||
time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
|
time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
|
||||||
@ -61,7 +52,7 @@ time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
|
|||||||
bool printed_bug = false;
|
bool printed_bug = false;
|
||||||
for (size_t i = 0; i < repeat; i++) {
|
for (size_t i = 0; i < repeat; i++) {
|
||||||
collector.start();
|
collector.start();
|
||||||
double ts = function(lines);
|
auto const ts = function(lines);
|
||||||
if (ts == 0 && !printed_bug) {
|
if (ts == 0 && !printed_bug) {
|
||||||
printf("bug\n");
|
printf("bug\n");
|
||||||
printed_bug = true;
|
printed_bug = true;
|
||||||
@ -71,7 +62,7 @@ time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
|
|||||||
return aggregate;
|
return aggregate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pretty_print(double volume, size_t number_of_floats, std::string name,
|
void pretty_print(size_t volume, size_t number_of_floats, std::string name,
|
||||||
std::vector<event_count> events) {
|
std::vector<event_count> events) {
|
||||||
double volumeMB = volume / (1024. * 1024.);
|
double volumeMB = volume / (1024. * 1024.);
|
||||||
double average_ns{0};
|
double average_ns{0};
|
||||||
@ -141,14 +132,14 @@ time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
|
|||||||
bool printed_bug = false;
|
bool printed_bug = false;
|
||||||
for (size_t i = 0; i < repeat; i++) {
|
for (size_t i = 0; i < repeat; i++) {
|
||||||
t1 = std::chrono::high_resolution_clock::now();
|
t1 = std::chrono::high_resolution_clock::now();
|
||||||
double ts = function(lines);
|
auto const ts = function(lines);
|
||||||
if (ts == 0 && !printed_bug) {
|
if (ts == 0 && !printed_bug) {
|
||||||
printf("bug\n");
|
printf("bug\n");
|
||||||
printed_bug = true;
|
printed_bug = true;
|
||||||
}
|
}
|
||||||
t2 = std::chrono::high_resolution_clock::now();
|
t2 = std::chrono::high_resolution_clock::now();
|
||||||
double dif =
|
double const dif = static_cast<double>(
|
||||||
std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count();
|
std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count());
|
||||||
average += dif;
|
average += dif;
|
||||||
min_value = min_value < dif ? min_value : dif;
|
min_value = min_value < dif ? min_value : dif;
|
||||||
}
|
}
|
||||||
@ -156,8 +147,8 @@ time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
|
|||||||
return std::make_pair(min_value, average);
|
return std::make_pair(min_value, average);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pretty_print(double volume, size_t number_of_floats, std::string name,
|
void pretty_print(size_t volume, size_t number_of_floats,
|
||||||
std::pair<double, double> result) {
|
std::string const &name, std::pair<double, double> result) {
|
||||||
double volumeMB = volume / (1024. * 1024.);
|
double volumeMB = volume / (1024. * 1024.);
|
||||||
printf("%-40s: %8.2f MB/s (+/- %.1f %%) ", name.data(),
|
printf("%-40s: %8.2f MB/s (+/- %.1f %%) ", name.data(),
|
||||||
volumeMB * 1000000000 / result.first,
|
volumeMB * 1000000000 / result.first,
|
||||||
@ -168,7 +159,7 @@ void pretty_print(double volume, size_t number_of_floats, std::string name,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// this is okay, all chars are ASCII
|
// this is okay, all chars are ASCII
|
||||||
inline std::u16string widen(std::string line) {
|
inline std::u16string widen(std::string const &line) {
|
||||||
std::u16string u16line;
|
std::u16string u16line;
|
||||||
u16line.resize(line.size());
|
u16line.resize(line.size());
|
||||||
for (size_t i = 0; i < line.size(); ++i) {
|
for (size_t i = 0; i < line.size(); ++i) {
|
||||||
@ -181,28 +172,29 @@ std::vector<std::u16string> widen(const std::vector<std::string> &lines) {
|
|||||||
std::vector<std::u16string> u16lines;
|
std::vector<std::u16string> u16lines;
|
||||||
u16lines.reserve(lines.size());
|
u16lines.reserve(lines.size());
|
||||||
for (auto const &line : lines) {
|
for (auto const &line : lines) {
|
||||||
u16lines.push_back(widen(line));
|
u16lines.emplace_back(widen(line));
|
||||||
}
|
}
|
||||||
return u16lines;
|
return u16lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
void process(std::vector<std::string> &lines, size_t volume) {
|
void process(std::vector<std::string> &lines, size_t volume) {
|
||||||
size_t repeat = 1000;
|
size_t const repeat = 1000;
|
||||||
double volumeMB = volume / (1024. * 1024.);
|
double volumeMB = volume / (1024. * 1024.);
|
||||||
std::cout << "ASCII volume = " << volumeMB << " MB " << std::endl;
|
std::cout << "ASCII volume = " << volumeMB << " MB " << std::endl;
|
||||||
pretty_print(volume, lines.size(), "fastfloat (64)",
|
pretty_print(volume, lines.size(), "fastfloat (64)",
|
||||||
time_it_ns(lines, findmax_fastfloat64<char>, repeat));
|
time_it_ns(lines, findmax_fastfloat<char, double>, repeat));
|
||||||
pretty_print(volume, lines.size(), "fastfloat (32)",
|
pretty_print(volume, lines.size(), "fastfloat (32)",
|
||||||
time_it_ns(lines, findmax_fastfloat32<char>, repeat));
|
time_it_ns(lines, findmax_fastfloat<char, float>, repeat));
|
||||||
|
|
||||||
std::vector<std::u16string> lines16 = widen(lines);
|
std::vector<std::u16string> lines16 = widen(lines);
|
||||||
volume = 2 * volume;
|
volume = 2 * volume;
|
||||||
volumeMB = volume / (1024. * 1024.);
|
volumeMB = volume / (1024. * 1024.);
|
||||||
std::cout << "UTF-16 volume = " << volumeMB << " MB " << std::endl;
|
std::cout << "UTF-16 volume = " << volumeMB << " MB " << std::endl;
|
||||||
pretty_print(volume, lines.size(), "fastfloat (64)",
|
pretty_print(
|
||||||
time_it_ns(lines16, findmax_fastfloat64<char16_t>, repeat));
|
volume, lines.size(), "fastfloat (64)",
|
||||||
|
time_it_ns(lines16, findmax_fastfloat<char16_t, double>, repeat));
|
||||||
pretty_print(volume, lines.size(), "fastfloat (32)",
|
pretty_print(volume, lines.size(), "fastfloat (32)",
|
||||||
time_it_ns(lines16, findmax_fastfloat32<char16_t>, repeat));
|
time_it_ns(lines16, findmax_fastfloat<char16_t, float>, repeat));
|
||||||
}
|
}
|
||||||
|
|
||||||
void fileload(std::string filename) {
|
void fileload(std::string filename) {
|
||||||
@ -216,17 +208,31 @@ void fileload(std::string filename) {
|
|||||||
std::cout << "#### " << std::endl;
|
std::cout << "#### " << std::endl;
|
||||||
std::string line;
|
std::string line;
|
||||||
std::vector<std::string> lines;
|
std::vector<std::string> lines;
|
||||||
lines.reserve(10000); // let us reserve plenty of memory.
|
lines.reserve(120000); // let us reserve plenty of memory.
|
||||||
size_t volume = 0;
|
size_t volume = 0;
|
||||||
while (getline(inputfile, line)) {
|
while (getline(inputfile, line)) {
|
||||||
|
#ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
if (line[0] == '-') {
|
||||||
|
line.erase(0, 1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
lines.emplace_back(line);
|
||||||
volume += line.size();
|
volume += line.size();
|
||||||
lines.push_back(line);
|
|
||||||
}
|
}
|
||||||
std::cout << "# read " << lines.size() << " lines " << std::endl;
|
std::cout << "# read " << lines.size() << " lines " << std::endl;
|
||||||
process(lines, volume);
|
process(lines, volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
|
#ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
std::cout << "# FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN is enabled"
|
||||||
|
<< std::endl;
|
||||||
|
#endif
|
||||||
|
#ifdef FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
|
||||||
|
std::cout << "# FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED is enabled"
|
||||||
|
<< std::endl;
|
||||||
|
#endif
|
||||||
|
#ifdef USING_COUNTERS
|
||||||
if (collector.has_events()) {
|
if (collector.has_events()) {
|
||||||
std::cout << "# Using hardware counters" << std::endl;
|
std::cout << "# Using hardware counters" << std::endl;
|
||||||
} else {
|
} else {
|
||||||
@ -236,10 +242,12 @@ int main(int argc, char **argv) {
|
|||||||
<< std::endl;
|
<< std::endl;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
fileload(argv[1]);
|
fileload(argv[1]);
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
fileload(std::string(BENCHMARK_DATA_DIR) + "/canada.txt");
|
fileload(std::string(BENCHMARK_DATA_DIR) + "/canada.txt");
|
||||||
fileload(std::string(BENCHMARK_DATA_DIR) + "/mesh.txt");
|
fileload(std::string(BENCHMARK_DATA_DIR) + "/mesh.txt");
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <vector>
|
#include <array>
|
||||||
|
|
||||||
#include "linux-perf-events.h"
|
#include "linux-perf-events.h"
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
@ -22,26 +22,28 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct event_count {
|
struct event_count {
|
||||||
std::chrono::duration<double> elapsed;
|
|
||||||
std::vector<unsigned long long> event_counts;
|
|
||||||
|
|
||||||
event_count() : elapsed(0), event_counts{0, 0, 0, 0, 0} {}
|
|
||||||
|
|
||||||
event_count(const std::chrono::duration<double> _elapsed,
|
|
||||||
const std::vector<unsigned long long> _event_counts)
|
|
||||||
: elapsed(_elapsed), event_counts(_event_counts) {}
|
|
||||||
|
|
||||||
event_count(const event_count &other)
|
|
||||||
: elapsed(other.elapsed), event_counts(other.event_counts) {}
|
|
||||||
|
|
||||||
// The types of counters (so we can read the getter more easily)
|
// The types of counters (so we can read the getter more easily)
|
||||||
enum event_counter_types {
|
enum event_counter_types {
|
||||||
CPU_CYCLES = 0,
|
CPU_CYCLES = 0,
|
||||||
INSTRUCTIONS = 1,
|
INSTRUCTIONS = 1,
|
||||||
BRANCHES = 2,
|
BRANCHES = 2,
|
||||||
MISSED_BRANCHES = 3
|
MISSED_BRANCHES = 3,
|
||||||
|
event_counter_types_size = 4
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::chrono::duration<double> elapsed;
|
||||||
|
std::array<unsigned long long, event_counter_types_size> event_counts;
|
||||||
|
|
||||||
|
event_count() : elapsed(0), event_counts{0, 0, 0, 0} {}
|
||||||
|
|
||||||
|
event_count(const std::chrono::duration<double> &_elapsed,
|
||||||
|
const std::array<unsigned long long, event_counter_types_size>
|
||||||
|
&_event_counts)
|
||||||
|
: elapsed(_elapsed), event_counts(_event_counts) {}
|
||||||
|
|
||||||
|
event_count(const event_count &other)
|
||||||
|
: elapsed(other.elapsed), event_counts(other.event_counts) {}
|
||||||
|
|
||||||
double elapsed_sec() const {
|
double elapsed_sec() const {
|
||||||
return std::chrono::duration<double>(elapsed).count();
|
return std::chrono::duration<double>(elapsed).count();
|
||||||
}
|
}
|
||||||
@ -79,7 +81,6 @@ struct event_count {
|
|||||||
event_counts[1] + other.event_counts[1],
|
event_counts[1] + other.event_counts[1],
|
||||||
event_counts[2] + other.event_counts[2],
|
event_counts[2] + other.event_counts[2],
|
||||||
event_counts[3] + other.event_counts[3],
|
event_counts[3] + other.event_counts[3],
|
||||||
event_counts[4] + other.event_counts[4],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +130,8 @@ struct event_collector {
|
|||||||
LinuxEvents<PERF_TYPE_HARDWARE> linux_events;
|
LinuxEvents<PERF_TYPE_HARDWARE> linux_events;
|
||||||
|
|
||||||
event_collector()
|
event_collector()
|
||||||
: linux_events(std::vector<int>{
|
: linux_events(std::array<unsigned long long,
|
||||||
|
4 /*event_counter_types_size*/>{
|
||||||
PERF_COUNT_HW_CPU_CYCLES, PERF_COUNT_HW_INSTRUCTIONS,
|
PERF_COUNT_HW_CPU_CYCLES, PERF_COUNT_HW_INSTRUCTIONS,
|
||||||
PERF_COUNT_HW_BRANCH_INSTRUCTIONS, // Retired branch instructions
|
PERF_COUNT_HW_BRANCH_INSTRUCTIONS, // Retired branch instructions
|
||||||
PERF_COUNT_HW_BRANCH_MISSES}) {}
|
PERF_COUNT_HW_BRANCH_MISSES}) {}
|
||||||
@ -142,7 +144,7 @@ struct event_collector {
|
|||||||
|
|
||||||
bool has_events() { return setup_performance_counters(); }
|
bool has_events() { return setup_performance_counters(); }
|
||||||
#else
|
#else
|
||||||
event_collector() {}
|
event_collector() = default;
|
||||||
|
|
||||||
bool has_events() { return false; }
|
bool has_events() { return false; }
|
||||||
#endif
|
#endif
|
||||||
@ -171,7 +173,6 @@ struct event_collector {
|
|||||||
count.event_counts[1] = diff.instructions;
|
count.event_counts[1] = diff.instructions;
|
||||||
count.event_counts[2] = diff.branches;
|
count.event_counts[2] = diff.branches;
|
||||||
count.event_counts[3] = diff.missed_branches;
|
count.event_counts[3] = diff.missed_branches;
|
||||||
count.event_counts[4] = 0;
|
|
||||||
#endif
|
#endif
|
||||||
count.elapsed = end_clock - start_clock;
|
count.elapsed = end_clock - start_clock;
|
||||||
return count;
|
return count;
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
#include <cstring> // for memset
|
#include <cstring> // for memset
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <iostream>
|
#include <array>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
template <int TYPE = PERF_TYPE_HARDWARE> class LinuxEvents {
|
template <int TYPE = PERF_TYPE_HARDWARE> class LinuxEvents {
|
||||||
@ -22,7 +22,8 @@ template <int TYPE = PERF_TYPE_HARDWARE> class LinuxEvents {
|
|||||||
std::vector<uint64_t> ids{};
|
std::vector<uint64_t> ids{};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit LinuxEvents(std::vector<int> config_vec) : fd(0), working(true) {
|
explicit LinuxEvents(std::array<unsigned long long, 4> config_vec)
|
||||||
|
: fd(0), working(true) {
|
||||||
memset(&attribs, 0, sizeof(attribs));
|
memset(&attribs, 0, sizeof(attribs));
|
||||||
attribs.type = TYPE;
|
attribs.type = TYPE;
|
||||||
attribs.size = sizeof(attribs);
|
attribs.size = sizeof(attribs);
|
||||||
@ -75,7 +76,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void end(std::vector<unsigned long long> &results) {
|
inline void end(std::array<unsigned long long, 4> &results) {
|
||||||
if (fd != -1) {
|
if (fd != -1) {
|
||||||
if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) {
|
if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) {
|
||||||
report_error("ioctl(PERF_EVENT_IOC_DISABLE)");
|
report_error("ioctl(PERF_EVENT_IOC_DISABLE)");
|
||||||
|
|||||||
28
format.cmd
Normal file
28
format.cmd
Normal 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.
|
||||||
@ -10,17 +10,16 @@
|
|||||||
|
|
||||||
#include "float_common.h"
|
#include "float_common.h"
|
||||||
|
|
||||||
#ifdef FASTFLOAT_SSE2
|
#if defined(FASTFLOAT_SSE2)
|
||||||
#include <emmintrin.h>
|
#include <emmintrin.h>
|
||||||
#endif
|
#elif defined(FASTFLOAT_NEON)
|
||||||
|
|
||||||
#ifdef FASTFLOAT_NEON
|
|
||||||
#include <arm_neon.h>
|
#include <arm_neon.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace fast_float {
|
namespace fast_float {
|
||||||
|
|
||||||
template <typename UC> fastfloat_really_inline constexpr bool has_simd_opt() {
|
template <typename UC>
|
||||||
|
fastfloat_really_inline constexpr bool has_simd_opt() noexcept {
|
||||||
#ifdef FASTFLOAT_HAS_SIMD
|
#ifdef FASTFLOAT_HAS_SIMD
|
||||||
return std::is_same<UC, char16_t>::value;
|
return std::is_same<UC, char16_t>::value;
|
||||||
#else
|
#else
|
||||||
@ -35,12 +34,14 @@ fastfloat_really_inline constexpr bool is_integer(UC c) noexcept {
|
|||||||
return !(c > UC('9') || c < UC('0'));
|
return !(c > UC('9') || c < UC('0'));
|
||||||
}
|
}
|
||||||
|
|
||||||
fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
|
#if FASTFLOAT_HAS_BYTESWAP == 0
|
||||||
|
fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) noexcept {
|
||||||
return (val & 0xFF00000000000000) >> 56 | (val & 0x00FF000000000000) >> 40 |
|
return (val & 0xFF00000000000000) >> 56 | (val & 0x00FF000000000000) >> 40 |
|
||||||
(val & 0x0000FF0000000000) >> 24 | (val & 0x000000FF00000000) >> 8 |
|
(val & 0x0000FF0000000000) >> 24 | (val & 0x000000FF00000000) >> 8 |
|
||||||
(val & 0x00000000FF000000) << 8 | (val & 0x0000000000FF0000) << 24 |
|
(val & 0x00000000FF000000) << 8 | (val & 0x0000000000FF0000) << 24 |
|
||||||
(val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56;
|
(val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Read 8 UC into a u64. Truncates UC if not char.
|
// Read 8 UC into a u64. Truncates UC if not char.
|
||||||
template <typename UC>
|
template <typename UC>
|
||||||
@ -48,7 +49,7 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
|
|||||||
read8_to_u64(UC const *chars) {
|
read8_to_u64(UC const *chars) {
|
||||||
if (cpp20_and_in_constexpr() || !std::is_same<UC, char>::value) {
|
if (cpp20_and_in_constexpr() || !std::is_same<UC, char>::value) {
|
||||||
uint64_t val = 0;
|
uint64_t val = 0;
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (uint_fast8_t i = 0; i != 8; ++i) {
|
||||||
val |= uint64_t(uint8_t(*chars)) << (i * 8);
|
val |= uint64_t(uint8_t(*chars)) << (i * 8);
|
||||||
++chars;
|
++chars;
|
||||||
}
|
}
|
||||||
@ -58,7 +59,11 @@ read8_to_u64(UC const *chars) {
|
|||||||
::memcpy(&val, chars, sizeof(uint64_t));
|
::memcpy(&val, chars, sizeof(uint64_t));
|
||||||
#if FASTFLOAT_IS_BIG_ENDIAN == 1
|
#if FASTFLOAT_IS_BIG_ENDIAN == 1
|
||||||
// Need to read as-if the number was in little-endian order.
|
// Need to read as-if the number was in little-endian order.
|
||||||
val = byteswap(val);
|
val =
|
||||||
|
#if FASTFLOAT_HAS_BYTESWAP == 1
|
||||||
|
std::
|
||||||
|
#endif
|
||||||
|
byteswap(val);
|
||||||
#endif
|
#endif
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
@ -67,9 +72,10 @@ read8_to_u64(UC const *chars) {
|
|||||||
|
|
||||||
fastfloat_really_inline uint64_t simd_read8_to_u64(__m128i const data) {
|
fastfloat_really_inline uint64_t simd_read8_to_u64(__m128i const data) {
|
||||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||||
|
// _mm_packus_epi16 is SSE2+, converts 8×u16 → 8×u8
|
||||||
__m128i const packed = _mm_packus_epi16(data, data);
|
__m128i const packed = _mm_packus_epi16(data, data);
|
||||||
#ifdef FASTFLOAT_64BIT
|
#ifdef FASTFLOAT_64BIT
|
||||||
return uint64_t(_mm_cvtsi128_si64(packed));
|
return static_cast<uint64_t>(_mm_cvtsi128_si64(packed));
|
||||||
#else
|
#else
|
||||||
uint64_t value;
|
uint64_t value;
|
||||||
// Visual Studio + older versions of GCC don't support _mm_storeu_si64
|
// Visual Studio + older versions of GCC don't support _mm_storeu_si64
|
||||||
@ -102,7 +108,7 @@ fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) {
|
|||||||
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // FASTFLOAT_SSE2
|
#endif
|
||||||
|
|
||||||
// MSVC SFINAE is broken pre-VS2017
|
// MSVC SFINAE is broken pre-VS2017
|
||||||
#if defined(_MSC_VER) && _MSC_VER <= 1900
|
#if defined(_MSC_VER) && _MSC_VER <= 1900
|
||||||
@ -117,10 +123,10 @@ uint64_t simd_read8_to_u64(UC const *) {
|
|||||||
|
|
||||||
// credit @aqrit
|
// credit @aqrit
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t
|
||||||
parse_eight_digits_unrolled(uint64_t val) {
|
parse_eight_digits_unrolled(uint64_t val) noexcept {
|
||||||
uint64_t const mask = 0x000000FF000000FF;
|
constexpr uint64_t mask = 0x000000FF000000FF;
|
||||||
uint64_t const mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
|
constexpr uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
|
||||||
uint64_t const mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
|
constexpr uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
|
||||||
val -= 0x3030303030303030;
|
val -= 0x3030303030303030;
|
||||||
val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
|
val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
|
||||||
val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
|
val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
|
||||||
@ -157,19 +163,22 @@ simd_parse_if_eight_digits_unrolled(char16_t const *chars,
|
|||||||
}
|
}
|
||||||
#ifdef FASTFLOAT_SSE2
|
#ifdef FASTFLOAT_SSE2
|
||||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||||
|
// Load 8 UTF-16 characters (16 bytes)
|
||||||
__m128i const data =
|
__m128i const data =
|
||||||
_mm_loadu_si128(reinterpret_cast<__m128i const *>(chars));
|
_mm_loadu_si128(reinterpret_cast<__m128i const *>(chars));
|
||||||
|
|
||||||
// (x - '0') <= 9
|
// Branchless "are all digits?" trick from Lemire:
|
||||||
|
// (x - '0') <= 9 <=> (x + 32720) <= 32729
|
||||||
|
// encoded as signed comparison: (x + 32720) > -32759 ? not digit : digit
|
||||||
// http://0x80.pl/articles/simd-parsing-int-sequences.html
|
// http://0x80.pl/articles/simd-parsing-int-sequences.html
|
||||||
__m128i const t0 = _mm_add_epi16(data, _mm_set1_epi16(32720));
|
__m128i const t0 = _mm_add_epi16(data, _mm_set1_epi16(32720));
|
||||||
__m128i const t1 = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759));
|
__m128i const mask = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759));
|
||||||
|
|
||||||
if (_mm_movemask_epi8(t1) == 0) {
|
// If mask == 0 → all digits valid.
|
||||||
|
if (_mm_movemask_epi8(mask) == 0) {
|
||||||
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
|
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
|
||||||
return true;
|
return true;
|
||||||
} else
|
}
|
||||||
return false;
|
|
||||||
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||||
#elif defined(FASTFLOAT_NEON)
|
#elif defined(FASTFLOAT_NEON)
|
||||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||||
@ -183,17 +192,16 @@ simd_parse_if_eight_digits_unrolled(char16_t const *chars,
|
|||||||
if (vminvq_u16(mask) == 0xFFFF) {
|
if (vminvq_u16(mask) == 0xFFFF) {
|
||||||
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
|
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
|
||||||
return true;
|
return true;
|
||||||
} else
|
}
|
||||||
return false;
|
|
||||||
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||||
#else
|
#else
|
||||||
(void)chars;
|
(void)chars;
|
||||||
(void)i;
|
(void)i;
|
||||||
|
#endif
|
||||||
return false;
|
return false;
|
||||||
#endif // FASTFLOAT_SSE2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // FASTFLOAT_HAS_SIMD
|
#endif
|
||||||
|
|
||||||
// MSVC SFINAE is broken pre-VS2017
|
// MSVC SFINAE is broken pre-VS2017
|
||||||
#if defined(_MSC_VER) && _MSC_VER <= 1900
|
#if defined(_MSC_VER) && _MSC_VER <= 1900
|
||||||
@ -232,12 +240,17 @@ loop_parse_if_eight_digits(char const *&p, char const *const pend,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class parse_error {
|
enum class parse_error : uint_fast8_t {
|
||||||
no_error,
|
no_error,
|
||||||
// [JSON-only] The minus sign must be followed by an integer.
|
|
||||||
missing_integer_after_sign,
|
|
||||||
// A sign must be followed by an integer or dot.
|
// A sign must be followed by an integer or dot.
|
||||||
missing_integer_or_dot_after_sign,
|
missing_integer_or_dot_after_sign,
|
||||||
|
// The mantissa must have at least one digit.
|
||||||
|
no_digits_in_mantissa,
|
||||||
|
// Scientific notation requires an exponential part.
|
||||||
|
missing_exponential_part,
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
// [JSON-only] The minus sign must be followed by an integer.
|
||||||
|
missing_integer_after_sign,
|
||||||
// [JSON-only] The integer part must not have leading zeros.
|
// [JSON-only] The integer part must not have leading zeros.
|
||||||
leading_zeros_in_integer_part,
|
leading_zeros_in_integer_part,
|
||||||
// [JSON-only] The integer part must have at least one digit.
|
// [JSON-only] The integer part must have at least one digit.
|
||||||
@ -245,18 +258,18 @@ enum class parse_error {
|
|||||||
// [JSON-only] If there is a decimal point, there must be digits in the
|
// [JSON-only] If there is a decimal point, there must be digits in the
|
||||||
// fractional part.
|
// fractional part.
|
||||||
no_digits_in_fractional_part,
|
no_digits_in_fractional_part,
|
||||||
// The mantissa must have at least one digit.
|
#endif
|
||||||
no_digits_in_mantissa,
|
|
||||||
// Scientific notation requires an exponential part.
|
|
||||||
missing_exponential_part,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename UC> struct parsed_number_string_t {
|
template <typename UC> struct parsed_number_string_t {
|
||||||
int64_t exponent{0};
|
// an unsigned int avoids signed overflows (which are bad)
|
||||||
uint64_t mantissa{0};
|
am_mant_t mantissa{0};
|
||||||
|
am_pow_t exponent{0};
|
||||||
UC const *lastmatch{nullptr};
|
UC const *lastmatch{nullptr};
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
bool negative{false};
|
bool negative{false};
|
||||||
bool valid{false};
|
#endif
|
||||||
|
bool invalid{false};
|
||||||
bool too_many_digits{false};
|
bool too_many_digits{false};
|
||||||
// contains the range of the significant digits
|
// contains the range of the significant digits
|
||||||
span<UC const> integer{}; // non-nullable
|
span<UC const> integer{}; // non-nullable
|
||||||
@ -269,9 +282,9 @@ using parsed_number_string = parsed_number_string_t<char>;
|
|||||||
|
|
||||||
template <typename UC>
|
template <typename UC>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
|
||||||
report_parse_error(UC const *p, parse_error error) {
|
report_parse_error(UC const *p, parse_error error) noexcept {
|
||||||
parsed_number_string_t<UC> answer;
|
parsed_number_string_t<UC> answer;
|
||||||
answer.valid = false;
|
answer.invalid = true;
|
||||||
answer.lastmatch = p;
|
answer.lastmatch = p;
|
||||||
answer.error = error;
|
answer.error = error;
|
||||||
return answer;
|
return answer;
|
||||||
@ -282,53 +295,60 @@ report_parse_error(UC const *p, parse_error error) {
|
|||||||
template <bool basic_json_fmt, typename UC>
|
template <bool basic_json_fmt, typename UC>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
|
||||||
parse_number_string(UC const *p, UC const *pend,
|
parse_number_string(UC const *p, UC const *pend,
|
||||||
parse_options_t<UC> options) noexcept {
|
parse_options_t<UC> const &options) noexcept {
|
||||||
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
|
// Cyclomatic complexity https://en.wikipedia.org/wiki/Cyclomatic_complexity
|
||||||
UC const decimal_point = options.decimal_point;
|
// Consider refactoring the 'parse_number_string' function.
|
||||||
|
// FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN fix this.
|
||||||
parsed_number_string_t<UC> answer;
|
parsed_number_string_t<UC> answer;
|
||||||
answer.valid = false;
|
// so dereference without checks
|
||||||
answer.too_many_digits = false;
|
FASTFLOAT_ASSUME(p < pend);
|
||||||
// assume p < pend, so dereference without checks;
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
answer.negative = (*p == UC('-'));
|
answer.negative = (*p == UC('-'));
|
||||||
|
if (answer.negative ||
|
||||||
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here
|
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here
|
||||||
if ((*p == UC('-')) || (uint64_t(fmt & chars_format::allow_leading_plus) &&
|
((chars_format_t(options.format & chars_format::allow_leading_plus)) &&
|
||||||
!basic_json_fmt && *p == UC('+'))) {
|
(!basic_json_fmt && *p == UC('+')))) {
|
||||||
++p;
|
++p;
|
||||||
if (p == pend) {
|
if (p == pend) {
|
||||||
return report_parse_error<UC>(
|
return report_parse_error<UC>(
|
||||||
p, parse_error::missing_integer_or_dot_after_sign);
|
p, parse_error::missing_integer_or_dot_after_sign);
|
||||||
}
|
}
|
||||||
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
|
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
|
||||||
if (!is_integer(*p)) { // a sign must be followed by an integer
|
// a sign must be followed by an integer
|
||||||
|
if (!is_integer(*p)) {
|
||||||
return report_parse_error<UC>(p,
|
return report_parse_error<UC>(p,
|
||||||
parse_error::missing_integer_after_sign);
|
parse_error::missing_integer_after_sign);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!is_integer(*p) &&
|
// a sign must be followed by an integer or the dot
|
||||||
(*p !=
|
if (!is_integer(*p) && (*p != options.decimal_point)) {
|
||||||
decimal_point)) { // a sign must be followed by an integer or the dot
|
|
||||||
return report_parse_error<UC>(
|
return report_parse_error<UC>(
|
||||||
p, parse_error::missing_integer_or_dot_after_sign);
|
p, parse_error::missing_integer_or_dot_after_sign);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UC const *const start_digits = p;
|
#endif
|
||||||
|
|
||||||
uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
|
UC const *const start_digits = p;
|
||||||
|
|
||||||
while ((p != pend) && is_integer(*p)) {
|
while ((p != pend) && is_integer(*p)) {
|
||||||
// a multiplication by 10 is cheaper than an arbitrary integer
|
// a multiplication by 10 is cheaper than an arbitrary integer
|
||||||
// multiplication
|
// multiplication
|
||||||
i = 10 * i +
|
answer.mantissa = static_cast<fast_float::am_mant_t>(
|
||||||
uint64_t(*p -
|
answer.mantissa * 10 +
|
||||||
UC('0')); // might overflow, we will handle the overflow later
|
static_cast<fast_float::am_mant_t>(
|
||||||
|
*p - UC('0'))); // might overflow, we will handle the overflow later
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
|
|
||||||
UC const *const end_of_integer_part = p;
|
UC const *const end_of_integer_part = p;
|
||||||
int64_t digit_count = int64_t(end_of_integer_part - start_digits);
|
am_digits digit_count =
|
||||||
answer.integer = span<UC const>(start_digits, size_t(digit_count));
|
static_cast<am_digits>(end_of_integer_part - start_digits);
|
||||||
|
answer.integer = span<UC const>(start_digits, digit_count);
|
||||||
|
// We have now parsed the integer part of the mantissa.
|
||||||
|
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
|
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
|
||||||
// at least 1 digit in integer part, without leading zeros
|
// at least 1 digit in integer part, without leading zeros
|
||||||
if (digit_count == 0) {
|
if (digit_count == 0) {
|
||||||
@ -339,148 +359,180 @@ parse_number_string(UC const *p, UC const *pend,
|
|||||||
parse_error::leading_zeros_in_integer_part);
|
parse_error::leading_zeros_in_integer_part);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int64_t exponent = 0;
|
// We can now parse the fraction part of the mantissa.
|
||||||
bool const has_decimal_point = (p != pend) && (*p == decimal_point);
|
if ((p != pend) && (*p == options.decimal_point)) {
|
||||||
if (has_decimal_point) {
|
|
||||||
++p;
|
++p;
|
||||||
UC const *before = p;
|
UC const *const before = p;
|
||||||
// can occur at most twice without overflowing, but let it occur more, since
|
// can occur at most twice without overflowing, but let it occur more, since
|
||||||
// for integers with many digits, digit parsing is the primary bottleneck.
|
// for integers with many digits, digit parsing is the primary bottleneck.
|
||||||
loop_parse_if_eight_digits(p, pend, i);
|
loop_parse_if_eight_digits(p, pend, answer.mantissa);
|
||||||
|
|
||||||
while ((p != pend) && is_integer(*p)) {
|
while ((p != pend) && is_integer(*p)) {
|
||||||
uint8_t digit = uint8_t(*p - UC('0'));
|
UC const digit = UC(*p - UC('0'));
|
||||||
|
answer.mantissa = static_cast<fast_float::am_mant_t>(
|
||||||
|
answer.mantissa * 10 +
|
||||||
|
static_cast<am_mant_t>(
|
||||||
|
digit)); // in rare cases, this will overflow, but that's ok
|
||||||
++p;
|
++p;
|
||||||
i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
|
|
||||||
}
|
|
||||||
exponent = before - p;
|
|
||||||
answer.fraction = span<UC const>(before, size_t(p - before));
|
|
||||||
digit_count -= exponent;
|
|
||||||
}
|
}
|
||||||
|
answer.exponent = static_cast<am_pow_t>(before - p);
|
||||||
|
answer.fraction =
|
||||||
|
span<UC const>(before, static_cast<am_digits>(p - before));
|
||||||
|
digit_count -= static_cast<am_digits>(answer.exponent);
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
|
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
|
||||||
// at least 1 digit in fractional part
|
// at least 1 digit in fractional part
|
||||||
if (has_decimal_point && exponent == 0) {
|
if (answer.exponent == 0) {
|
||||||
return report_parse_error<UC>(p,
|
return report_parse_error<UC>(
|
||||||
parse_error::no_digits_in_fractional_part);
|
p, parse_error::no_digits_in_fractional_part);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (digit_count == 0) { // we must have encountered at least one integer!
|
#endif
|
||||||
|
} else if (digit_count == 0) {
|
||||||
|
// We must have encountered at least one integer!
|
||||||
return report_parse_error<UC>(p, parse_error::no_digits_in_mantissa);
|
return report_parse_error<UC>(p, parse_error::no_digits_in_mantissa);
|
||||||
}
|
}
|
||||||
int64_t exp_number = 0; // explicit exponential part
|
// We have now parsed the integer and the fraction part of the mantissa.
|
||||||
if ((uint64_t(fmt & chars_format::scientific) && (p != pend) &&
|
|
||||||
((UC('e') == *p) || (UC('E') == *p))) ||
|
// Now we can parse the explicit exponential part.
|
||||||
(uint64_t(fmt & detail::basic_fortran_fmt) && (p != pend) &&
|
am_pow_t exp_number = 0; // explicit exponential part
|
||||||
|
if ((p != pend) &&
|
||||||
|
((chars_format_t(options.format & chars_format::scientific) &&
|
||||||
|
(UC('e') == *p || UC('E') == *p))
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
|| (chars_format_t(options.format & detail::basic_fortran_fmt) &&
|
||||||
((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) ||
|
((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) ||
|
||||||
(UC('D') == *p)))) {
|
(UC('D') == *p)))
|
||||||
|
#endif
|
||||||
|
)) {
|
||||||
UC const *location_of_e = p;
|
UC const *location_of_e = p;
|
||||||
|
#ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
++p;
|
||||||
|
#else
|
||||||
if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) ||
|
if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) ||
|
||||||
(UC('D') == *p)) {
|
(UC('D') == *p)) {
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
bool neg_exp = false;
|
bool neg_exp = false;
|
||||||
if ((p != pend) && (UC('-') == *p)) {
|
if (p != pend) {
|
||||||
|
if (UC('-') == *p) {
|
||||||
neg_exp = true;
|
neg_exp = true;
|
||||||
++p;
|
++p;
|
||||||
} else if ((p != pend) &&
|
} else if (UC('+') == *p) {
|
||||||
(UC('+') ==
|
// '+' on exponent is allowed by C++17 20.19.3.(7.1)
|
||||||
*p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
|
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// We have now parsed the sign of the exponent.
|
||||||
if ((p == pend) || !is_integer(*p)) {
|
if ((p == pend) || !is_integer(*p)) {
|
||||||
if (!uint64_t(fmt & chars_format::fixed)) {
|
if (!(chars_format_t(options.format & chars_format::fixed))) {
|
||||||
// The exponential part is invalid for scientific notation, so it must
|
// The exponential part is invalid for scientific notation, so it
|
||||||
// be a trailing token for fixed notation. However, fixed notation is
|
// must be a trailing token for fixed notation. However, fixed
|
||||||
// disabled, so report a scientific notation error.
|
// notation is disabled, so report a scientific notation error.
|
||||||
return report_parse_error<UC>(p, parse_error::missing_exponential_part);
|
return report_parse_error<UC>(p, parse_error::missing_exponential_part);
|
||||||
}
|
}
|
||||||
// Otherwise, we will be ignoring the 'e'.
|
// Otherwise, we will be ignoring the 'e'.
|
||||||
p = location_of_e;
|
p = location_of_e;
|
||||||
} else {
|
} else {
|
||||||
|
// Now let's parse the explicit exponent.
|
||||||
while ((p != pend) && is_integer(*p)) {
|
while ((p != pend) && is_integer(*p)) {
|
||||||
uint8_t digit = uint8_t(*p - UC('0'));
|
if (exp_number < 0x10000) {
|
||||||
if (exp_number < 0x10000000) {
|
// check for exponent overflow if we have too many digits.
|
||||||
exp_number = 10 * exp_number + digit;
|
UC const digit = UC(*p - UC('0'));
|
||||||
|
exp_number = 10 * exp_number + static_cast<am_pow_t>(digit);
|
||||||
}
|
}
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
if (neg_exp) {
|
if (neg_exp) {
|
||||||
exp_number = -exp_number;
|
exp_number = -exp_number;
|
||||||
}
|
}
|
||||||
exponent += exp_number;
|
answer.exponent += exp_number;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If it scientific and not fixed, we have to bail out.
|
// If it scientific and not fixed, we have to bail out.
|
||||||
if (uint64_t(fmt & chars_format::scientific) &&
|
if ((chars_format_t(options.format & chars_format::scientific)) &&
|
||||||
!uint64_t(fmt & chars_format::fixed)) {
|
!(chars_format_t(options.format & chars_format::fixed))) {
|
||||||
return report_parse_error<UC>(p, parse_error::missing_exponential_part);
|
return report_parse_error<UC>(p, parse_error::missing_exponential_part);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
answer.lastmatch = p;
|
|
||||||
answer.valid = true;
|
|
||||||
|
|
||||||
// If we frequently had to deal with long strings of digits,
|
// We parsed all parts of the number, let's save progress.
|
||||||
|
answer.lastmatch = p;
|
||||||
|
|
||||||
|
// Now we can check for errors.
|
||||||
|
|
||||||
|
// TODO: If we frequently had to deal with long strings of digits,
|
||||||
// we could extend our code by using a 128-bit integer instead
|
// we could extend our code by using a 128-bit integer instead
|
||||||
// of a 64-bit integer. However, this is uncommon.
|
// of a 64-bit integer. However, this is uncommon.
|
||||||
//
|
|
||||||
// We can deal with up to 19 digits.
|
// We can deal with up to 19 digits.
|
||||||
if (digit_count > 19) { // this is uncommon
|
if (digit_count > 19) {
|
||||||
// It is possible that the integer had an overflow.
|
// It is possible that the integer had an overflow.
|
||||||
// We have to handle the case where we have 0.0000somenumber.
|
// We have to handle the case where we have 0.0000somenumber.
|
||||||
// We need to be mindful of the case where we only have zeroes...
|
// We need to be mindful of the case where we only have zeroes...
|
||||||
// E.g., 0.000000000...000.
|
// E.g., 0.000000000...000.
|
||||||
UC const *start = start_digits;
|
UC const *start = start_digits;
|
||||||
while ((start != pend) && (*start == UC('0') || *start == decimal_point)) {
|
while ((start != pend) &&
|
||||||
|
(*start == UC('0') || *start == options.decimal_point)) {
|
||||||
if (*start == UC('0')) {
|
if (*start == UC('0')) {
|
||||||
digit_count--;
|
--digit_count;
|
||||||
}
|
}
|
||||||
start++;
|
++start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We have to check if we have a number with more than 19 significant
|
||||||
|
// digits.
|
||||||
if (digit_count > 19) {
|
if (digit_count > 19) {
|
||||||
answer.too_many_digits = true;
|
answer.too_many_digits = true;
|
||||||
// Let us start again, this time, avoiding overflows.
|
// Let us start again, this time, avoiding overflows.
|
||||||
// We don't need to call if is_integer, since we use the
|
// We don't need to call if is_integer, since we use the
|
||||||
// pre-tokenized spans from above.
|
// pre-tokenized spans from above.
|
||||||
i = 0;
|
answer.mantissa = 0;
|
||||||
p = answer.integer.ptr;
|
p = answer.integer.ptr;
|
||||||
UC const *int_end = p + answer.integer.len();
|
UC const *int_end = p + answer.integer.len();
|
||||||
uint64_t const minimal_nineteen_digit_integer{1000000000000000000};
|
constexpr am_mant_t minimal_nineteen_digit_integer{1000000000000000000};
|
||||||
while ((i < minimal_nineteen_digit_integer) && (p != int_end)) {
|
while ((answer.mantissa < minimal_nineteen_digit_integer) &&
|
||||||
i = i * 10 + uint64_t(*p - UC('0'));
|
(p != int_end)) {
|
||||||
|
answer.mantissa = static_cast<am_mant_t>(
|
||||||
|
answer.mantissa * 10 + static_cast<am_mant_t>(*p - UC('0')));
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
if (i >= minimal_nineteen_digit_integer) { // We have a big integer
|
if (answer.mantissa >= minimal_nineteen_digit_integer) {
|
||||||
exponent = end_of_integer_part - p + exp_number;
|
// We have a big integers, so skip the fraction part completely.
|
||||||
} else { // We have a value with a fractional component.
|
answer.exponent = am_pow_t(end_of_integer_part - p) + exp_number;
|
||||||
|
} else {
|
||||||
|
// We have a value with a significant fractional component.
|
||||||
p = answer.fraction.ptr;
|
p = answer.fraction.ptr;
|
||||||
UC const *frac_end = p + answer.fraction.len();
|
UC const *const frac_end = p + answer.fraction.len();
|
||||||
while ((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
|
while ((answer.mantissa < minimal_nineteen_digit_integer) &&
|
||||||
i = i * 10 + uint64_t(*p - UC('0'));
|
(p != frac_end)) {
|
||||||
|
answer.mantissa = static_cast<am_mant_t>(
|
||||||
|
answer.mantissa * 10 + static_cast<am_mant_t>(*p - UC('0')));
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
exponent = answer.fraction.ptr - p + exp_number;
|
answer.exponent = am_pow_t(answer.fraction.ptr - p) + exp_number;
|
||||||
}
|
|
||||||
// We have now corrected both exponent and i, to a truncated value
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
answer.exponent = exponent;
|
// We have now corrected both exponent and mantissa, to a truncated value
|
||||||
answer.mantissa = i;
|
}
|
||||||
|
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename UC>
|
template <typename T, typename UC>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||||
parse_int_string(UC const *p, UC const *pend, T &value,
|
parse_int_string(UC const *p, UC const *pend, T &value,
|
||||||
parse_options_t<UC> options) {
|
parse_options_t<UC> const &options) noexcept {
|
||||||
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
|
|
||||||
int const base = options.base;
|
|
||||||
|
|
||||||
from_chars_result_t<UC> answer;
|
from_chars_result_t<UC> answer;
|
||||||
|
|
||||||
UC const *const first = p;
|
UC const *const first = p;
|
||||||
|
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
// Read sign
|
||||||
bool const negative = (*p == UC('-'));
|
bool const negative = (*p == UC('-'));
|
||||||
#ifdef FASTFLOAT_VISUAL_STUDIO
|
#ifdef FASTFLOAT_VISUAL_STUDIO
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
@ -495,12 +547,15 @@ parse_int_string(UC const *p, UC const *pend, T &value,
|
|||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
if ((*p == UC('-')) ||
|
if ((*p == UC('-')) ||
|
||||||
(uint64_t(fmt & chars_format::allow_leading_plus) && (*p == UC('+')))) {
|
((chars_format_t(options.format & chars_format::allow_leading_plus)) &&
|
||||||
|
(*p == UC('+')))) {
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
UC const *const start_num = p;
|
UC const *const start_num = p;
|
||||||
|
|
||||||
|
// Skip leading zeros
|
||||||
while (p != pend && *p == UC('0')) {
|
while (p != pend && *p == UC('0')) {
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
@ -509,20 +564,21 @@ parse_int_string(UC const *p, UC const *pend, T &value,
|
|||||||
|
|
||||||
UC const *const start_digits = p;
|
UC const *const start_digits = p;
|
||||||
|
|
||||||
|
// Parse digits
|
||||||
uint64_t i = 0;
|
uint64_t i = 0;
|
||||||
if (base == 10) {
|
if (options.base == 10) {
|
||||||
loop_parse_if_eight_digits(p, pend, i); // use SIMD if possible
|
loop_parse_if_eight_digits(p, pend, i); // use SIMD if possible
|
||||||
}
|
}
|
||||||
while (p != pend) {
|
while (p != pend) {
|
||||||
uint8_t digit = ch_to_digit(*p);
|
uint_fast8_t const digit = ch_to_digit(*p);
|
||||||
if (digit >= base) {
|
if (digit >= options.base) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
i = uint64_t(base) * i + digit; // might overflow, check this later
|
i = uint64_t(options.base) * i + digit; // might overflow, check this later
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t digit_count = size_t(p - start_digits);
|
am_digits const digit_count = static_cast<am_digits>(p - start_digits);
|
||||||
|
|
||||||
if (digit_count == 0) {
|
if (digit_count == 0) {
|
||||||
if (has_leading_zeros) {
|
if (has_leading_zeros) {
|
||||||
@ -539,26 +595,33 @@ parse_int_string(UC const *p, UC const *pend, T &value,
|
|||||||
answer.ptr = p;
|
answer.ptr = p;
|
||||||
|
|
||||||
// check u64 overflow
|
// check u64 overflow
|
||||||
size_t max_digits = max_digits_u64(base);
|
uint_fast8_t const max_digits = max_digits_u64(options.base);
|
||||||
if (digit_count > max_digits) {
|
if (digit_count > max_digits) {
|
||||||
answer.ec = std::errc::result_out_of_range;
|
answer.ec = std::errc::result_out_of_range;
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
// this check can be eliminated for all other types, but they will all require
|
// this check can be eliminated for all other types, but they will all require
|
||||||
// a max_digits(base) equivalent
|
// a max_digits(base) equivalent
|
||||||
if (digit_count == max_digits && i < min_safe_u64(base)) {
|
if (digit_count == max_digits && i < min_safe_u64(options.base)) {
|
||||||
answer.ec = std::errc::result_out_of_range;
|
answer.ec = std::errc::result_out_of_range;
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check other types overflow
|
// check other types overflow
|
||||||
if (!std::is_same<T, uint64_t>::value) {
|
if (!std::is_same<T, uint64_t>::value) {
|
||||||
if (i > uint64_t(std::numeric_limits<T>::max()) + uint64_t(negative)) {
|
if (i > uint64_t(std::numeric_limits<T>::max())
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
+ uint64_t(negative)
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
answer.ec = std::errc::result_out_of_range;
|
answer.ec = std::errc::result_out_of_range;
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
value = T(i);
|
||||||
|
#else
|
||||||
if (negative) {
|
if (negative) {
|
||||||
#ifdef FASTFLOAT_VISUAL_STUDIO
|
#ifdef FASTFLOAT_VISUAL_STUDIO
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
@ -578,6 +641,7 @@ parse_int_string(UC const *p, UC const *pend, T &value,
|
|||||||
} else {
|
} else {
|
||||||
value = T(i);
|
value = T(i);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
answer.ec = std::errc();
|
answer.ec = std::errc();
|
||||||
return answer;
|
return answer;
|
||||||
|
|||||||
@ -19,11 +19,11 @@ namespace fast_float {
|
|||||||
#if defined(FASTFLOAT_64BIT) && !defined(__sparc)
|
#if defined(FASTFLOAT_64BIT) && !defined(__sparc)
|
||||||
#define FASTFLOAT_64BIT_LIMB 1
|
#define FASTFLOAT_64BIT_LIMB 1
|
||||||
typedef uint64_t limb;
|
typedef uint64_t limb;
|
||||||
constexpr size_t limb_bits = 64;
|
constexpr limb_t limb_bits = 64;
|
||||||
#else
|
#else
|
||||||
#define FASTFLOAT_32BIT_LIMB
|
#define FASTFLOAT_32BIT_LIMB
|
||||||
typedef uint32_t limb;
|
typedef uint32_t limb;
|
||||||
constexpr size_t limb_bits = 32;
|
constexpr limb_t limb_bits = 32;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef span<limb> limb_span;
|
typedef span<limb> limb_span;
|
||||||
@ -32,59 +32,58 @@ typedef span<limb> limb_span;
|
|||||||
// of bits required to store the largest bigint, which is
|
// of bits required to store the largest bigint, which is
|
||||||
// `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or
|
// `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or
|
||||||
// ~3600 bits, so we round to 4000.
|
// ~3600 bits, so we round to 4000.
|
||||||
constexpr size_t bigint_bits = 4000;
|
typedef uint_fast16_t bigint_bits_t;
|
||||||
constexpr size_t bigint_limbs = bigint_bits / limb_bits;
|
constexpr bigint_bits_t bigint_bits = 4000;
|
||||||
|
constexpr limb_t bigint_limbs = bigint_bits / limb_bits;
|
||||||
|
|
||||||
// vector-like type that is allocated on the stack. the entire
|
// vector-like type that is allocated on the stack. the entire
|
||||||
// buffer is pre-allocated, and only the length changes.
|
// buffer is pre-allocated, and only the length changes.
|
||||||
template <uint16_t size> struct stackvec {
|
template <limb_t size> struct stackvec {
|
||||||
limb data[size];
|
limb data[size];
|
||||||
// we never need more than 150 limbs
|
// we never need more than 150 limbs
|
||||||
uint16_t length{0};
|
uint_fast8_t length{0};
|
||||||
|
|
||||||
stackvec() = default;
|
FASTFLOAT_CONSTEXPR20 stackvec() noexcept = default;
|
||||||
stackvec(stackvec const &) = delete;
|
stackvec(stackvec const &) = delete;
|
||||||
stackvec &operator=(stackvec const &) = delete;
|
stackvec &operator=(stackvec const &) = delete;
|
||||||
stackvec(stackvec &&) = delete;
|
stackvec(stackvec &&) = delete;
|
||||||
stackvec &operator=(stackvec &&other) = delete;
|
stackvec &operator=(stackvec &&other) = delete;
|
||||||
|
|
||||||
// create stack vector from existing limb span.
|
// create stack vector from existing limb span.
|
||||||
FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) {
|
FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) noexcept {
|
||||||
FASTFLOAT_ASSERT(try_extend(s));
|
FASTFLOAT_ASSERT(try_extend(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
FASTFLOAT_CONSTEXPR14 limb &operator[](size_t index) noexcept {
|
FASTFLOAT_CONSTEXPR14 limb &operator[](limb_t index) noexcept {
|
||||||
FASTFLOAT_DEBUG_ASSERT(index < length);
|
FASTFLOAT_DEBUG_ASSERT(index < length);
|
||||||
return data[index];
|
return data[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
FASTFLOAT_CONSTEXPR14 const limb &operator[](size_t index) const noexcept {
|
FASTFLOAT_CONSTEXPR14 const limb &operator[](limb_t index) const noexcept {
|
||||||
FASTFLOAT_DEBUG_ASSERT(index < length);
|
FASTFLOAT_DEBUG_ASSERT(index < length);
|
||||||
return data[index];
|
return data[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
// index from the end of the container
|
// index from the end of the container
|
||||||
FASTFLOAT_CONSTEXPR14 const limb &rindex(size_t index) const noexcept {
|
FASTFLOAT_CONSTEXPR14 const limb &rindex(limb_t index) const noexcept {
|
||||||
FASTFLOAT_DEBUG_ASSERT(index < length);
|
FASTFLOAT_DEBUG_ASSERT(index < length);
|
||||||
size_t rindex = length - index - 1;
|
limb_t rindex = static_cast<limb_t>(length - index - 1);
|
||||||
return data[rindex];
|
return data[rindex];
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the length, without bounds checking.
|
// set the length, without bounds checking.
|
||||||
FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept {
|
FASTFLOAT_CONSTEXPR14 void set_len(limb_t len) noexcept { length = len; }
|
||||||
length = uint16_t(len);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr size_t len() const noexcept { return length; }
|
constexpr limb_t len() const noexcept { return length; }
|
||||||
|
|
||||||
constexpr bool is_empty() const noexcept { return length == 0; }
|
constexpr bool is_empty() const noexcept { return length == 0; }
|
||||||
|
|
||||||
constexpr size_t capacity() const noexcept { return size; }
|
constexpr limb_t capacity() const noexcept { return size; }
|
||||||
|
|
||||||
// append item to vector, without bounds checking
|
// append item to vector, without bounds checking
|
||||||
FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept {
|
FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept {
|
||||||
data[length] = value;
|
data[length] = value;
|
||||||
length++;
|
++length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// append item to vector, returning if item was added
|
// append item to vector, returning if item was added
|
||||||
@ -101,7 +100,7 @@ template <uint16_t size> struct stackvec {
|
|||||||
FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept {
|
FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept {
|
||||||
limb *ptr = data + length;
|
limb *ptr = data + length;
|
||||||
std::copy_n(s.ptr, s.len(), ptr);
|
std::copy_n(s.ptr, s.len(), ptr);
|
||||||
set_len(len() + s.len());
|
set_len(limb_t(len() + s.len()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to add items to the vector, returning if items were added
|
// try to add items to the vector, returning if items were added
|
||||||
@ -118,37 +117,34 @@ template <uint16_t size> struct stackvec {
|
|||||||
// if the new size is longer than the vector, assign value to each
|
// if the new size is longer than the vector, assign value to each
|
||||||
// appended item.
|
// appended item.
|
||||||
FASTFLOAT_CONSTEXPR20
|
FASTFLOAT_CONSTEXPR20
|
||||||
void resize_unchecked(size_t new_len, limb value) noexcept {
|
void resize_unchecked(limb_t new_len, limb value) noexcept {
|
||||||
if (new_len > len()) {
|
if (new_len > len()) {
|
||||||
size_t count = new_len - len();
|
limb_t count = new_len - len();
|
||||||
limb *first = data + len();
|
limb *first = data + len();
|
||||||
limb *last = first + count;
|
limb *last = first + count;
|
||||||
::std::fill(first, last, value);
|
::std::fill(first, last, value);
|
||||||
set_len(new_len);
|
|
||||||
} else {
|
|
||||||
set_len(new_len);
|
|
||||||
}
|
}
|
||||||
|
set_len(new_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to resize the vector, returning if the vector was resized.
|
// try to resize the vector, returning if the vector was resized.
|
||||||
FASTFLOAT_CONSTEXPR20 bool try_resize(size_t new_len, limb value) noexcept {
|
FASTFLOAT_CONSTEXPR20 bool try_resize(limb_t new_len, limb value) noexcept {
|
||||||
if (new_len > capacity()) {
|
if (new_len > capacity()) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
}
|
||||||
resize_unchecked(new_len, value);
|
resize_unchecked(new_len, value);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// check if any limbs are non-zero after the given index.
|
// check if any limbs are non-zero after the given index.
|
||||||
// this needs to be done in reverse order, since the index
|
// this needs to be done in reverse order, since the index
|
||||||
// is relative to the most significant limbs.
|
// is relative to the most significant limbs.
|
||||||
FASTFLOAT_CONSTEXPR14 bool nonzero(size_t index) const noexcept {
|
FASTFLOAT_CONSTEXPR14 bool nonzero(limb_t index) const noexcept {
|
||||||
while (index < len()) {
|
while (index < len()) {
|
||||||
if (rindex(index) != 0) {
|
if (rindex(index) != 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
index++;
|
++index;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -156,7 +152,7 @@ template <uint16_t size> struct stackvec {
|
|||||||
// normalize the big integer, so most-significant zero limbs are removed.
|
// normalize the big integer, so most-significant zero limbs are removed.
|
||||||
FASTFLOAT_CONSTEXPR14 void normalize() noexcept {
|
FASTFLOAT_CONSTEXPR14 void normalize() noexcept {
|
||||||
while (len() > 0 && rindex(0) == 0) {
|
while (len() > 0 && rindex(0) == 0) {
|
||||||
length--;
|
--length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -258,16 +254,15 @@ scalar_mul(limb x, limb y, limb &carry) noexcept {
|
|||||||
|
|
||||||
// add scalar value to bigint starting from offset.
|
// add scalar value to bigint starting from offset.
|
||||||
// used in grade school multiplication
|
// used in grade school multiplication
|
||||||
template <uint16_t size>
|
template <limb_t size>
|
||||||
inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec<size> &vec, limb y,
|
inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec<size> &vec, limb y,
|
||||||
size_t start) noexcept {
|
limb_t start) noexcept {
|
||||||
size_t index = start;
|
|
||||||
limb carry = y;
|
limb carry = y;
|
||||||
bool overflow;
|
bool overflow;
|
||||||
while (carry != 0 && index < vec.len()) {
|
while (carry != 0 && start < vec.len()) {
|
||||||
vec[index] = scalar_add(vec[index], carry, overflow);
|
vec[start] = scalar_add(vec[start], carry, overflow);
|
||||||
carry = limb(overflow);
|
carry = limb(overflow);
|
||||||
index += 1;
|
++start;
|
||||||
}
|
}
|
||||||
if (carry != 0) {
|
if (carry != 0) {
|
||||||
FASTFLOAT_TRY(vec.try_push(carry));
|
FASTFLOAT_TRY(vec.try_push(carry));
|
||||||
@ -276,18 +271,18 @@ inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec<size> &vec, limb y,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add scalar value to bigint.
|
// add scalar value to bigint.
|
||||||
template <uint16_t size>
|
template <limb_t size>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
|
||||||
small_add(stackvec<size> &vec, limb y) noexcept {
|
small_add(stackvec<size> &vec, limb y) noexcept {
|
||||||
return small_add_from(vec, y, 0);
|
return small_add_from(vec, y, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// multiply bigint by scalar value.
|
// multiply bigint by scalar value.
|
||||||
template <uint16_t size>
|
template <limb_t size>
|
||||||
inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec<size> &vec,
|
inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec<size> &vec,
|
||||||
limb y) noexcept {
|
limb y) noexcept {
|
||||||
limb carry = 0;
|
limb carry = 0;
|
||||||
for (size_t index = 0; index < vec.len(); index++) {
|
for (limb_t index = 0; index != vec.len(); ++index) {
|
||||||
vec[index] = scalar_mul(vec[index], y, carry);
|
vec[index] = scalar_mul(vec[index], y, carry);
|
||||||
}
|
}
|
||||||
if (carry != 0) {
|
if (carry != 0) {
|
||||||
@ -298,17 +293,17 @@ inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec<size> &vec,
|
|||||||
|
|
||||||
// add bigint to bigint starting from index.
|
// add bigint to bigint starting from index.
|
||||||
// used in grade school multiplication
|
// used in grade school multiplication
|
||||||
template <uint16_t size>
|
template <limb_t size>
|
||||||
FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec<size> &x, limb_span y,
|
FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec<size> &x, limb_span y,
|
||||||
size_t start) noexcept {
|
limb_t start) noexcept {
|
||||||
// the effective x buffer is from `xstart..x.len()`, so exit early
|
// the effective x buffer is from `xstart..x.len()`, so exit early
|
||||||
// if we can't get that current range.
|
// if we can't get that current range.
|
||||||
if (x.len() < start || y.len() > x.len() - start) {
|
if (x.len() < start || y.len() > limb_t(x.len() - start)) {
|
||||||
FASTFLOAT_TRY(x.try_resize(y.len() + start, 0));
|
FASTFLOAT_TRY(x.try_resize(limb_t(y.len() + start), 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool carry = false;
|
bool carry = false;
|
||||||
for (size_t index = 0; index < y.len(); index++) {
|
for (limb_t index = 0; index < y.len(); ++index) {
|
||||||
limb xi = x[index + start];
|
limb xi = x[index + start];
|
||||||
limb yi = y[index];
|
limb yi = y[index];
|
||||||
bool c1 = false;
|
bool c1 = false;
|
||||||
@ -323,20 +318,20 @@ FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec<size> &x, limb_span y,
|
|||||||
|
|
||||||
// handle overflow
|
// handle overflow
|
||||||
if (carry) {
|
if (carry) {
|
||||||
FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start));
|
FASTFLOAT_TRY(small_add_from(x, 1, limb_t(y.len() + start)));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add bigint to bigint.
|
// add bigint to bigint.
|
||||||
template <uint16_t size>
|
template <limb_t size>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
|
||||||
large_add_from(stackvec<size> &x, limb_span y) noexcept {
|
large_add_from(stackvec<size> &x, limb_span y) noexcept {
|
||||||
return large_add_from(x, y, 0);
|
return large_add_from(x, y, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// grade-school multiplication algorithm
|
// grade-school multiplication algorithm
|
||||||
template <uint16_t size>
|
template <limb_t size>
|
||||||
FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept {
|
FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept {
|
||||||
limb_span xs = limb_span(x.data, x.len());
|
limb_span xs = limb_span(x.data, x.len());
|
||||||
stackvec<size> z(xs);
|
stackvec<size> z(xs);
|
||||||
@ -345,7 +340,7 @@ FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept {
|
|||||||
if (y.len() != 0) {
|
if (y.len() != 0) {
|
||||||
limb y0 = y[0];
|
limb y0 = y[0];
|
||||||
FASTFLOAT_TRY(small_mul(x, y0));
|
FASTFLOAT_TRY(small_mul(x, y0));
|
||||||
for (size_t index = 1; index < y.len(); index++) {
|
for (limb_t index = 1; index != y.len(); ++index) {
|
||||||
limb yi = y[index];
|
limb yi = y[index];
|
||||||
stackvec<size> zi;
|
stackvec<size> zi;
|
||||||
if (yi != 0) {
|
if (yi != 0) {
|
||||||
@ -364,7 +359,7 @@ FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// grade-school multiplication algorithm
|
// grade-school multiplication algorithm
|
||||||
template <uint16_t size>
|
template <limb_t size>
|
||||||
FASTFLOAT_CONSTEXPR20 bool large_mul(stackvec<size> &x, limb_span y) noexcept {
|
FASTFLOAT_CONSTEXPR20 bool large_mul(stackvec<size> &x, limb_span y) noexcept {
|
||||||
if (y.len() == 1) {
|
if (y.len() == 1) {
|
||||||
FASTFLOAT_TRY(small_mul(x, y[0]));
|
FASTFLOAT_TRY(small_mul(x, y[0]));
|
||||||
@ -375,7 +370,7 @@ FASTFLOAT_CONSTEXPR20 bool large_mul(stackvec<size> &x, limb_span y) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename = void> struct pow5_tables {
|
template <typename = void> struct pow5_tables {
|
||||||
static constexpr uint32_t large_step = 135;
|
static constexpr uint8_t large_step = 135;
|
||||||
static constexpr uint64_t small_power_of_5[] = {
|
static constexpr uint64_t small_power_of_5[] = {
|
||||||
1UL,
|
1UL,
|
||||||
5UL,
|
5UL,
|
||||||
@ -419,7 +414,7 @@ template <typename = void> struct pow5_tables {
|
|||||||
|
|
||||||
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
|
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
|
||||||
|
|
||||||
template <typename T> constexpr uint32_t pow5_tables<T>::large_step;
|
template <typename T> constexpr uint8_t pow5_tables<T>::large_step;
|
||||||
|
|
||||||
template <typename T> constexpr uint64_t pow5_tables<T>::small_power_of_5[];
|
template <typename T> constexpr uint64_t pow5_tables<T>::small_power_of_5[];
|
||||||
|
|
||||||
@ -435,14 +430,14 @@ struct bigint : pow5_tables<> {
|
|||||||
// storage of the limbs, in little-endian order.
|
// storage of the limbs, in little-endian order.
|
||||||
stackvec<bigint_limbs> vec;
|
stackvec<bigint_limbs> vec;
|
||||||
|
|
||||||
FASTFLOAT_CONSTEXPR20 bigint() : vec() {}
|
FASTFLOAT_CONSTEXPR20 bigint() noexcept : vec() {}
|
||||||
|
|
||||||
bigint(bigint const &) = delete;
|
bigint(bigint const &) = delete;
|
||||||
bigint &operator=(bigint const &) = delete;
|
bigint &operator=(bigint const &) = delete;
|
||||||
bigint(bigint &&) = delete;
|
bigint(bigint &&) = delete;
|
||||||
bigint &operator=(bigint &&other) = delete;
|
bigint &operator=(bigint &&other) = delete;
|
||||||
|
|
||||||
FASTFLOAT_CONSTEXPR20 bigint(uint64_t value) : vec() {
|
FASTFLOAT_CONSTEXPR20 bigint(uint64_t value) noexcept : vec() {
|
||||||
#ifdef FASTFLOAT_64BIT_LIMB
|
#ifdef FASTFLOAT_64BIT_LIMB
|
||||||
vec.push_unchecked(value);
|
vec.push_unchecked(value);
|
||||||
#else
|
#else
|
||||||
@ -493,7 +488,7 @@ struct bigint : pow5_tables<> {
|
|||||||
} else if (vec.len() < other.vec.len()) {
|
} else if (vec.len() < other.vec.len()) {
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
for (size_t index = vec.len(); index > 0; index--) {
|
for (limb_t index = vec.len(); index != 0; --index) {
|
||||||
limb xi = vec[index - 1];
|
limb xi = vec[index - 1];
|
||||||
limb yi = other.vec[index - 1];
|
limb yi = other.vec[index - 1];
|
||||||
if (xi > yi) {
|
if (xi > yi) {
|
||||||
@ -508,7 +503,7 @@ struct bigint : pow5_tables<> {
|
|||||||
|
|
||||||
// shift left each limb n bits, carrying over to the new limb
|
// shift left each limb n bits, carrying over to the new limb
|
||||||
// returns true if we were able to shift all the digits.
|
// returns true if we were able to shift all the digits.
|
||||||
FASTFLOAT_CONSTEXPR20 bool shl_bits(size_t n) noexcept {
|
FASTFLOAT_CONSTEXPR20 bool shl_bits(bigint_bits_t n) noexcept {
|
||||||
// Internally, for each item, we shift left by n, and add the previous
|
// Internally, for each item, we shift left by n, and add the previous
|
||||||
// right shifted limb-bits.
|
// right shifted limb-bits.
|
||||||
// For example, we transform (for u8) shifted left 2, to:
|
// For example, we transform (for u8) shifted left 2, to:
|
||||||
@ -517,10 +512,10 @@ struct bigint : pow5_tables<> {
|
|||||||
FASTFLOAT_DEBUG_ASSERT(n != 0);
|
FASTFLOAT_DEBUG_ASSERT(n != 0);
|
||||||
FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8);
|
FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8);
|
||||||
|
|
||||||
size_t shl = n;
|
bigint_bits_t const shl = n;
|
||||||
size_t shr = limb_bits - shl;
|
bigint_bits_t const shr = limb_bits - shl;
|
||||||
limb prev = 0;
|
limb prev = 0;
|
||||||
for (size_t index = 0; index < vec.len(); index++) {
|
for (limb_t index = 0; index != vec.len(); ++index) {
|
||||||
limb xi = vec[index];
|
limb xi = vec[index];
|
||||||
vec[index] = (xi << shl) | (prev >> shr);
|
vec[index] = (xi << shl) | (prev >> shr);
|
||||||
prev = xi;
|
prev = xi;
|
||||||
@ -534,11 +529,16 @@ struct bigint : pow5_tables<> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// move the limbs left by `n` limbs.
|
// move the limbs left by `n` limbs.
|
||||||
FASTFLOAT_CONSTEXPR20 bool shl_limbs(size_t n) noexcept {
|
FASTFLOAT_CONSTEXPR20 bool shl_limbs(bigint_bits_t n) noexcept {
|
||||||
FASTFLOAT_DEBUG_ASSERT(n != 0);
|
FASTFLOAT_DEBUG_ASSERT(n != 0);
|
||||||
if (n + vec.len() > vec.capacity()) {
|
if (n + vec.len() > vec.capacity()) {
|
||||||
|
// we can't shift more than the capacity of the vector.
|
||||||
return false;
|
return false;
|
||||||
} else if (!vec.is_empty()) {
|
}
|
||||||
|
if (vec.is_empty()) {
|
||||||
|
// nothing to do
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// move limbs
|
// move limbs
|
||||||
limb *dst = vec.data + n;
|
limb *dst = vec.data + n;
|
||||||
limb const *src = vec.data;
|
limb const *src = vec.data;
|
||||||
@ -547,17 +547,14 @@ struct bigint : pow5_tables<> {
|
|||||||
limb *first = vec.data;
|
limb *first = vec.data;
|
||||||
limb *last = first + n;
|
limb *last = first + n;
|
||||||
::std::fill(first, last, 0);
|
::std::fill(first, last, 0);
|
||||||
vec.set_len(n + vec.len());
|
vec.set_len(limb_t(n + vec.len()));
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// move the limbs left by `n` bits.
|
// move the limbs left by `n` bits.
|
||||||
FASTFLOAT_CONSTEXPR20 bool shl(size_t n) noexcept {
|
FASTFLOAT_CONSTEXPR20 bool shl(bigint_bits_t n) noexcept {
|
||||||
size_t rem = n % limb_bits;
|
bigint_bits_t const rem = n % limb_bits;
|
||||||
size_t div = n / limb_bits;
|
bigint_bits_t const div = n / limb_bits;
|
||||||
if (rem != 0) {
|
if (rem != 0) {
|
||||||
FASTFLOAT_TRY(shl_bits(rem));
|
FASTFLOAT_TRY(shl_bits(rem));
|
||||||
}
|
}
|
||||||
@ -568,10 +565,11 @@ struct bigint : pow5_tables<> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get the number of leading zeros in the bigint.
|
// get the number of leading zeros in the bigint.
|
||||||
FASTFLOAT_CONSTEXPR20 int ctlz() const noexcept {
|
FASTFLOAT_CONSTEXPR20 limb_t ctlz() const noexcept {
|
||||||
if (vec.is_empty()) {
|
if (vec.is_empty()) {
|
||||||
|
// empty vector, no bits, no zeros.
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
}
|
||||||
#ifdef FASTFLOAT_64BIT_LIMB
|
#ifdef FASTFLOAT_64BIT_LIMB
|
||||||
return leading_zeroes(vec.rindex(0));
|
return leading_zeroes(vec.rindex(0));
|
||||||
#else
|
#else
|
||||||
@ -580,12 +578,11 @@ struct bigint : pow5_tables<> {
|
|||||||
return leading_zeroes(r0 << 32);
|
return leading_zeroes(r0 << 32);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// get the number of bits in the bigint.
|
// get the number of bits in the bigint.
|
||||||
FASTFLOAT_CONSTEXPR20 int bit_length() const noexcept {
|
FASTFLOAT_CONSTEXPR20 bigint_bits_t bit_length() const noexcept {
|
||||||
int lz = ctlz();
|
limb_t lz = ctlz();
|
||||||
return int(limb_bits * vec.len()) - lz;
|
return static_cast<fast_float::bigint_bits_t>(limb_bits * vec.len() - lz);
|
||||||
}
|
}
|
||||||
|
|
||||||
FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept { return small_mul(vec, y); }
|
FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept { return small_mul(vec, y); }
|
||||||
@ -593,23 +590,25 @@ struct bigint : pow5_tables<> {
|
|||||||
FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept { return small_add(vec, y); }
|
FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept { return small_add(vec, y); }
|
||||||
|
|
||||||
// multiply as if by 2 raised to a power.
|
// multiply as if by 2 raised to a power.
|
||||||
FASTFLOAT_CONSTEXPR20 bool pow2(uint32_t exp) noexcept { return shl(exp); }
|
FASTFLOAT_CONSTEXPR20 bool pow2(am_pow_t exp) noexcept {
|
||||||
|
return shl(static_cast<fast_float::bigint_bits_t>(exp));
|
||||||
|
}
|
||||||
|
|
||||||
// multiply as if by 5 raised to a power.
|
// multiply as if by 5 raised to a power.
|
||||||
FASTFLOAT_CONSTEXPR20 bool pow5(uint32_t exp) noexcept {
|
FASTFLOAT_CONSTEXPR20 bool pow5(am_pow_t exp) noexcept {
|
||||||
// multiply by a power of 5
|
// multiply by a power of 5
|
||||||
size_t large_length = sizeof(large_power_of_5) / sizeof(limb);
|
limb_t const large_length = sizeof(large_power_of_5) / sizeof(limb);
|
||||||
limb_span large = limb_span(large_power_of_5, large_length);
|
limb_span const large = limb_span(large_power_of_5, large_length);
|
||||||
while (exp >= large_step) {
|
while (exp >= large_step) {
|
||||||
FASTFLOAT_TRY(large_mul(vec, large));
|
FASTFLOAT_TRY(large_mul(vec, large));
|
||||||
exp -= large_step;
|
exp -= large_step;
|
||||||
}
|
}
|
||||||
#ifdef FASTFLOAT_64BIT_LIMB
|
#ifdef FASTFLOAT_64BIT_LIMB
|
||||||
uint32_t small_step = 27;
|
limb_t constexpr small_step = 27;
|
||||||
limb max_native = 7450580596923828125UL;
|
limb constexpr max_native = 7450580596923828125UL;
|
||||||
#else
|
#else
|
||||||
uint32_t small_step = 13;
|
limb_t constexpr small_step = 13;
|
||||||
limb max_native = 1220703125U;
|
limb constexpr max_native = 1220703125U;
|
||||||
#endif
|
#endif
|
||||||
while (exp >= small_step) {
|
while (exp >= small_step) {
|
||||||
FASTFLOAT_TRY(small_mul(vec, max_native));
|
FASTFLOAT_TRY(small_mul(vec, max_native));
|
||||||
@ -627,7 +626,7 @@ struct bigint : pow5_tables<> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// multiply as if by 10 raised to a power.
|
// multiply as if by 10 raised to a power.
|
||||||
FASTFLOAT_CONSTEXPR20 bool pow10(uint32_t exp) noexcept {
|
FASTFLOAT_CONSTEXPR20 bool pow10(am_pow_t exp) noexcept {
|
||||||
FASTFLOAT_TRY(pow5(exp));
|
FASTFLOAT_TRY(pow5(exp));
|
||||||
return pow2(exp);
|
return pow2(exp);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,8 +23,16 @@
|
|||||||
#if defined(__cpp_lib_is_constant_evaluated) && \
|
#if defined(__cpp_lib_is_constant_evaluated) && \
|
||||||
__cpp_lib_is_constant_evaluated >= 201811L
|
__cpp_lib_is_constant_evaluated >= 201811L
|
||||||
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1
|
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1
|
||||||
|
#define FASTFLOAT_CONSTEVAL consteval
|
||||||
#else
|
#else
|
||||||
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0
|
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0
|
||||||
|
#define FASTFLOAT_CONSTEVAL FASTFLOAT_CONSTEXPR14
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__cpp_lib_byteswap)
|
||||||
|
#define FASTFLOAT_HAS_BYTESWAP 1
|
||||||
|
#else
|
||||||
|
#define FASTFLOAT_HAS_BYTESWAP 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L
|
#if defined(__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L
|
||||||
@ -50,4 +58,11 @@
|
|||||||
#define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 1
|
#define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// For support attribute [[assume]] is declared in P1774
|
||||||
|
#if defined(__cpp_attrubute_assume)
|
||||||
|
#define FASTFLOAT_ASSUME(expr) [[assume(expr)]]
|
||||||
|
#else
|
||||||
|
#define FASTFLOAT_ASSUME(expr)
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H
|
#endif // FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H
|
||||||
|
|||||||
@ -17,17 +17,17 @@ namespace fast_float {
|
|||||||
// most significant bits and the low part corresponding to the least significant
|
// most significant bits and the low part corresponding to the least significant
|
||||||
// bits.
|
// bits.
|
||||||
//
|
//
|
||||||
template <int bit_precision>
|
template <limb_t bit_precision>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128
|
||||||
compute_product_approximation(int64_t q, uint64_t w) {
|
compute_product_approximation(int64_t q, uint64_t w) noexcept {
|
||||||
int const index = 2 * int(q - powers::smallest_power_of_five);
|
am_pow_t const index = 2 * am_pow_t(q - powers::smallest_power_of_five);
|
||||||
// For small values of q, e.g., q in [0,27], the answer is always exact
|
// For small values of q, e.g., q in [0,27], the answer is always exact
|
||||||
// because The line value128 firstproduct = full_multiplication(w,
|
// because The line value128 firstproduct = full_multiplication(w,
|
||||||
// power_of_five_128[index]); gives the exact answer.
|
// power_of_five_128[index]); gives the exact answer.
|
||||||
value128 firstproduct =
|
value128 firstproduct =
|
||||||
full_multiplication(w, powers::power_of_five_128[index]);
|
full_multiplication(w, powers::power_of_five_128[index]);
|
||||||
static_assert((bit_precision >= 0) && (bit_precision <= 64),
|
static_assert((bit_precision >= 0) && (bit_precision <= 64),
|
||||||
" precision should be in (0,64]");
|
" precision should be in [0,64]");
|
||||||
constexpr uint64_t precision_mask =
|
constexpr uint64_t precision_mask =
|
||||||
(bit_precision < 64) ? (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision)
|
(bit_precision < 64) ? (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision)
|
||||||
: uint64_t(0xFFFFFFFFFFFFFFFF);
|
: uint64_t(0xFFFFFFFFFFFFFFFF);
|
||||||
@ -40,7 +40,7 @@ compute_product_approximation(int64_t q, uint64_t w) {
|
|||||||
full_multiplication(w, powers::power_of_five_128[index + 1]);
|
full_multiplication(w, powers::power_of_five_128[index + 1]);
|
||||||
firstproduct.low += secondproduct.high;
|
firstproduct.low += secondproduct.high;
|
||||||
if (secondproduct.high > firstproduct.low) {
|
if (secondproduct.high > firstproduct.low) {
|
||||||
firstproduct.high++;
|
++firstproduct.high;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return firstproduct;
|
return firstproduct;
|
||||||
@ -62,7 +62,7 @@ namespace detail {
|
|||||||
* where
|
* where
|
||||||
* p = log(5**-q)/log(2) = -q * log(5)/log(2)
|
* p = log(5**-q)/log(2) = -q * log(5)/log(2)
|
||||||
*/
|
*/
|
||||||
constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept {
|
constexpr fastfloat_really_inline am_pow_t power(am_pow_t q) noexcept {
|
||||||
return (((152170 + 65536) * q) >> 16) + 63;
|
return (((152170 + 65536) * q) >> 16) + 63;
|
||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
@ -71,12 +71,13 @@ constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept {
|
|||||||
// for significant digits already multiplied by 10 ** q.
|
// for significant digits already multiplied by 10 ** q.
|
||||||
template <typename binary>
|
template <typename binary>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 adjusted_mantissa
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 adjusted_mantissa
|
||||||
compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
|
compute_error_scaled(int64_t q, uint64_t w, limb_t lz) noexcept {
|
||||||
int hilz = int(w >> 63) ^ 1;
|
limb_t const hilz = static_cast<limb_t>((w >> 63) ^ 1);
|
||||||
adjusted_mantissa answer;
|
adjusted_mantissa answer;
|
||||||
answer.mantissa = w << hilz;
|
answer.mantissa = w << hilz;
|
||||||
int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent();
|
constexpr am_pow_t bias =
|
||||||
answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 +
|
binary::mantissa_explicit_bits() - binary::minimum_exponent();
|
||||||
|
answer.power2 = am_pow_t(detail::power(am_pow_t(q)) + bias - hilz - lz - 62 +
|
||||||
invalid_am_bias);
|
invalid_am_bias);
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
@ -86,7 +87,7 @@ compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
|
|||||||
template <typename binary>
|
template <typename binary>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
|
||||||
compute_error(int64_t q, uint64_t w) noexcept {
|
compute_error(int64_t q, uint64_t w) noexcept {
|
||||||
int lz = leading_zeroes(w);
|
limb_t const lz = leading_zeroes(w);
|
||||||
w <<= lz;
|
w <<= lz;
|
||||||
value128 product =
|
value128 product =
|
||||||
compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
|
compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
|
||||||
@ -118,7 +119,7 @@ compute_float(int64_t q, uint64_t w) noexcept {
|
|||||||
// powers::largest_power_of_five].
|
// powers::largest_power_of_five].
|
||||||
|
|
||||||
// We want the most significant bit of i to be 1. Shift if needed.
|
// We want the most significant bit of i to be 1. Shift if needed.
|
||||||
int lz = leading_zeroes(w);
|
limb_t const lz = leading_zeroes(w);
|
||||||
w <<= lz;
|
w <<= lz;
|
||||||
|
|
||||||
// The required precision is binary::mantissa_explicit_bits() + 3 because
|
// The required precision is binary::mantissa_explicit_bits() + 3 because
|
||||||
@ -138,14 +139,15 @@ compute_float(int64_t q, uint64_t w) noexcept {
|
|||||||
// branchless approach: value128 product = compute_product(q, w); but in
|
// branchless approach: value128 product = compute_product(q, w); but in
|
||||||
// practice, we can win big with the compute_product_approximation if its
|
// practice, we can win big with the compute_product_approximation if its
|
||||||
// additional branch is easily predicted. Which is best is data specific.
|
// additional branch is easily predicted. Which is best is data specific.
|
||||||
int upperbit = int(product.high >> 63);
|
limb_t const upperbit = limb_t(product.high >> 63);
|
||||||
int shift = upperbit + 64 - binary::mantissa_explicit_bits() - 3;
|
limb_t const shift =
|
||||||
|
limb_t(upperbit + 64 - binary::mantissa_explicit_bits() - 3);
|
||||||
|
|
||||||
answer.mantissa = product.high >> shift;
|
answer.mantissa = product.high >> shift;
|
||||||
|
|
||||||
answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz -
|
answer.power2 = am_pow_t(detail::power(am_pow_t(q)) + upperbit - lz -
|
||||||
binary::minimum_exponent());
|
binary::minimum_exponent());
|
||||||
if (answer.power2 <= 0) { // we have a subnormal?
|
if (answer.power2 <= 0) { // we have a subnormal or very small value.
|
||||||
// Here have that answer.power2 <= 0 so -answer.power2 >= 0
|
// Here have that answer.power2 <= 0 so -answer.power2 >= 0
|
||||||
if (-answer.power2 + 1 >=
|
if (-answer.power2 + 1 >=
|
||||||
64) { // if we have more than 64 bits below the minimum exponent, you
|
64) { // if we have more than 64 bits below the minimum exponent, you
|
||||||
@ -155,6 +157,7 @@ compute_float(int64_t q, uint64_t w) noexcept {
|
|||||||
// result should be zero
|
// result should be zero
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
// We have a subnormal number. We need to shift the mantissa to the right
|
||||||
// next line is safe because -answer.power2 + 1 < 64
|
// next line is safe because -answer.power2 + 1 < 64
|
||||||
answer.mantissa >>= -answer.power2 + 1;
|
answer.mantissa >>= -answer.power2 + 1;
|
||||||
// Thankfully, we can't have both "round-to-even" and subnormals because
|
// Thankfully, we can't have both "round-to-even" and subnormals because
|
||||||
@ -170,7 +173,7 @@ compute_float(int64_t q, uint64_t w) noexcept {
|
|||||||
// subnormal, but we can only know this after rounding.
|
// subnormal, but we can only know this after rounding.
|
||||||
// So we only declare a subnormal if we are smaller than the threshold.
|
// So we only declare a subnormal if we are smaller than the threshold.
|
||||||
answer.power2 =
|
answer.power2 =
|
||||||
(answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits()))
|
(answer.mantissa < (am_mant_t(1) << binary::mantissa_explicit_bits()))
|
||||||
? 0
|
? 0
|
||||||
: 1;
|
: 1;
|
||||||
return answer;
|
return answer;
|
||||||
@ -188,18 +191,18 @@ compute_float(int64_t q, uint64_t w) noexcept {
|
|||||||
// ... we dropped out only zeroes. But if this happened, then we can go
|
// ... we dropped out only zeroes. But if this happened, then we can go
|
||||||
// back!!!
|
// back!!!
|
||||||
if ((answer.mantissa << shift) == product.high) {
|
if ((answer.mantissa << shift) == product.high) {
|
||||||
answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up
|
answer.mantissa &= ~am_mant_t(1); // flip it so that we do not round up
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
answer.mantissa += (answer.mantissa & 1); // round up
|
answer.mantissa += (answer.mantissa & 1); // round up
|
||||||
answer.mantissa >>= 1;
|
answer.mantissa >>= 1;
|
||||||
if (answer.mantissa >= (uint64_t(2) << binary::mantissa_explicit_bits())) {
|
if (answer.mantissa >= (am_mant_t(2) << binary::mantissa_explicit_bits())) {
|
||||||
answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits());
|
answer.mantissa = (am_mant_t(1) << binary::mantissa_explicit_bits());
|
||||||
answer.power2++; // undo previous addition
|
++answer.power2; // undo previous addition
|
||||||
}
|
}
|
||||||
|
|
||||||
answer.mantissa &= ~(uint64_t(1) << binary::mantissa_explicit_bits());
|
answer.mantissa &= ~(am_mant_t(1) << binary::mantissa_explicit_bits());
|
||||||
if (answer.power2 >= binary::infinite_power()) { // infinity
|
if (answer.power2 >= binary::infinite_power()) { // infinity
|
||||||
answer.power2 = binary::infinite_power();
|
answer.power2 = binary::infinite_power();
|
||||||
answer.mantissa = 0;
|
answer.mantissa = 0;
|
||||||
|
|||||||
@ -38,8 +38,8 @@ constexpr static uint64_t powers_of_ten_uint64[] = {1UL,
|
|||||||
// this algorithm is not even close to optimized, but it has no practical
|
// this algorithm is not even close to optimized, but it has no practical
|
||||||
// effect on performance: in order to have a faster algorithm, we'd need
|
// effect on performance: in order to have a faster algorithm, we'd need
|
||||||
// to slow down performance for faster algorithms, and this is still fast.
|
// to slow down performance for faster algorithms, and this is still fast.
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int32_t
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 am_pow_t
|
||||||
scientific_exponent(uint64_t mantissa, int32_t exponent) noexcept {
|
scientific_exponent(am_mant_t mantissa, am_pow_t exponent) noexcept {
|
||||||
while (mantissa >= 10000) {
|
while (mantissa >= 10000) {
|
||||||
mantissa /= 10000;
|
mantissa /= 10000;
|
||||||
exponent += 4;
|
exponent += 4;
|
||||||
@ -58,18 +58,22 @@ scientific_exponent(uint64_t mantissa, int32_t exponent) noexcept {
|
|||||||
// this converts a native floating-point number to an extended-precision float.
|
// this converts a native floating-point number to an extended-precision float.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
|
||||||
to_extended(T value) noexcept {
|
to_extended(T const &value) noexcept {
|
||||||
using equiv_uint = equiv_uint_t<T>;
|
using equiv_uint = equiv_uint_t<T>;
|
||||||
constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
|
constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
|
||||||
constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
|
constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
|
||||||
constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
|
constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
|
||||||
|
|
||||||
adjusted_mantissa am;
|
adjusted_mantissa am;
|
||||||
int32_t bias = binary_format<T>::mantissa_explicit_bits() -
|
constexpr am_pow_t bias = binary_format<T>::mantissa_explicit_bits() -
|
||||||
binary_format<T>::minimum_exponent();
|
binary_format<T>::minimum_exponent();
|
||||||
equiv_uint bits;
|
equiv_uint bits;
|
||||||
#if FASTFLOAT_HAS_BIT_CAST
|
#if FASTFLOAT_HAS_BIT_CAST
|
||||||
bits = std::bit_cast<equiv_uint>(value);
|
bits =
|
||||||
|
#if FASTFLOAT_HAS_BIT_CAST == 1
|
||||||
|
std::
|
||||||
|
#endif
|
||||||
|
bit_cast<equiv_uint>(value);
|
||||||
#else
|
#else
|
||||||
::memcpy(&bits, &value, sizeof(T));
|
::memcpy(&bits, &value, sizeof(T));
|
||||||
#endif
|
#endif
|
||||||
@ -79,7 +83,7 @@ to_extended(T value) noexcept {
|
|||||||
am.mantissa = bits & mantissa_mask;
|
am.mantissa = bits & mantissa_mask;
|
||||||
} else {
|
} else {
|
||||||
// normal
|
// normal
|
||||||
am.power2 = int32_t((bits & exponent_mask) >>
|
am.power2 = am_pow_t((bits & exponent_mask) >>
|
||||||
binary_format<T>::mantissa_explicit_bits());
|
binary_format<T>::mantissa_explicit_bits());
|
||||||
am.power2 -= bias;
|
am.power2 -= bias;
|
||||||
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
|
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
|
||||||
@ -93,7 +97,7 @@ to_extended(T value) noexcept {
|
|||||||
// halfway between b and b+u.
|
// halfway between b and b+u.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
|
||||||
to_extended_halfway(T value) noexcept {
|
to_extended_halfway(T const &value) noexcept {
|
||||||
adjusted_mantissa am = to_extended(value);
|
adjusted_mantissa am = to_extended(value);
|
||||||
am.mantissa <<= 1;
|
am.mantissa <<= 1;
|
||||||
am.mantissa += 1;
|
am.mantissa += 1;
|
||||||
@ -105,14 +109,15 @@ to_extended_halfway(T value) noexcept {
|
|||||||
template <typename T, typename callback>
|
template <typename T, typename callback>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
|
||||||
callback cb) noexcept {
|
callback cb) noexcept {
|
||||||
int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1;
|
constexpr am_pow_t mantissa_shift =
|
||||||
|
64 - binary_format<T>::mantissa_explicit_bits() - 1;
|
||||||
if (-am.power2 >= mantissa_shift) {
|
if (-am.power2 >= mantissa_shift) {
|
||||||
// have a denormal float
|
// have a denormal float
|
||||||
int32_t shift = -am.power2 + 1;
|
am_pow_t shift = -am.power2 + 1;
|
||||||
cb(am, std::min<int32_t>(shift, 64));
|
cb(am, std::min<am_pow_t>(shift, 64));
|
||||||
// check for round-up: if rounding-nearest carried us to the hidden bit.
|
// check for round-up: if rounding-nearest carried us to the hidden bit.
|
||||||
am.power2 = (am.mantissa <
|
am.power2 = (am.mantissa <
|
||||||
(uint64_t(1) << binary_format<T>::mantissa_explicit_bits()))
|
(am_mant_t(1) << binary_format<T>::mantissa_explicit_bits()))
|
||||||
? 0
|
? 0
|
||||||
: 1;
|
: 1;
|
||||||
return;
|
return;
|
||||||
@ -123,13 +128,13 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
|
|||||||
|
|
||||||
// check for carry
|
// check for carry
|
||||||
if (am.mantissa >=
|
if (am.mantissa >=
|
||||||
(uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) {
|
(am_mant_t(2) << binary_format<T>::mantissa_explicit_bits())) {
|
||||||
am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
|
am.mantissa = (am_mant_t(1) << binary_format<T>::mantissa_explicit_bits());
|
||||||
am.power2++;
|
++am.power2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for infinite: we could have carried to an infinite power
|
// check for infinite: we could have carried to an infinite power
|
||||||
am.mantissa &= ~(uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
|
am.mantissa &= ~(am_mant_t(1) << binary_format<T>::mantissa_explicit_bits());
|
||||||
if (am.power2 >= binary_format<T>::infinite_power()) {
|
if (am.power2 >= binary_format<T>::infinite_power()) {
|
||||||
am.power2 = binary_format<T>::infinite_power();
|
am.power2 = binary_format<T>::infinite_power();
|
||||||
am.mantissa = 0;
|
am.mantissa = 0;
|
||||||
@ -138,11 +143,12 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
|
|||||||
|
|
||||||
template <typename callback>
|
template <typename callback>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
|
||||||
round_nearest_tie_even(adjusted_mantissa &am, int32_t shift,
|
round_nearest_tie_even(adjusted_mantissa &am, am_pow_t shift,
|
||||||
callback cb) noexcept {
|
callback cb) noexcept {
|
||||||
uint64_t const mask = (shift == 64) ? UINT64_MAX : (uint64_t(1) << shift) - 1;
|
am_mant_t const mask =
|
||||||
uint64_t const halfway = (shift == 0) ? 0 : uint64_t(1) << (shift - 1);
|
(shift == 64) ? UINT64_MAX : (am_mant_t(1) << shift) - 1;
|
||||||
uint64_t truncated_bits = am.mantissa & mask;
|
am_mant_t const halfway = (shift == 0) ? 0 : am_mant_t(1) << (shift - 1);
|
||||||
|
am_mant_t truncated_bits = am.mantissa & mask;
|
||||||
bool is_above = truncated_bits > halfway;
|
bool is_above = truncated_bits > halfway;
|
||||||
bool is_halfway = truncated_bits == halfway;
|
bool is_halfway = truncated_bits == halfway;
|
||||||
|
|
||||||
@ -155,11 +161,11 @@ round_nearest_tie_even(adjusted_mantissa &am, int32_t shift,
|
|||||||
am.power2 += shift;
|
am.power2 += shift;
|
||||||
|
|
||||||
bool is_odd = (am.mantissa & 1) == 1;
|
bool is_odd = (am.mantissa & 1) == 1;
|
||||||
am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above));
|
am.mantissa += am_mant_t(cb(is_odd, is_halfway, is_above));
|
||||||
}
|
}
|
||||||
|
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
|
||||||
round_down(adjusted_mantissa &am, int32_t shift) noexcept {
|
round_down(adjusted_mantissa &am, am_pow_t shift) noexcept {
|
||||||
if (shift == 64) {
|
if (shift == 64) {
|
||||||
am.mantissa = 0;
|
am.mantissa = 0;
|
||||||
} else {
|
} else {
|
||||||
@ -171,9 +177,9 @@ round_down(adjusted_mantissa &am, int32_t shift) noexcept {
|
|||||||
template <typename UC>
|
template <typename UC>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
||||||
skip_zeros(UC const *&first, UC const *last) noexcept {
|
skip_zeros(UC const *&first, UC const *last) noexcept {
|
||||||
uint64_t val;
|
|
||||||
while (!cpp20_and_in_constexpr() &&
|
while (!cpp20_and_in_constexpr() &&
|
||||||
std::distance(first, last) >= int_cmp_len<UC>()) {
|
std::distance(first, last) >= int_cmp_len<UC>()) {
|
||||||
|
uint64_t val;
|
||||||
::memcpy(&val, first, sizeof(uint64_t));
|
::memcpy(&val, first, sizeof(uint64_t));
|
||||||
if (val != int_cmp_zeros<UC>()) {
|
if (val != int_cmp_zeros<UC>()) {
|
||||||
break;
|
break;
|
||||||
@ -184,7 +190,7 @@ skip_zeros(UC const *&first, UC const *last) noexcept {
|
|||||||
if (*first != UC('0')) {
|
if (*first != UC('0')) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
first++;
|
++first;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,9 +200,9 @@ template <typename UC>
|
|||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
|
||||||
is_truncated(UC const *first, UC const *last) noexcept {
|
is_truncated(UC const *first, UC const *last) noexcept {
|
||||||
// do 8-bit optimizations, can just compare to 8 literal 0s.
|
// do 8-bit optimizations, can just compare to 8 literal 0s.
|
||||||
uint64_t val;
|
|
||||||
while (!cpp20_and_in_constexpr() &&
|
while (!cpp20_and_in_constexpr() &&
|
||||||
std::distance(first, last) >= int_cmp_len<UC>()) {
|
std::distance(first, last) >= int_cmp_len<UC>()) {
|
||||||
|
uint64_t val;
|
||||||
::memcpy(&val, first, sizeof(uint64_t));
|
::memcpy(&val, first, sizeof(uint64_t));
|
||||||
if (val != int_cmp_zeros<UC>()) {
|
if (val != int_cmp_zeros<UC>()) {
|
||||||
return true;
|
return true;
|
||||||
@ -220,8 +226,8 @@ is_truncated(span<UC const> s) noexcept {
|
|||||||
|
|
||||||
template <typename UC>
|
template <typename UC>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
||||||
parse_eight_digits(UC const *&p, limb &value, size_t &counter,
|
parse_eight_digits(UC const *&p, limb &value, am_digits &counter,
|
||||||
size_t &count) noexcept {
|
am_digits &count) noexcept {
|
||||||
value = value * 100000000 + parse_eight_digits_unrolled(p);
|
value = value * 100000000 + parse_eight_digits_unrolled(p);
|
||||||
p += 8;
|
p += 8;
|
||||||
counter += 8;
|
counter += 8;
|
||||||
@ -230,12 +236,12 @@ parse_eight_digits(UC const *&p, limb &value, size_t &counter,
|
|||||||
|
|
||||||
template <typename UC>
|
template <typename UC>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
|
||||||
parse_one_digit(UC const *&p, limb &value, size_t &counter,
|
parse_one_digit(UC const *&p, limb &value, am_digits &counter,
|
||||||
size_t &count) noexcept {
|
am_digits &count) noexcept {
|
||||||
value = value * 10 + limb(*p - UC('0'));
|
value = value * 10 + limb(*p - UC('0'));
|
||||||
p++;
|
++p;
|
||||||
counter++;
|
++counter;
|
||||||
count++;
|
++count;
|
||||||
}
|
}
|
||||||
|
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
||||||
@ -245,28 +251,28 @@ add_native(bigint &big, limb power, limb value) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
||||||
round_up_bigint(bigint &big, size_t &count) noexcept {
|
round_up_bigint(bigint &big, am_digits &count) noexcept {
|
||||||
// need to round-up the digits, but need to avoid rounding
|
// need to round-up the digits, but need to avoid rounding
|
||||||
// ....9999 to ...10000, which could cause a false halfway point.
|
// ....9999 to ...10000, which could cause a false halfway point.
|
||||||
add_native(big, 10, 1);
|
add_native(big, 10, 1);
|
||||||
count++;
|
++count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse the significant digits into a big integer
|
// parse the significant digits into a big integer
|
||||||
template <typename UC>
|
template <typename T, typename UC>
|
||||||
inline FASTFLOAT_CONSTEXPR20 void
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 am_digits
|
||||||
parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
|
parse_mantissa(bigint &result, const parsed_number_string_t<UC> &num) noexcept {
|
||||||
size_t max_digits, size_t &digits) noexcept {
|
|
||||||
// try to minimize the number of big integer and scalar multiplication.
|
// try to minimize the number of big integer and scalar multiplication.
|
||||||
// therefore, try to parse 8 digits at a time, and multiply by the largest
|
// therefore, try to parse 8 digits at a time, and multiply by the largest
|
||||||
// scalar value (9 or 19 digits) for each step.
|
// scalar value (9 or 19 digits) for each step.
|
||||||
size_t counter = 0;
|
constexpr am_digits max_digits = binary_format<T>::max_digits();
|
||||||
digits = 0;
|
am_digits counter = 0;
|
||||||
|
am_digits digits = 0;
|
||||||
limb value = 0;
|
limb value = 0;
|
||||||
#ifdef FASTFLOAT_64BIT_LIMB
|
#ifdef FASTFLOAT_64BIT_LIMB
|
||||||
size_t step = 19;
|
constexpr am_digits step = 19;
|
||||||
#else
|
#else
|
||||||
size_t step = 9;
|
constexpr am_digits step = 9;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// process all integer digits.
|
// process all integer digits.
|
||||||
@ -277,10 +283,10 @@ parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
|
|||||||
while (p != pend) {
|
while (p != pend) {
|
||||||
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) &&
|
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) &&
|
||||||
(max_digits - digits >= 8)) {
|
(max_digits - digits >= 8)) {
|
||||||
parse_eight_digits(p, value, counter, digits);
|
parse_eight_digits<UC>(p, value, counter, digits);
|
||||||
}
|
}
|
||||||
while (counter < step && p != pend && digits < max_digits) {
|
while (counter < step && p != pend && digits < max_digits) {
|
||||||
parse_one_digit(p, value, counter, digits);
|
parse_one_digit<UC>(p, value, counter, digits);
|
||||||
}
|
}
|
||||||
if (digits == max_digits) {
|
if (digits == max_digits) {
|
||||||
// add the temporary value, then check if we've truncated any digits
|
// add the temporary value, then check if we've truncated any digits
|
||||||
@ -292,7 +298,7 @@ parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
|
|||||||
if (truncated) {
|
if (truncated) {
|
||||||
round_up_bigint(result, digits);
|
round_up_bigint(result, digits);
|
||||||
}
|
}
|
||||||
return;
|
return digits;
|
||||||
} else {
|
} else {
|
||||||
add_native(result, limb(powers_of_ten_uint64[counter]), value);
|
add_native(result, limb(powers_of_ten_uint64[counter]), value);
|
||||||
counter = 0;
|
counter = 0;
|
||||||
@ -311,10 +317,10 @@ parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
|
|||||||
while (p != pend) {
|
while (p != pend) {
|
||||||
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) &&
|
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) &&
|
||||||
(max_digits - digits >= 8)) {
|
(max_digits - digits >= 8)) {
|
||||||
parse_eight_digits(p, value, counter, digits);
|
parse_eight_digits<UC>(p, value, counter, digits);
|
||||||
}
|
}
|
||||||
while (counter < step && p != pend && digits < max_digits) {
|
while (counter < step && p != pend && digits < max_digits) {
|
||||||
parse_one_digit(p, value, counter, digits);
|
parse_one_digit<UC>(p, value, counter, digits);
|
||||||
}
|
}
|
||||||
if (digits == max_digits) {
|
if (digits == max_digits) {
|
||||||
// add the temporary value, then check if we've truncated any digits
|
// add the temporary value, then check if we've truncated any digits
|
||||||
@ -323,7 +329,7 @@ parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
|
|||||||
if (truncated) {
|
if (truncated) {
|
||||||
round_up_bigint(result, digits);
|
round_up_bigint(result, digits);
|
||||||
}
|
}
|
||||||
return;
|
return digits;
|
||||||
} else {
|
} else {
|
||||||
add_native(result, limb(powers_of_ten_uint64[counter]), value);
|
add_native(result, limb(powers_of_ten_uint64[counter]), value);
|
||||||
counter = 0;
|
counter = 0;
|
||||||
@ -335,20 +341,21 @@ parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
|
|||||||
if (counter != 0) {
|
if (counter != 0) {
|
||||||
add_native(result, limb(powers_of_ten_uint64[counter]), value);
|
add_native(result, limb(powers_of_ten_uint64[counter]), value);
|
||||||
}
|
}
|
||||||
|
return digits;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
|
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa positive_digit_comp(
|
||||||
positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept {
|
bigint &bigmant, adjusted_mantissa am, am_pow_t const exponent) noexcept {
|
||||||
FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent)));
|
FASTFLOAT_ASSERT(bigmant.pow10(exponent));
|
||||||
adjusted_mantissa answer;
|
|
||||||
bool truncated;
|
bool truncated;
|
||||||
answer.mantissa = bigmant.hi64(truncated);
|
am.mantissa = bigmant.hi64(truncated);
|
||||||
int bias = binary_format<T>::mantissa_explicit_bits() -
|
constexpr am_pow_t bias = binary_format<T>::mantissa_explicit_bits() -
|
||||||
binary_format<T>::minimum_exponent();
|
binary_format<T>::minimum_exponent();
|
||||||
answer.power2 = bigmant.bit_length() - 64 + bias;
|
am.power2 =
|
||||||
|
static_cast<fast_float::am_pow_t>(bigmant.bit_length() - 64 + bias);
|
||||||
|
|
||||||
round<T>(answer, [truncated](adjusted_mantissa &a, int32_t shift) {
|
round<T>(am, [truncated](adjusted_mantissa &a, am_pow_t shift) {
|
||||||
round_nearest_tie_even(
|
round_nearest_tie_even(
|
||||||
a, shift,
|
a, shift,
|
||||||
[truncated](bool is_odd, bool is_halfway, bool is_above) -> bool {
|
[truncated](bool is_odd, bool is_halfway, bool is_above) -> bool {
|
||||||
@ -357,7 +364,7 @@ positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return answer;
|
return am;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the scaling here is quite simple: we have, for the real digits `m * 10^e`,
|
// the scaling here is quite simple: we have, for the real digits `m * 10^e`,
|
||||||
@ -367,38 +374,42 @@ positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept {
|
|||||||
// are of the same magnitude.
|
// are of the same magnitude.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
|
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
|
||||||
bigint &bigmant, adjusted_mantissa am, int32_t exponent) noexcept {
|
bigint &bigmant, adjusted_mantissa am, am_pow_t const exponent) noexcept {
|
||||||
bigint &real_digits = bigmant;
|
bigint &real_digits = bigmant;
|
||||||
int32_t real_exp = exponent;
|
am_pow_t const &real_exp = exponent;
|
||||||
|
|
||||||
// get the value of `b`, rounded down, and get a bigint representation of b+h
|
// get the value of `b`, rounded down, and get a bigint representation of
|
||||||
|
// b+h
|
||||||
adjusted_mantissa am_b = am;
|
adjusted_mantissa am_b = am;
|
||||||
// gcc7 buf: use a lambda to remove the noexcept qualifier bug with
|
// gcc7 bug: use a lambda to remove the noexcept qualifier bug with
|
||||||
// -Wnoexcept-type.
|
// -Wnoexcept-type.
|
||||||
round<T>(am_b,
|
round<T>(am_b,
|
||||||
[](adjusted_mantissa &a, int32_t shift) { round_down(a, shift); });
|
[](adjusted_mantissa &a, am_pow_t shift) { round_down(a, shift); });
|
||||||
T b;
|
T b;
|
||||||
to_float(false, am_b, b);
|
to_float(
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
false,
|
||||||
|
#endif
|
||||||
|
am_b, b);
|
||||||
adjusted_mantissa theor = to_extended_halfway(b);
|
adjusted_mantissa theor = to_extended_halfway(b);
|
||||||
bigint theor_digits(theor.mantissa);
|
bigint theor_digits(theor.mantissa);
|
||||||
int32_t theor_exp = theor.power2;
|
am_pow_t theor_exp = theor.power2;
|
||||||
|
|
||||||
// scale real digits and theor digits to be same power.
|
// scale real digits and theor digits to be same power.
|
||||||
int32_t pow2_exp = theor_exp - real_exp;
|
am_pow_t pow2_exp = theor_exp - real_exp;
|
||||||
uint32_t pow5_exp = uint32_t(-real_exp);
|
am_pow_t pow5_exp = -real_exp;
|
||||||
if (pow5_exp != 0) {
|
if (pow5_exp != 0) {
|
||||||
FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp));
|
FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp));
|
||||||
}
|
}
|
||||||
if (pow2_exp > 0) {
|
if (pow2_exp > 0) {
|
||||||
FASTFLOAT_ASSERT(theor_digits.pow2(uint32_t(pow2_exp)));
|
FASTFLOAT_ASSERT(theor_digits.pow2(pow2_exp));
|
||||||
} else if (pow2_exp < 0) {
|
} else if (pow2_exp < 0) {
|
||||||
FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp)));
|
FASTFLOAT_ASSERT(real_digits.pow2(-pow2_exp));
|
||||||
}
|
}
|
||||||
|
|
||||||
// compare digits, and use it to direct rounding
|
// compare digits, and use it to direct rounding
|
||||||
int ord = real_digits.compare(theor_digits);
|
int ord = real_digits.compare(theor_digits);
|
||||||
adjusted_mantissa answer = am;
|
round<T>(am, [ord](adjusted_mantissa &a, am_pow_t shift) {
|
||||||
round<T>(answer, [ord](adjusted_mantissa &a, int32_t shift) {
|
|
||||||
round_nearest_tie_even(
|
round_nearest_tie_even(
|
||||||
a, shift, [ord](bool is_odd, bool _, bool __) -> bool {
|
a, shift, [ord](bool is_odd, bool _, bool __) -> bool {
|
||||||
(void)_; // not needed, since we've done our comparison
|
(void)_; // not needed, since we've done our comparison
|
||||||
@ -413,7 +424,7 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return answer;
|
return am;
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse the significant digits as a big integer to unambiguously round
|
// parse the significant digits as a big integer to unambiguously round
|
||||||
@ -430,21 +441,19 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
|
|||||||
// the actual digits. we then compare the big integer representations
|
// the actual digits. we then compare the big integer representations
|
||||||
// of both, and use that to direct rounding.
|
// of both, and use that to direct rounding.
|
||||||
template <typename T, typename UC>
|
template <typename T, typename UC>
|
||||||
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
|
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa digit_comp(
|
||||||
digit_comp(parsed_number_string_t<UC> &num, adjusted_mantissa am) noexcept {
|
parsed_number_string_t<UC> const &num, adjusted_mantissa am) noexcept {
|
||||||
// remove the invalid exponent bias
|
// remove the invalid exponent bias
|
||||||
am.power2 -= invalid_am_bias;
|
am.power2 -= invalid_am_bias;
|
||||||
|
|
||||||
int32_t sci_exp =
|
am_pow_t const sci_exp = scientific_exponent(num.mantissa, num.exponent);
|
||||||
scientific_exponent(num.mantissa, static_cast<int32_t>(num.exponent));
|
|
||||||
size_t max_digits = binary_format<T>::max_digits();
|
|
||||||
size_t digits = 0;
|
|
||||||
bigint bigmant;
|
bigint bigmant;
|
||||||
parse_mantissa(bigmant, num, max_digits, digits);
|
am_digits const digits = parse_mantissa<T, UC>(bigmant, num);
|
||||||
// can't underflow, since digits is at most max_digits.
|
// can't underflow, since digits is at most max_digits.
|
||||||
int32_t exponent = sci_exp + 1 - int32_t(digits);
|
am_pow_t const exponent =
|
||||||
|
static_cast<am_pow_t>(sci_exp + 1 - static_cast<am_pow_t>(digits));
|
||||||
if (exponent >= 0) {
|
if (exponent >= 0) {
|
||||||
return positive_digit_comp<T>(bigmant, exponent);
|
return positive_digit_comp<T>(bigmant, am, exponent);
|
||||||
} else {
|
} else {
|
||||||
return negative_digit_comp<T>(bigmant, am, exponent);
|
return negative_digit_comp<T>(bigmant, am, exponent);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,7 +34,7 @@ template <typename T, typename UC = char,
|
|||||||
typename = FASTFLOAT_ENABLE_IF(is_supported_float_type<T>::value)>
|
typename = FASTFLOAT_ENABLE_IF(is_supported_float_type<T>::value)>
|
||||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||||
from_chars(UC const *first, UC const *last, T &value,
|
from_chars(UC const *first, UC const *last, T &value,
|
||||||
chars_format fmt = chars_format::general) noexcept;
|
chars_format const fmt = chars_format::general) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like from_chars, but accepts an `options` argument to govern number parsing.
|
* Like from_chars, but accepts an `options` argument to govern number parsing.
|
||||||
@ -43,7 +43,7 @@ from_chars(UC const *first, UC const *last, T &value,
|
|||||||
template <typename T, typename UC = char>
|
template <typename T, typename UC = char>
|
||||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||||
from_chars_advanced(UC const *first, UC const *last, T &value,
|
from_chars_advanced(UC const *first, UC const *last, T &value,
|
||||||
parse_options_t<UC> options) noexcept;
|
parse_options_t<UC> const &options) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function multiplies an integer number by a power of 10 and returns
|
* This function multiplies an integer number by a power of 10 and returns
|
||||||
@ -59,9 +59,11 @@ from_chars_advanced(UC const *first, UC const *last, T &value,
|
|||||||
* `new` or `malloc`).
|
* `new` or `malloc`).
|
||||||
*/
|
*/
|
||||||
FASTFLOAT_CONSTEXPR20 inline double
|
FASTFLOAT_CONSTEXPR20 inline double
|
||||||
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept;
|
integer_times_pow10(uint64_t const mantissa,
|
||||||
|
int16_t const decimal_exponent) noexcept;
|
||||||
FASTFLOAT_CONSTEXPR20 inline double
|
FASTFLOAT_CONSTEXPR20 inline double
|
||||||
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept;
|
integer_times_pow10(int64_t const mantissa,
|
||||||
|
int16_t const decimal_exponent) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function is a template overload of `integer_times_pow10()`
|
* This function is a template overload of `integer_times_pow10()`
|
||||||
@ -71,11 +73,13 @@ integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept;
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
FASTFLOAT_CONSTEXPR20
|
FASTFLOAT_CONSTEXPR20
|
||||||
typename std::enable_if<is_supported_float_type<T>::value, T>::type
|
typename std::enable_if<is_supported_float_type<T>::value, T>::type
|
||||||
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept;
|
integer_times_pow10(uint64_t const mantissa,
|
||||||
|
int16_t const decimal_exponent) noexcept;
|
||||||
template <typename T>
|
template <typename T>
|
||||||
FASTFLOAT_CONSTEXPR20
|
FASTFLOAT_CONSTEXPR20
|
||||||
typename std::enable_if<is_supported_float_type<T>::value, T>::type
|
typename std::enable_if<is_supported_float_type<T>::value, T>::type
|
||||||
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept;
|
integer_times_pow10(int64_t const mantissa,
|
||||||
|
int16_t const decimal_exponent) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* from_chars for integer types.
|
* from_chars for integer types.
|
||||||
@ -83,7 +87,8 @@ FASTFLOAT_CONSTEXPR20
|
|||||||
template <typename T, typename UC = char,
|
template <typename T, typename UC = char,
|
||||||
typename = FASTFLOAT_ENABLE_IF(is_supported_integer_type<T>::value)>
|
typename = FASTFLOAT_ENABLE_IF(is_supported_integer_type<T>::value)>
|
||||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||||
from_chars(UC const *first, UC const *last, T &value, int base = 10) noexcept;
|
from_chars(UC const *first, UC const *last, T &value,
|
||||||
|
int const base = 10) noexcept;
|
||||||
|
|
||||||
} // namespace fast_float
|
} // namespace fast_float
|
||||||
|
|
||||||
|
|||||||
@ -31,14 +31,14 @@ namespace fast_float {
|
|||||||
*/
|
*/
|
||||||
template <class unused = void> struct powers_template {
|
template <class unused = void> struct powers_template {
|
||||||
|
|
||||||
constexpr static int smallest_power_of_five =
|
constexpr static am_pow_t smallest_power_of_five =
|
||||||
binary_format<double>::smallest_power_of_ten();
|
binary_format<double>::smallest_power_of_ten();
|
||||||
constexpr static int largest_power_of_five =
|
constexpr static am_pow_t largest_power_of_five =
|
||||||
binary_format<double>::largest_power_of_ten();
|
binary_format<double>::largest_power_of_ten();
|
||||||
constexpr static int number_of_entries =
|
constexpr static am_digits number_of_entries =
|
||||||
2 * (largest_power_of_five - smallest_power_of_five + 1);
|
2 * (largest_power_of_five - smallest_power_of_five + 1);
|
||||||
// Powers of five from 5^-342 all the way to 5^308 rounded toward one.
|
// Powers of five from 5^-342 all the way to 5^308 rounded toward one.
|
||||||
constexpr static uint64_t power_of_five_128[number_of_entries] = {
|
constexpr static am_mant_t power_of_five_128[number_of_entries] = {
|
||||||
0xeef453d6923bd65a, 0x113faa2906a13b3f,
|
0xeef453d6923bd65a, 0x113faa2906a13b3f,
|
||||||
0x9558b4661b6565f8, 0x4ac7ca59a424c507,
|
0x9558b4661b6565f8, 0x4ac7ca59a424c507,
|
||||||
0xbaaee17fa23ebf76, 0x5d79bcf00d2df649,
|
0xbaaee17fa23ebf76, 0x5d79bcf00d2df649,
|
||||||
@ -696,7 +696,7 @@ template <class unused = void> struct powers_template {
|
|||||||
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
|
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
|
||||||
|
|
||||||
template <class unused>
|
template <class unused>
|
||||||
constexpr uint64_t
|
constexpr am_mant_t
|
||||||
powers_template<unused>::power_of_five_128[number_of_entries];
|
powers_template<unused>::power_of_five_128[number_of_entries];
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -33,26 +33,38 @@
|
|||||||
|
|
||||||
namespace fast_float {
|
namespace fast_float {
|
||||||
|
|
||||||
enum class chars_format : uint64_t;
|
// The number of digits in the mantissa.
|
||||||
|
typedef uint_fast16_t am_digits;
|
||||||
|
|
||||||
|
// The number of bits in the limb.
|
||||||
|
typedef uint_fast8_t limb_t;
|
||||||
|
|
||||||
|
typedef uint_fast8_t chars_format_t;
|
||||||
|
|
||||||
|
enum class chars_format : chars_format_t;
|
||||||
|
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
namespace detail {
|
namespace detail {
|
||||||
constexpr chars_format basic_json_fmt = chars_format(1 << 5);
|
constexpr chars_format basic_json_fmt = chars_format(1 << 4);
|
||||||
constexpr chars_format basic_fortran_fmt = chars_format(1 << 6);
|
constexpr chars_format basic_fortran_fmt = chars_format(1 << 5);
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
#endif
|
||||||
|
|
||||||
enum class chars_format : uint64_t {
|
enum class chars_format : chars_format_t {
|
||||||
scientific = 1 << 0,
|
scientific = 1 << 0,
|
||||||
fixed = 1 << 2,
|
fixed = 1 << 1,
|
||||||
hex = 1 << 3,
|
|
||||||
no_infnan = 1 << 4,
|
|
||||||
// RFC 8259: https://datatracker.ietf.org/doc/html/rfc8259#section-6
|
|
||||||
json = uint64_t(detail::basic_json_fmt) | fixed | scientific | no_infnan,
|
|
||||||
// Extension of RFC 8259 where, e.g., "inf" and "nan" are allowed.
|
|
||||||
json_or_infnan = uint64_t(detail::basic_json_fmt) | fixed | scientific,
|
|
||||||
fortran = uint64_t(detail::basic_fortran_fmt) | fixed | scientific,
|
|
||||||
general = fixed | scientific,
|
general = fixed | scientific,
|
||||||
allow_leading_plus = 1 << 7,
|
hex = 1 << 2,
|
||||||
skip_white_space = 1 << 8,
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
no_infnan = 1 << 3,
|
||||||
|
// RFC 8259: https://datatracker.ietf.org/doc/html/rfc8259#section-6
|
||||||
|
json = chars_format_t(detail::basic_json_fmt) | general | no_infnan,
|
||||||
|
// Extension of RFC 8259 where, e.g., "inf" and "nan" are allowed.
|
||||||
|
json_or_infnan = chars_format_t(detail::basic_json_fmt) | general,
|
||||||
|
fortran = chars_format_t(detail::basic_fortran_fmt) | general,
|
||||||
|
allow_leading_plus = 1 << 6,
|
||||||
|
skip_white_space = 1 << 7,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename UC> struct from_chars_result_t {
|
template <typename UC> struct from_chars_result_t {
|
||||||
@ -68,16 +80,19 @@ template <typename UC> struct from_chars_result_t {
|
|||||||
using from_chars_result = from_chars_result_t<char>;
|
using from_chars_result = from_chars_result_t<char>;
|
||||||
|
|
||||||
template <typename UC> struct parse_options_t {
|
template <typename UC> struct parse_options_t {
|
||||||
constexpr explicit parse_options_t(chars_format fmt = chars_format::general,
|
constexpr explicit parse_options_t(
|
||||||
UC dot = UC('.'), int b = 10)
|
chars_format const fmt = chars_format::general, UC const dot = UC('.'),
|
||||||
: format(fmt), decimal_point(dot), base(b) {}
|
uint_fast8_t const b = 10) noexcept
|
||||||
|
: format(fmt), decimal_point(dot), base(b) {
|
||||||
|
FASTFLOAT_ASSUME(base >= 2 && base <= 36);
|
||||||
|
}
|
||||||
|
|
||||||
/** Which number formats are accepted */
|
/** Which number formats are accepted */
|
||||||
chars_format format;
|
chars_format format;
|
||||||
/** The character used as decimal point */
|
/** The character used as decimal point */
|
||||||
UC decimal_point;
|
UC decimal_point;
|
||||||
/** The base used for integers */
|
/** The base used for integers */
|
||||||
int base;
|
uint_fast8_t base; /* only allowed from 2 to 36 */
|
||||||
};
|
};
|
||||||
|
|
||||||
using parse_options = parse_options_t<char>;
|
using parse_options = parse_options_t<char>;
|
||||||
@ -164,7 +179,8 @@ using parse_options = parse_options_t<char>;
|
|||||||
#if defined(__SSE2__) || (defined(FASTFLOAT_VISUAL_STUDIO) && \
|
#if defined(__SSE2__) || (defined(FASTFLOAT_VISUAL_STUDIO) && \
|
||||||
(defined(_M_AMD64) || defined(_M_X64) || \
|
(defined(_M_AMD64) || defined(_M_X64) || \
|
||||||
(defined(_M_IX86_FP) && _M_IX86_FP == 2)))
|
(defined(_M_IX86_FP) && _M_IX86_FP == 2)))
|
||||||
#define FASTFLOAT_SSE2 1
|
// try to fix error on x86 platform: disable SSE2 code
|
||||||
|
// #define FASTFLOAT_SSE2 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__aarch64__) || defined(_M_ARM64)
|
#if defined(__aarch64__) || defined(_M_ARM64)
|
||||||
@ -218,7 +234,7 @@ using parse_options = parse_options_t<char>;
|
|||||||
|
|
||||||
namespace fast_float {
|
namespace fast_float {
|
||||||
|
|
||||||
fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() {
|
fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() noexcept {
|
||||||
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED
|
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED
|
||||||
return std::is_constant_evaluated();
|
return std::is_constant_evaluated();
|
||||||
#else
|
#else
|
||||||
@ -267,12 +283,13 @@ struct is_supported_char_type
|
|||||||
> {
|
> {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
// Compares two ASCII strings in a case insensitive manner.
|
// Compares two ASCII strings in a case insensitive manner.
|
||||||
template <typename UC>
|
template <typename UC>
|
||||||
inline FASTFLOAT_CONSTEXPR14 bool
|
inline FASTFLOAT_CONSTEXPR14 bool
|
||||||
fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
|
fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
|
||||||
size_t length) {
|
uint8_t const length) noexcept {
|
||||||
for (size_t i = 0; i < length; ++i) {
|
for (uint8_t i = 0; i != length; ++i) {
|
||||||
UC const actual = actual_mixedcase[i];
|
UC const actual = actual_mixedcase[i];
|
||||||
if ((actual < 256 ? actual | 32 : actual) != expected_lowercase[i]) {
|
if ((actual < 256 ? actual | 32 : actual) != expected_lowercase[i]) {
|
||||||
return false;
|
return false;
|
||||||
@ -280,6 +297,7 @@ fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef FLT_EVAL_METHOD
|
#ifndef FLT_EVAL_METHOD
|
||||||
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
|
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
|
||||||
@ -288,15 +306,16 @@ fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
|
|||||||
// a pointer and a length to a contiguous block of memory
|
// a pointer and a length to a contiguous block of memory
|
||||||
template <typename T> struct span {
|
template <typename T> struct span {
|
||||||
T const *ptr;
|
T const *ptr;
|
||||||
size_t length;
|
am_digits length;
|
||||||
|
|
||||||
constexpr span(T const *_ptr, size_t _length) : ptr(_ptr), length(_length) {}
|
constexpr span(T const *_ptr, am_digits _length) noexcept
|
||||||
|
: ptr(_ptr), length(_length) {}
|
||||||
|
|
||||||
constexpr span() : ptr(nullptr), length(0) {}
|
constexpr span() noexcept : ptr(nullptr), length(0) {}
|
||||||
|
|
||||||
constexpr size_t len() const noexcept { return length; }
|
constexpr am_digits len() const noexcept { return length; }
|
||||||
|
|
||||||
FASTFLOAT_CONSTEXPR14 const T &operator[](size_t index) const noexcept {
|
FASTFLOAT_CONSTEXPR14 const T &operator[](am_digits index) const noexcept {
|
||||||
FASTFLOAT_DEBUG_ASSERT(index < length);
|
FASTFLOAT_DEBUG_ASSERT(index < length);
|
||||||
return ptr[index];
|
return ptr[index];
|
||||||
}
|
}
|
||||||
@ -306,14 +325,15 @@ struct value128 {
|
|||||||
uint64_t low;
|
uint64_t low;
|
||||||
uint64_t high;
|
uint64_t high;
|
||||||
|
|
||||||
constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
|
constexpr value128(uint64_t _low, uint64_t _high) noexcept
|
||||||
|
: low(_low), high(_high) {}
|
||||||
|
|
||||||
constexpr value128() : low(0), high(0) {}
|
constexpr value128() noexcept : low(0), high(0) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Helper C++14 constexpr generic implementation of leading_zeroes */
|
/* Helper C++14 constexpr generic implementation of leading_zeroes */
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 limb_t
|
||||||
leading_zeroes_generic(uint64_t input_num, int last_bit = 0) {
|
leading_zeroes_generic(uint64_t input_num, uint32_t last_bit = 0) noexcept {
|
||||||
if (input_num & uint64_t(0xffffffff00000000)) {
|
if (input_num & uint64_t(0xffffffff00000000)) {
|
||||||
input_num >>= 32;
|
input_num >>= 32;
|
||||||
last_bit |= 32;
|
last_bit |= 32;
|
||||||
@ -337,13 +357,14 @@ leading_zeroes_generic(uint64_t input_num, int last_bit = 0) {
|
|||||||
if (input_num & uint64_t(0x2)) { /* input_num >>= 1; */
|
if (input_num & uint64_t(0x2)) { /* input_num >>= 1; */
|
||||||
last_bit |= 1;
|
last_bit |= 1;
|
||||||
}
|
}
|
||||||
return 63 - last_bit;
|
return 63 - (limb_t)last_bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* result might be undefined when input_num is zero */
|
/* result might be undefined when input_num is zero */
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 int
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb_t
|
||||||
leading_zeroes(uint64_t input_num) {
|
leading_zeroes(uint64_t input_num) noexcept {
|
||||||
assert(input_num > 0);
|
assert(input_num > 0);
|
||||||
|
FASTFLOAT_ASSUME(input_num > 0);
|
||||||
if (cpp20_and_in_constexpr()) {
|
if (cpp20_and_in_constexpr()) {
|
||||||
return leading_zeroes_generic(input_num);
|
return leading_zeroes_generic(input_num);
|
||||||
}
|
}
|
||||||
@ -353,22 +374,23 @@ leading_zeroes(uint64_t input_num) {
|
|||||||
// Search the mask data from most significant bit (MSB)
|
// Search the mask data from most significant bit (MSB)
|
||||||
// to least significant bit (LSB) for a set bit (1).
|
// to least significant bit (LSB) for a set bit (1).
|
||||||
_BitScanReverse64(&leading_zero, input_num);
|
_BitScanReverse64(&leading_zero, input_num);
|
||||||
return (int)(63 - leading_zero);
|
return (limb_t)(63 - leading_zero);
|
||||||
#else
|
#else
|
||||||
return leading_zeroes_generic(input_num);
|
return (limb_t)leading_zeroes_generic(input_num);
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
return __builtin_clzll(input_num);
|
return (limb_t)__builtin_clzll(input_num);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// slow emulation routine for 32-bit
|
// slow emulation routine for 32-bit
|
||||||
fastfloat_really_inline constexpr uint64_t emulu(uint32_t x, uint32_t y) {
|
fastfloat_really_inline constexpr uint64_t emulu(uint32_t x,
|
||||||
|
uint32_t y) noexcept {
|
||||||
return x * (uint64_t)y;
|
return x * (uint64_t)y;
|
||||||
}
|
}
|
||||||
|
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t
|
||||||
umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) {
|
umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) noexcept {
|
||||||
uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
|
uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
|
||||||
uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
|
uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
|
||||||
uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
|
uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
|
||||||
@ -383,9 +405,8 @@ umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) {
|
|||||||
|
|
||||||
// slow emulation routine for 32-bit
|
// slow emulation routine for 32-bit
|
||||||
#if !defined(__MINGW64__)
|
#if !defined(__MINGW64__)
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t _umul128(uint64_t ab,
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t
|
||||||
uint64_t cd,
|
_umul128(uint64_t ab, uint64_t cd, uint64_t *hi) noexcept {
|
||||||
uint64_t *hi) {
|
|
||||||
return umul128_generic(ab, cd, hi);
|
return umul128_generic(ab, cd, hi);
|
||||||
}
|
}
|
||||||
#endif // !__MINGW64__
|
#endif // !__MINGW64__
|
||||||
@ -394,7 +415,7 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t _umul128(uint64_t ab,
|
|||||||
|
|
||||||
// compute 64-bit a*b
|
// compute 64-bit a*b
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128
|
||||||
full_multiplication(uint64_t a, uint64_t b) {
|
full_multiplication(uint64_t a, uint64_t b) noexcept {
|
||||||
if (cpp20_and_in_constexpr()) {
|
if (cpp20_and_in_constexpr()) {
|
||||||
value128 answer;
|
value128 answer;
|
||||||
answer.low = umul128_generic(a, b, &answer.high);
|
answer.low = umul128_generic(a, b, &answer.high);
|
||||||
@ -419,47 +440,56 @@ full_multiplication(uint64_t a, uint64_t b) {
|
|||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct adjusted_mantissa {
|
// Value of the mantissa.
|
||||||
uint64_t mantissa{0};
|
typedef uint_fast64_t am_mant_t;
|
||||||
int32_t power2{0}; // a negative value indicates an invalid result
|
// Size of bits in the mantissa and path and rounding shifts
|
||||||
adjusted_mantissa() = default;
|
typedef int_fast8_t am_bits_t;
|
||||||
|
|
||||||
constexpr bool operator==(adjusted_mantissa const &o) const {
|
// Power bias is signed for handling a denormal float
|
||||||
|
// or an invalid mantissa.
|
||||||
|
typedef int_fast16_t am_pow_t;
|
||||||
|
|
||||||
|
// Bias so we can get the real exponent with an invalid adjusted_mantissa.
|
||||||
|
constexpr static am_pow_t invalid_am_bias = -0x8000;
|
||||||
|
|
||||||
|
struct adjusted_mantissa {
|
||||||
|
am_mant_t mantissa;
|
||||||
|
am_pow_t power2;
|
||||||
|
adjusted_mantissa() noexcept = default;
|
||||||
|
|
||||||
|
constexpr bool operator==(adjusted_mantissa const &o) const noexcept {
|
||||||
return mantissa == o.mantissa && power2 == o.power2;
|
return mantissa == o.mantissa && power2 == o.power2;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool operator!=(adjusted_mantissa const &o) const {
|
constexpr bool operator!=(adjusted_mantissa const &o) const noexcept {
|
||||||
return mantissa != o.mantissa || power2 != o.power2;
|
return mantissa != o.mantissa || power2 != o.power2;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Bias so we can get the real exponent with an invalid adjusted_mantissa.
|
|
||||||
constexpr static int32_t invalid_am_bias = -0x8000;
|
|
||||||
|
|
||||||
// used for binary_format_lookup_tables<T>::max_mantissa
|
// used for binary_format_lookup_tables<T>::max_mantissa
|
||||||
constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5;
|
constexpr am_mant_t constant_55555 = 5 * 5 * 5 * 5 * 5;
|
||||||
|
|
||||||
template <typename T, typename U = void> struct binary_format_lookup_tables;
|
template <typename T, typename U = void> struct binary_format_lookup_tables;
|
||||||
|
|
||||||
template <typename T> struct binary_format : binary_format_lookup_tables<T> {
|
template <typename T> struct binary_format : binary_format_lookup_tables<T> {
|
||||||
using equiv_uint = equiv_uint_t<T>;
|
using equiv_uint = equiv_uint_t<T>;
|
||||||
|
|
||||||
static constexpr int mantissa_explicit_bits();
|
static constexpr limb_t mantissa_explicit_bits();
|
||||||
static constexpr int minimum_exponent();
|
static constexpr am_pow_t minimum_exponent();
|
||||||
static constexpr int infinite_power();
|
static constexpr am_pow_t infinite_power();
|
||||||
static constexpr int sign_index();
|
static constexpr am_bits_t sign_index();
|
||||||
static constexpr int
|
static constexpr am_bits_t
|
||||||
min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST
|
min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST
|
||||||
static constexpr int max_exponent_fast_path();
|
static constexpr am_bits_t max_exponent_fast_path();
|
||||||
static constexpr int max_exponent_round_to_even();
|
static constexpr am_bits_t max_exponent_round_to_even();
|
||||||
static constexpr int min_exponent_round_to_even();
|
static constexpr am_bits_t min_exponent_round_to_even();
|
||||||
static constexpr uint64_t max_mantissa_fast_path(int64_t power);
|
static constexpr equiv_uint max_mantissa_fast_path(am_pow_t power);
|
||||||
static constexpr uint64_t
|
static constexpr equiv_uint
|
||||||
max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST
|
max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST
|
||||||
static constexpr int largest_power_of_ten();
|
static constexpr am_pow_t largest_power_of_ten();
|
||||||
static constexpr int smallest_power_of_ten();
|
static constexpr am_pow_t smallest_power_of_ten();
|
||||||
static constexpr T exact_power_of_ten(int64_t power);
|
static constexpr T exact_power_of_ten(am_pow_t power);
|
||||||
static constexpr size_t max_digits();
|
static constexpr am_digits max_digits();
|
||||||
static constexpr equiv_uint exponent_mask();
|
static constexpr equiv_uint exponent_mask();
|
||||||
static constexpr equiv_uint mantissa_mask();
|
static constexpr equiv_uint mantissa_mask();
|
||||||
static constexpr equiv_uint hidden_bit_mask();
|
static constexpr equiv_uint hidden_bit_mask();
|
||||||
@ -523,7 +553,7 @@ template <typename U> struct binary_format_lookup_tables<float, U> {
|
|||||||
|
|
||||||
// Largest integer value v so that (5**index * v) <= 1<<24.
|
// Largest integer value v so that (5**index * v) <= 1<<24.
|
||||||
// 0x1000000 == 1<<24
|
// 0x1000000 == 1<<24
|
||||||
static constexpr uint64_t max_mantissa[] = {
|
static constexpr uint32_t max_mantissa[] = {
|
||||||
0x1000000,
|
0x1000000,
|
||||||
0x1000000 / 5,
|
0x1000000 / 5,
|
||||||
0x1000000 / (5 * 5),
|
0x1000000 / (5 * 5),
|
||||||
@ -544,12 +574,12 @@ template <typename U>
|
|||||||
constexpr float binary_format_lookup_tables<float, U>::powers_of_ten[];
|
constexpr float binary_format_lookup_tables<float, U>::powers_of_ten[];
|
||||||
|
|
||||||
template <typename U>
|
template <typename U>
|
||||||
constexpr uint64_t binary_format_lookup_tables<float, U>::max_mantissa[];
|
constexpr uint32_t binary_format_lookup_tables<float, U>::max_mantissa[];
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<double>::min_exponent_fast_path() {
|
inline constexpr am_bits_t binary_format<double>::min_exponent_fast_path() {
|
||||||
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
|
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
|
||||||
return 0;
|
return 0;
|
||||||
#else
|
#else
|
||||||
@ -558,7 +588,7 @@ inline constexpr int binary_format<double>::min_exponent_fast_path() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<float>::min_exponent_fast_path() {
|
inline constexpr am_bits_t binary_format<float>::min_exponent_fast_path() {
|
||||||
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
|
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
|
||||||
return 0;
|
return 0;
|
||||||
#else
|
#else
|
||||||
@ -567,77 +597,78 @@ inline constexpr int binary_format<float>::min_exponent_fast_path() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<double>::mantissa_explicit_bits() {
|
inline constexpr limb_t binary_format<double>::mantissa_explicit_bits() {
|
||||||
return 52;
|
return 52;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<float>::mantissa_explicit_bits() {
|
inline constexpr limb_t binary_format<float>::mantissa_explicit_bits() {
|
||||||
return 23;
|
return 23;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<double>::max_exponent_round_to_even() {
|
inline constexpr am_bits_t binary_format<double>::max_exponent_round_to_even() {
|
||||||
return 23;
|
return 23;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<float>::max_exponent_round_to_even() {
|
inline constexpr am_bits_t binary_format<float>::max_exponent_round_to_even() {
|
||||||
return 10;
|
return 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<double>::min_exponent_round_to_even() {
|
inline constexpr am_bits_t binary_format<double>::min_exponent_round_to_even() {
|
||||||
return -4;
|
return -4;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<float>::min_exponent_round_to_even() {
|
inline constexpr am_bits_t binary_format<float>::min_exponent_round_to_even() {
|
||||||
return -17;
|
return -17;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> inline constexpr int binary_format<double>::minimum_exponent() {
|
template <>
|
||||||
|
inline constexpr am_pow_t binary_format<double>::minimum_exponent() {
|
||||||
return -1023;
|
return -1023;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> inline constexpr int binary_format<float>::minimum_exponent() {
|
template <> inline constexpr am_pow_t binary_format<float>::minimum_exponent() {
|
||||||
return -127;
|
return -127;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> inline constexpr int binary_format<double>::infinite_power() {
|
template <> inline constexpr am_pow_t binary_format<double>::infinite_power() {
|
||||||
return 0x7FF;
|
return 0x7FF;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> inline constexpr int binary_format<float>::infinite_power() {
|
template <> inline constexpr am_pow_t binary_format<float>::infinite_power() {
|
||||||
return 0xFF;
|
return 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> inline constexpr int binary_format<double>::sign_index() {
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
|
||||||
|
template <> inline constexpr am_bits_t binary_format<double>::sign_index() {
|
||||||
return 63;
|
return 63;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> inline constexpr int binary_format<float>::sign_index() {
|
template <> inline constexpr am_bits_t binary_format<float>::sign_index() {
|
||||||
return 31;
|
return 31;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<double>::max_exponent_fast_path() {
|
inline constexpr am_bits_t binary_format<double>::max_exponent_fast_path() {
|
||||||
return 22;
|
return 22;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<float>::max_exponent_fast_path() {
|
inline constexpr am_bits_t binary_format<float>::max_exponent_fast_path() {
|
||||||
return 10;
|
return 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <typename T>
|
||||||
inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() {
|
inline constexpr typename binary_format<T>::equiv_uint
|
||||||
return uint64_t(2) << mantissa_explicit_bits();
|
binary_format<T>::max_mantissa_fast_path() {
|
||||||
}
|
return binary_format<T>::equiv_uint(2) << mantissa_explicit_bits();
|
||||||
|
|
||||||
template <>
|
|
||||||
inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() {
|
|
||||||
return uint64_t(2) << mantissa_explicit_bits();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// credit: Jakub Jelínek
|
// credit: Jakub Jelínek
|
||||||
@ -648,7 +679,7 @@ template <typename U> struct binary_format_lookup_tables<std::float16_t, U> {
|
|||||||
|
|
||||||
// Largest integer value v so that (5**index * v) <= 1<<11.
|
// Largest integer value v so that (5**index * v) <= 1<<11.
|
||||||
// 0x800 == 1<<11
|
// 0x800 == 1<<11
|
||||||
static constexpr uint64_t max_mantissa[] = {0x800,
|
static constexpr uint16_t max_mantissa[] = {0x800,
|
||||||
0x800 / 5,
|
0x800 / 5,
|
||||||
0x800 / (5 * 5),
|
0x800 / (5 * 5),
|
||||||
0x800 / (5 * 5 * 5),
|
0x800 / (5 * 5 * 5),
|
||||||
@ -663,14 +694,14 @@ constexpr std::float16_t
|
|||||||
binary_format_lookup_tables<std::float16_t, U>::powers_of_ten[];
|
binary_format_lookup_tables<std::float16_t, U>::powers_of_ten[];
|
||||||
|
|
||||||
template <typename U>
|
template <typename U>
|
||||||
constexpr uint64_t
|
constexpr uint16_t
|
||||||
binary_format_lookup_tables<std::float16_t, U>::max_mantissa[];
|
binary_format_lookup_tables<std::float16_t, U>::max_mantissa[];
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr std::float16_t
|
inline constexpr std::float16_t
|
||||||
binary_format<std::float16_t>::exact_power_of_ten(int64_t power) {
|
binary_format<std::float16_t>::exact_power_of_ten(am_pow_t power) {
|
||||||
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
||||||
return (void)powers_of_ten[0], powers_of_ten[power];
|
return (void)powers_of_ten[0], powers_of_ten[power];
|
||||||
}
|
}
|
||||||
@ -694,74 +725,78 @@ binary_format<std::float16_t>::hidden_bit_mask() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<std::float16_t>::max_exponent_fast_path() {
|
inline constexpr am_bits_t
|
||||||
|
binary_format<std::float16_t>::max_exponent_fast_path() {
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<std::float16_t>::mantissa_explicit_bits() {
|
inline constexpr limb_t
|
||||||
|
binary_format<std::float16_t>::mantissa_explicit_bits() {
|
||||||
return 10;
|
return 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr uint64_t
|
inline constexpr binary_format<std::float16_t>::equiv_uint
|
||||||
binary_format<std::float16_t>::max_mantissa_fast_path() {
|
binary_format<std::float16_t>::max_mantissa_fast_path(am_pow_t power) {
|
||||||
return uint64_t(2) << mantissa_explicit_bits();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline constexpr uint64_t
|
|
||||||
binary_format<std::float16_t>::max_mantissa_fast_path(int64_t power) {
|
|
||||||
// caller is responsible to ensure that
|
// caller is responsible to ensure that
|
||||||
// power >= 0 && power <= 4
|
FASTFLOAT_ASSUME(power >= 0 && power <= 4);
|
||||||
//
|
//
|
||||||
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
||||||
return (void)max_mantissa[0], max_mantissa[power];
|
return (void)max_mantissa[0], max_mantissa[power];
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<std::float16_t>::min_exponent_fast_path() {
|
inline constexpr am_bits_t
|
||||||
|
binary_format<std::float16_t>::min_exponent_fast_path() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int
|
inline constexpr am_bits_t
|
||||||
binary_format<std::float16_t>::max_exponent_round_to_even() {
|
binary_format<std::float16_t>::max_exponent_round_to_even() {
|
||||||
return 5;
|
return 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int
|
inline constexpr am_bits_t
|
||||||
binary_format<std::float16_t>::min_exponent_round_to_even() {
|
binary_format<std::float16_t>::min_exponent_round_to_even() {
|
||||||
return -22;
|
return -22;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<std::float16_t>::minimum_exponent() {
|
inline constexpr am_pow_t binary_format<std::float16_t>::minimum_exponent() {
|
||||||
return -15;
|
return -15;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<std::float16_t>::infinite_power() {
|
inline constexpr am_pow_t binary_format<std::float16_t>::infinite_power() {
|
||||||
return 0x1F;
|
return 0x1F;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> inline constexpr int binary_format<std::float16_t>::sign_index() {
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline constexpr am_bits_t binary_format<std::float16_t>::sign_index() {
|
||||||
return 15;
|
return 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<std::float16_t>::largest_power_of_ten() {
|
inline constexpr am_pow_t
|
||||||
|
binary_format<std::float16_t>::largest_power_of_ten() {
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<std::float16_t>::smallest_power_of_ten() {
|
inline constexpr am_pow_t
|
||||||
|
binary_format<std::float16_t>::smallest_power_of_ten() {
|
||||||
return -27;
|
return -27;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr size_t binary_format<std::float16_t>::max_digits() {
|
inline constexpr am_digits binary_format<std::float16_t>::max_digits() {
|
||||||
return 22;
|
return 22;
|
||||||
}
|
}
|
||||||
#endif // __STDCPP_FLOAT16_T__
|
#endif // __STDCPP_FLOAT16_T__
|
||||||
@ -774,7 +809,7 @@ template <typename U> struct binary_format_lookup_tables<std::bfloat16_t, U> {
|
|||||||
|
|
||||||
// Largest integer value v so that (5**index * v) <= 1<<8.
|
// Largest integer value v so that (5**index * v) <= 1<<8.
|
||||||
// 0x100 == 1<<8
|
// 0x100 == 1<<8
|
||||||
static constexpr uint64_t max_mantissa[] = {0x100, 0x100 / 5, 0x100 / (5 * 5),
|
static constexpr uint16_t max_mantissa[] = {0x100, 0x100 / 5, 0x100 / (5 * 5),
|
||||||
0x100 / (5 * 5 * 5),
|
0x100 / (5 * 5 * 5),
|
||||||
0x100 / (5 * 5 * 5 * 5)};
|
0x100 / (5 * 5 * 5 * 5)};
|
||||||
};
|
};
|
||||||
@ -793,13 +828,14 @@ constexpr uint64_t
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr std::bfloat16_t
|
inline constexpr std::bfloat16_t
|
||||||
binary_format<std::bfloat16_t>::exact_power_of_ten(int64_t power) {
|
binary_format<std::bfloat16_t>::exact_power_of_ten(am_pow_t power) {
|
||||||
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
||||||
return (void)powers_of_ten[0], powers_of_ten[power];
|
return (void)powers_of_ten[0], powers_of_ten[power];
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<std::bfloat16_t>::max_exponent_fast_path() {
|
inline constexpr am_bits_t
|
||||||
|
binary_format<std::bfloat16_t>::max_exponent_fast_path() {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -822,88 +858,91 @@ binary_format<std::bfloat16_t>::hidden_bit_mask() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<std::bfloat16_t>::mantissa_explicit_bits() {
|
inline constexpr limb_t
|
||||||
|
binary_format<std::bfloat16_t>::mantissa_explicit_bits() {
|
||||||
return 7;
|
return 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr uint64_t
|
inline constexpr binary_format<std::bfloat16_t>::equiv_uint
|
||||||
binary_format<std::bfloat16_t>::max_mantissa_fast_path() {
|
binary_format<std::bfloat16_t>::max_mantissa_fast_path(am_pow_t power) {
|
||||||
return uint64_t(2) << mantissa_explicit_bits();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline constexpr uint64_t
|
|
||||||
binary_format<std::bfloat16_t>::max_mantissa_fast_path(int64_t power) {
|
|
||||||
// caller is responsible to ensure that
|
// caller is responsible to ensure that
|
||||||
// power >= 0 && power <= 3
|
FASTFLOAT_ASSUME(power >= 0 && power <= 3);
|
||||||
//
|
//
|
||||||
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
||||||
return (void)max_mantissa[0], max_mantissa[power];
|
return (void)max_mantissa[0], max_mantissa[power];
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<std::bfloat16_t>::min_exponent_fast_path() {
|
inline constexpr am_bits_t
|
||||||
|
binary_format<std::bfloat16_t>::min_exponent_fast_path() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int
|
inline constexpr am_bits_t
|
||||||
binary_format<std::bfloat16_t>::max_exponent_round_to_even() {
|
binary_format<std::bfloat16_t>::max_exponent_round_to_even() {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int
|
inline constexpr am_bits_t
|
||||||
binary_format<std::bfloat16_t>::min_exponent_round_to_even() {
|
binary_format<std::bfloat16_t>::min_exponent_round_to_even() {
|
||||||
return -24;
|
return -24;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<std::bfloat16_t>::minimum_exponent() {
|
inline constexpr am_pow_t binary_format<std::bfloat16_t>::minimum_exponent() {
|
||||||
return -127;
|
return -127;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<std::bfloat16_t>::infinite_power() {
|
inline constexpr am_pow_t binary_format<std::bfloat16_t>::infinite_power() {
|
||||||
return 0xFF;
|
return 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> inline constexpr int binary_format<std::bfloat16_t>::sign_index() {
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline constexpr am_bits_t binary_format<std::bfloat16_t>::sign_index() {
|
||||||
return 15;
|
return 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<std::bfloat16_t>::largest_power_of_ten() {
|
inline constexpr am_pow_t
|
||||||
|
binary_format<std::bfloat16_t>::largest_power_of_ten() {
|
||||||
return 38;
|
return 38;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<std::bfloat16_t>::smallest_power_of_ten() {
|
inline constexpr am_pow_t
|
||||||
|
binary_format<std::bfloat16_t>::smallest_power_of_ten() {
|
||||||
return -60;
|
return -60;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr size_t binary_format<std::bfloat16_t>::max_digits() {
|
inline constexpr am_digits binary_format<std::bfloat16_t>::max_digits() {
|
||||||
return 98;
|
return 98;
|
||||||
}
|
}
|
||||||
#endif // __STDCPP_BFLOAT16_T__
|
#endif // __STDCPP_BFLOAT16_T__
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr uint64_t
|
inline constexpr binary_format<double>::equiv_uint
|
||||||
binary_format<double>::max_mantissa_fast_path(int64_t power) {
|
binary_format<double>::max_mantissa_fast_path(am_pow_t power) {
|
||||||
// caller is responsible to ensure that
|
// caller is responsible to ensure that
|
||||||
// power >= 0 && power <= 22
|
FASTFLOAT_ASSUME(power >= 0 && power <= 22);
|
||||||
//
|
//
|
||||||
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
||||||
return (void)max_mantissa[0], max_mantissa[power];
|
return (void)max_mantissa[0], max_mantissa[power];
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr uint64_t
|
inline constexpr binary_format<float>::equiv_uint
|
||||||
binary_format<float>::max_mantissa_fast_path(int64_t power) {
|
binary_format<float>::max_mantissa_fast_path(am_pow_t power) {
|
||||||
// caller is responsible to ensure that
|
// caller is responsible to ensure that
|
||||||
// power >= 0 && power <= 10
|
FASTFLOAT_ASSUME(power >= 0 && power <= 10);
|
||||||
//
|
//
|
||||||
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
||||||
return (void)max_mantissa[0], max_mantissa[power];
|
return (void)max_mantissa[0], max_mantissa[power];
|
||||||
@ -911,39 +950,49 @@ binary_format<float>::max_mantissa_fast_path(int64_t power) {
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr double
|
inline constexpr double
|
||||||
binary_format<double>::exact_power_of_ten(int64_t power) {
|
binary_format<double>::exact_power_of_ten(am_pow_t power) {
|
||||||
|
// caller is responsible to ensure that
|
||||||
|
FASTFLOAT_ASSUME(power >= 0 && power <= 22);
|
||||||
|
//
|
||||||
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
||||||
return (void)powers_of_ten[0], powers_of_ten[power];
|
return (void)powers_of_ten[0], powers_of_ten[power];
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr float binary_format<float>::exact_power_of_ten(int64_t power) {
|
inline constexpr float
|
||||||
|
binary_format<float>::exact_power_of_ten(am_pow_t power) {
|
||||||
|
// caller is responsible to ensure that
|
||||||
|
FASTFLOAT_ASSUME(power >= 0 && power <= 10);
|
||||||
|
//
|
||||||
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
||||||
return (void)powers_of_ten[0], powers_of_ten[power];
|
return (void)powers_of_ten[0], powers_of_ten[power];
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> inline constexpr int binary_format<double>::largest_power_of_ten() {
|
template <>
|
||||||
|
inline constexpr am_pow_t binary_format<double>::largest_power_of_ten() {
|
||||||
return 308;
|
return 308;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> inline constexpr int binary_format<float>::largest_power_of_ten() {
|
template <>
|
||||||
|
inline constexpr am_pow_t binary_format<float>::largest_power_of_ten() {
|
||||||
return 38;
|
return 38;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr int binary_format<double>::smallest_power_of_ten() {
|
inline constexpr am_pow_t binary_format<double>::smallest_power_of_ten() {
|
||||||
return -342;
|
return -342;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> inline constexpr int binary_format<float>::smallest_power_of_ten() {
|
template <>
|
||||||
|
inline constexpr am_pow_t binary_format<float>::smallest_power_of_ten() {
|
||||||
return -64;
|
return -64;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> inline constexpr size_t binary_format<double>::max_digits() {
|
template <> inline constexpr am_digits binary_format<double>::max_digits() {
|
||||||
return 769;
|
return 769;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> inline constexpr size_t binary_format<float>::max_digits() {
|
template <> inline constexpr am_digits binary_format<float>::max_digits() {
|
||||||
return 114;
|
return 114;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -984,23 +1033,34 @@ binary_format<double>::hidden_bit_mask() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void to_float(
|
||||||
to_float(bool negative, adjusted_mantissa am, T &value) {
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
bool const negative,
|
||||||
|
#endif
|
||||||
|
adjusted_mantissa const &am, T &value) noexcept {
|
||||||
using equiv_uint = equiv_uint_t<T>;
|
using equiv_uint = equiv_uint_t<T>;
|
||||||
equiv_uint word = equiv_uint(am.mantissa);
|
equiv_uint word = equiv_uint(am.mantissa);
|
||||||
word = equiv_uint(word | equiv_uint(am.power2)
|
word = equiv_uint(word | equiv_uint(am.power2)
|
||||||
<< binary_format<T>::mantissa_explicit_bits());
|
<< binary_format<T>::mantissa_explicit_bits());
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
word =
|
word =
|
||||||
equiv_uint(word | equiv_uint(negative) << binary_format<T>::sign_index());
|
equiv_uint(word | equiv_uint(negative) << binary_format<T>::sign_index());
|
||||||
|
#endif
|
||||||
#if FASTFLOAT_HAS_BIT_CAST
|
#if FASTFLOAT_HAS_BIT_CAST
|
||||||
value = std::bit_cast<T>(word);
|
value =
|
||||||
|
#if FASTFLOAT_HAS_BIT_CAST == 1
|
||||||
|
std::
|
||||||
|
#endif
|
||||||
|
bit_cast<T>(word);
|
||||||
#else
|
#else
|
||||||
::memcpy(&value, &word, sizeof(T));
|
::memcpy(&value, &word, sizeof(T));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
|
||||||
template <typename = void> struct space_lut {
|
template <typename = void> struct space_lut {
|
||||||
static constexpr bool value[] = {
|
static constexpr uint8_t value[] = {
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
@ -1016,7 +1076,7 @@ template <typename = void> struct space_lut {
|
|||||||
|
|
||||||
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
|
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
|
||||||
|
|
||||||
template <typename T> constexpr bool space_lut<T>::value[];
|
template <typename T> constexpr uint8_t space_lut<T>::value[];
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1024,6 +1084,8 @@ template <typename UC> constexpr bool is_space(UC c) {
|
|||||||
return c < 256 && space_lut<>::value[uint8_t(c)];
|
return c < 256 && space_lut<>::value[uint8_t(c)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
template <typename UC> static constexpr uint64_t int_cmp_zeros() {
|
template <typename UC> static constexpr uint64_t int_cmp_zeros() {
|
||||||
static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4),
|
static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4),
|
||||||
"Unsupported character size");
|
"Unsupported character size");
|
||||||
@ -1038,6 +1100,8 @@ template <typename UC> static constexpr int int_cmp_len() {
|
|||||||
return sizeof(uint64_t) / sizeof(UC);
|
return sizeof(uint64_t) / sizeof(UC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
|
||||||
template <typename UC> constexpr UC const *str_const_nan();
|
template <typename UC> constexpr UC const *str_const_nan();
|
||||||
|
|
||||||
template <> constexpr char const *str_const_nan<char>() { return "nan"; }
|
template <> constexpr char const *str_const_nan<char>() { return "nan"; }
|
||||||
@ -1080,6 +1144,8 @@ template <> constexpr char8_t const *str_const_inf<char8_t>() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
template <typename = void> struct int_luts {
|
template <typename = void> struct int_luts {
|
||||||
static constexpr uint8_t chdigit[] = {
|
static constexpr uint8_t chdigit[] = {
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
@ -1101,7 +1167,7 @@ template <typename = void> struct int_luts {
|
|||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
255};
|
255};
|
||||||
|
|
||||||
static constexpr size_t maxdigits_u64[] = {
|
static constexpr uint8_t maxdigits_u64[] = {
|
||||||
64, 41, 32, 28, 25, 23, 22, 21, 20, 19, 18, 18, 17, 17, 16, 16, 16, 16,
|
64, 41, 32, 28, 25, 23, 22, 21, 20, 19, 18, 18, 17, 17, 16, 16, 16, 16,
|
||||||
15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13};
|
15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13};
|
||||||
|
|
||||||
@ -1124,14 +1190,14 @@ template <typename = void> struct int_luts {
|
|||||||
|
|
||||||
template <typename T> constexpr uint8_t int_luts<T>::chdigit[];
|
template <typename T> constexpr uint8_t int_luts<T>::chdigit[];
|
||||||
|
|
||||||
template <typename T> constexpr size_t int_luts<T>::maxdigits_u64[];
|
template <typename T> constexpr uint8_t int_luts<T>::maxdigits_u64[];
|
||||||
|
|
||||||
template <typename T> constexpr uint64_t int_luts<T>::min_safe_u64[];
|
template <typename T> constexpr uint64_t int_luts<T>::min_safe_u64[];
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <typename UC>
|
template <typename UC>
|
||||||
fastfloat_really_inline constexpr uint8_t ch_to_digit(UC c) {
|
fastfloat_really_inline constexpr uint_fast8_t ch_to_digit(UC c) noexcept {
|
||||||
// wchar_t and char can be signed, so we need to be careful.
|
// wchar_t and char can be signed, so we need to be careful.
|
||||||
using UnsignedUC = typename std::make_unsigned<UC>::type;
|
using UnsignedUC = typename std::make_unsigned<UC>::type;
|
||||||
return int_luts<>::chdigit[static_cast<unsigned char>(
|
return int_luts<>::chdigit[static_cast<unsigned char>(
|
||||||
@ -1140,13 +1206,15 @@ fastfloat_really_inline constexpr uint8_t ch_to_digit(UC c) {
|
|||||||
-((static_cast<UnsignedUC>(c) & ~0xFFull) == 0)))];
|
-((static_cast<UnsignedUC>(c) & ~0xFFull) == 0)))];
|
||||||
}
|
}
|
||||||
|
|
||||||
fastfloat_really_inline constexpr size_t max_digits_u64(int base) {
|
fastfloat_really_inline constexpr uint_fast8_t
|
||||||
|
max_digits_u64(uint_fast8_t base) noexcept {
|
||||||
return int_luts<>::maxdigits_u64[base - 2];
|
return int_luts<>::maxdigits_u64[base - 2];
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a u64 is exactly max_digits_u64() in length, this is
|
// If a u64 is exactly max_digits_u64() in length, this is
|
||||||
// the value below which it has definitely overflowed.
|
// the value below which it has definitely overflowed.
|
||||||
fastfloat_really_inline constexpr uint64_t min_safe_u64(int base) {
|
fastfloat_really_inline constexpr uint64_t
|
||||||
|
min_safe_u64(uint_fast8_t base) noexcept {
|
||||||
return int_luts<>::min_safe_u64[base - 2];
|
return int_luts<>::min_safe_u64[base - 2];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1238,19 +1306,6 @@ operator^=(chars_format &lhs, chars_format rhs) noexcept {
|
|||||||
return lhs = (lhs ^ rhs);
|
return lhs = (lhs ^ rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
// adjust for deprecated feature macros
|
|
||||||
constexpr chars_format adjust_for_feature_macros(chars_format fmt) {
|
|
||||||
return fmt
|
|
||||||
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS
|
|
||||||
| chars_format::allow_leading_plus
|
|
||||||
#endif
|
|
||||||
#ifdef FASTFLOAT_SKIP_WHITE_SPACE
|
|
||||||
| chars_format::skip_white_space
|
|
||||||
#endif
|
|
||||||
;
|
|
||||||
}
|
|
||||||
} // namespace detail
|
|
||||||
} // namespace fast_float
|
} // namespace fast_float
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
namespace fast_float {
|
namespace fast_float {
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
/**
|
/**
|
||||||
* Special case +inf, -inf, nan, infinity, -infinity.
|
* Special case +inf, -inf, nan, infinity, -infinity.
|
||||||
* The case comparisons could be made much faster given that we know that the
|
* The case comparisons could be made much faster given that we know that the
|
||||||
@ -22,18 +23,21 @@ namespace detail {
|
|||||||
template <typename T, typename UC>
|
template <typename T, typename UC>
|
||||||
from_chars_result_t<UC>
|
from_chars_result_t<UC>
|
||||||
FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, UC const *last,
|
FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, UC const *last,
|
||||||
T &value, chars_format fmt) noexcept {
|
T &value,
|
||||||
|
const chars_format fmt) noexcept {
|
||||||
from_chars_result_t<UC> answer{};
|
from_chars_result_t<UC> answer{};
|
||||||
answer.ptr = first;
|
answer.ptr = first;
|
||||||
answer.ec = std::errc(); // be optimistic
|
answer.ec = std::errc(); // be optimistic
|
||||||
// assume first < last, so dereference without checks;
|
FASTFLOAT_ASSUME(first < last); // so dereference without checks
|
||||||
|
|
||||||
bool const minusSign = (*first == UC('-'));
|
bool const minusSign = (*first == UC('-'));
|
||||||
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here
|
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here
|
||||||
if ((*first == UC('-')) ||
|
if ((*first == UC('-')) ||
|
||||||
(uint64_t(fmt & chars_format::allow_leading_plus) &&
|
((chars_format_t(fmt & chars_format::allow_leading_plus)) &&
|
||||||
(*first == UC('+')))) {
|
(*first == UC('+')))) {
|
||||||
++first;
|
++first;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (last - first >= 3) {
|
if (last - first >= 3) {
|
||||||
if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
|
if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
|
||||||
answer.ptr = (first += 3);
|
answer.ptr = (first += 3);
|
||||||
@ -69,7 +73,9 @@ from_chars_result_t<UC>
|
|||||||
answer.ec = std::errc::invalid_argument;
|
answer.ec = std::errc::invalid_argument;
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
|
||||||
/**
|
/**
|
||||||
* Returns true if the floating-pointing rounding mode is to 'nearest'.
|
* Returns true if the floating-pointing rounding mode is to 'nearest'.
|
||||||
* It is the default on most system. This function is meant to be inexpensive.
|
* It is the default on most system. This function is meant to be inexpensive.
|
||||||
@ -134,6 +140,7 @@ fastfloat_really_inline bool rounds_to_nearest() noexcept {
|
|||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
@ -141,7 +148,7 @@ template <typename T> struct from_chars_caller {
|
|||||||
template <typename UC>
|
template <typename UC>
|
||||||
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
|
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
|
||||||
call(UC const *first, UC const *last, T &value,
|
call(UC const *first, UC const *last, T &value,
|
||||||
parse_options_t<UC> options) noexcept {
|
parse_options_t<UC> const &options) noexcept {
|
||||||
return from_chars_advanced(first, last, value, options);
|
return from_chars_advanced(first, last, value, options);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -151,7 +158,7 @@ template <> struct from_chars_caller<std::float32_t> {
|
|||||||
template <typename UC>
|
template <typename UC>
|
||||||
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
|
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
|
||||||
call(UC const *first, UC const *last, std::float32_t &value,
|
call(UC const *first, UC const *last, std::float32_t &value,
|
||||||
parse_options_t<UC> options) noexcept {
|
parse_options_t<UC> const options) noexcept {
|
||||||
// if std::float32_t is defined, and we are in C++23 mode; macro set for
|
// if std::float32_t is defined, and we are in C++23 mode; macro set for
|
||||||
// float32; set value to float due to equivalence between float and
|
// float32; set value to float due to equivalence between float and
|
||||||
// float32_t
|
// float32_t
|
||||||
@ -168,7 +175,7 @@ template <> struct from_chars_caller<std::float64_t> {
|
|||||||
template <typename UC>
|
template <typename UC>
|
||||||
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
|
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
|
||||||
call(UC const *first, UC const *last, std::float64_t &value,
|
call(UC const *first, UC const *last, std::float64_t &value,
|
||||||
parse_options_t<UC> options) noexcept {
|
parse_options_t<UC> const options) noexcept {
|
||||||
// if std::float64_t is defined, and we are in C++23 mode; macro set for
|
// if std::float64_t is defined, and we are in C++23 mode; macro set for
|
||||||
// float64; set value as double due to equivalence between double and
|
// float64; set value as double due to equivalence between double and
|
||||||
// float64_t
|
// float64_t
|
||||||
@ -183,15 +190,19 @@ template <> struct from_chars_caller<std::float64_t> {
|
|||||||
template <typename T, typename UC, typename>
|
template <typename T, typename UC, typename>
|
||||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||||
from_chars(UC const *first, UC const *last, T &value,
|
from_chars(UC const *first, UC const *last, T &value,
|
||||||
chars_format fmt /*= chars_format::general*/) noexcept {
|
chars_format const fmt /*= chars_format::general*/) noexcept {
|
||||||
return from_chars_caller<T>::call(first, last, value,
|
return from_chars_caller<T>::call(first, last, value,
|
||||||
parse_options_t<UC>(fmt));
|
parse_options_t<UC>(fmt));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
|
||||||
clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
|
clinger_fast_path_impl(am_mant_t const mantissa, am_pow_t const exponent,
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
bool const is_negative,
|
||||||
|
#endif
|
||||||
T &value) noexcept {
|
T &value) noexcept {
|
||||||
|
|
||||||
// The implementation of the Clinger's fast path is convoluted because
|
// The implementation of the Clinger's fast path is convoluted because
|
||||||
// we want round-to-nearest in all cases, irrespective of the rounding mode
|
// we want round-to-nearest in all cases, irrespective of the rounding mode
|
||||||
// selected on the thread.
|
// selected on the thread.
|
||||||
@ -206,7 +217,9 @@ clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
|
|||||||
// We could check it first (before the previous branch), but
|
// We could check it first (before the previous branch), but
|
||||||
// there might be performance advantages at having the check
|
// there might be performance advantages at having the check
|
||||||
// be last.
|
// be last.
|
||||||
|
#ifndef FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
|
||||||
if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) {
|
if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) {
|
||||||
|
#endif
|
||||||
// We have that fegetround() == FE_TONEAREST.
|
// We have that fegetround() == FE_TONEAREST.
|
||||||
// Next is Clinger's fast path.
|
// Next is Clinger's fast path.
|
||||||
if (mantissa <= binary_format<T>::max_mantissa_fast_path()) {
|
if (mantissa <= binary_format<T>::max_mantissa_fast_path()) {
|
||||||
@ -216,11 +229,14 @@ clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
|
|||||||
} else {
|
} else {
|
||||||
value = value * binary_format<T>::exact_power_of_ten(exponent);
|
value = value * binary_format<T>::exact_power_of_ten(exponent);
|
||||||
}
|
}
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
if (is_negative) {
|
if (is_negative) {
|
||||||
value = -value;
|
value = -value;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
#ifndef FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
|
||||||
} else {
|
} else {
|
||||||
// We do not have that fegetround() == FE_TONEAREST.
|
// We do not have that fegetround() == FE_TONEAREST.
|
||||||
// Next is a modified Clinger's fast path, inspired by Jakub Jelínek's
|
// Next is a modified Clinger's fast path, inspired by Jakub Jelínek's
|
||||||
@ -230,17 +246,24 @@ clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
|
|||||||
#if defined(__clang__) || defined(FASTFLOAT_32BIT)
|
#if defined(__clang__) || defined(FASTFLOAT_32BIT)
|
||||||
// Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
|
// Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
|
||||||
if (mantissa == 0) {
|
if (mantissa == 0) {
|
||||||
value = is_negative ? T(-0.) : T(0.);
|
value =
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
is_negative ? T(-0.) :
|
||||||
|
#endif
|
||||||
|
T(0.);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
value = T(mantissa) * binary_format<T>::exact_power_of_ten(exponent);
|
value = T(mantissa) * binary_format<T>::exact_power_of_ten(exponent);
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
if (is_negative) {
|
if (is_negative) {
|
||||||
value = -value;
|
value = -value;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -252,7 +275,7 @@ clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
|
|||||||
*/
|
*/
|
||||||
template <typename T, typename UC>
|
template <typename T, typename UC>
|
||||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||||
from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
|
from_chars_advanced(parsed_number_string_t<UC> const &pns, T &value) noexcept {
|
||||||
static_assert(is_supported_float_type<T>::value,
|
static_assert(is_supported_float_type<T>::value,
|
||||||
"only some floating-point types are supported");
|
"only some floating-point types are supported");
|
||||||
static_assert(is_supported_char_type<UC>::value,
|
static_assert(is_supported_char_type<UC>::value,
|
||||||
@ -263,8 +286,11 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
|
|||||||
answer.ec = std::errc(); // be optimistic
|
answer.ec = std::errc(); // be optimistic
|
||||||
answer.ptr = pns.lastmatch;
|
answer.ptr = pns.lastmatch;
|
||||||
|
|
||||||
if (!pns.too_many_digits &&
|
if (!pns.too_many_digits && clinger_fast_path_impl(pns.mantissa, pns.exponent,
|
||||||
clinger_fast_path_impl(pns.mantissa, pns.exponent, pns.negative, value))
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
pns.negative,
|
||||||
|
#endif
|
||||||
|
value))
|
||||||
return answer;
|
return answer;
|
||||||
|
|
||||||
adjusted_mantissa am =
|
adjusted_mantissa am =
|
||||||
@ -280,7 +306,11 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
|
|||||||
if (am.power2 < 0) {
|
if (am.power2 < 0) {
|
||||||
am = digit_comp<T>(pns, am);
|
am = digit_comp<T>(pns, am);
|
||||||
}
|
}
|
||||||
to_float(pns.negative, am, value);
|
to_float(
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
pns.negative,
|
||||||
|
#endif
|
||||||
|
am, value);
|
||||||
// Test for over/underflow.
|
// Test for over/underflow.
|
||||||
if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) ||
|
if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) ||
|
||||||
am.power2 == binary_format<T>::infinite_power()) {
|
am.power2 == binary_format<T>::infinite_power()) {
|
||||||
@ -292,19 +322,18 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
|
|||||||
template <typename T, typename UC>
|
template <typename T, typename UC>
|
||||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||||
from_chars_float_advanced(UC const *first, UC const *last, T &value,
|
from_chars_float_advanced(UC const *first, UC const *last, T &value,
|
||||||
parse_options_t<UC> options) noexcept {
|
parse_options_t<UC> const &options) noexcept {
|
||||||
|
|
||||||
static_assert(is_supported_float_type<T>::value,
|
static_assert(is_supported_float_type<T>::value,
|
||||||
"only some floating-point types are supported");
|
"only some floating-point types are supported");
|
||||||
static_assert(is_supported_char_type<UC>::value,
|
static_assert(is_supported_char_type<UC>::value,
|
||||||
"only char, wchar_t, char16_t and char32_t are supported");
|
"only char, wchar_t, char16_t and char32_t are supported");
|
||||||
|
|
||||||
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
|
|
||||||
|
|
||||||
from_chars_result_t<UC> answer;
|
from_chars_result_t<UC> answer;
|
||||||
if (uint64_t(fmt & chars_format::skip_white_space)) {
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
if (chars_format_t(options.format & chars_format::skip_white_space)) {
|
||||||
while ((first != last) && fast_float::is_space(*first)) {
|
while ((first != last) && fast_float::is_space(*first)) {
|
||||||
first++;
|
++first;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (first == last) {
|
if (first == last) {
|
||||||
@ -312,18 +341,29 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value,
|
|||||||
answer.ptr = first;
|
answer.ptr = first;
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
parsed_number_string_t<UC> pns =
|
#else
|
||||||
uint64_t(fmt & detail::basic_json_fmt)
|
// We are in parser code with external loop that checks bounds.
|
||||||
|
FASTFLOAT_ASSUME(first < last);
|
||||||
|
#endif
|
||||||
|
parsed_number_string_t<UC> const pns =
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
(chars_format_t(options.format & detail::basic_json_fmt))
|
||||||
? parse_number_string<true, UC>(first, last, options)
|
? parse_number_string<true, UC>(first, last, options)
|
||||||
: parse_number_string<false, UC>(first, last, options);
|
:
|
||||||
if (!pns.valid) {
|
#endif
|
||||||
if (uint64_t(fmt & chars_format::no_infnan)) {
|
parse_number_string<false, UC>(first, last, options);
|
||||||
|
if (pns.invalid) {
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
if (chars_format_t(options.format & chars_format::no_infnan)) {
|
||||||
|
#endif
|
||||||
answer.ec = std::errc::invalid_argument;
|
answer.ec = std::errc::invalid_argument;
|
||||||
answer.ptr = first;
|
answer.ptr = first;
|
||||||
return answer;
|
return answer;
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
} else {
|
} else {
|
||||||
return detail::parse_infnan(first, last, value, fmt);
|
return detail::parse_infnan(first, last, value, options.format);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// call overload that takes parsed_number_string_t directly.
|
// call overload that takes parsed_number_string_t directly.
|
||||||
@ -332,55 +372,81 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value,
|
|||||||
|
|
||||||
template <typename T, typename UC, typename>
|
template <typename T, typename UC, typename>
|
||||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||||
from_chars(UC const *first, UC const *last, T &value, int base) noexcept {
|
from_chars(UC const *first, UC const *last, T &value, int const base) noexcept {
|
||||||
|
|
||||||
static_assert(is_supported_integer_type<T>::value,
|
static_assert(is_supported_integer_type<T>::value,
|
||||||
"only integer types are supported");
|
"only integer types are supported");
|
||||||
static_assert(is_supported_char_type<UC>::value,
|
static_assert(is_supported_char_type<UC>::value,
|
||||||
"only char, wchar_t, char16_t and char32_t are supported");
|
"only char, wchar_t, char16_t and char32_t are supported");
|
||||||
|
|
||||||
parse_options_t<UC> options;
|
parse_options_t<UC> const options(chars_format::general, UC('.'),
|
||||||
options.base = base;
|
static_cast<uint_fast8_t>(base));
|
||||||
return from_chars_advanced(first, last, value, options);
|
return from_chars_advanced(first, last, value, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
FASTFLOAT_CONSTEXPR20
|
FASTFLOAT_CONSTEXPR20
|
||||||
typename std::enable_if<is_supported_float_type<T>::value, T>::type
|
typename std::enable_if<is_supported_float_type<T>::value, T>::type
|
||||||
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept {
|
integer_times_pow10(uint64_t const mantissa,
|
||||||
|
int16_t const decimal_exponent) noexcept {
|
||||||
T value;
|
T value;
|
||||||
if (clinger_fast_path_impl(mantissa, decimal_exponent, false, value))
|
if (clinger_fast_path_impl(mantissa, decimal_exponent,
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
false,
|
||||||
|
#endif
|
||||||
|
value))
|
||||||
return value;
|
return value;
|
||||||
|
|
||||||
adjusted_mantissa am =
|
adjusted_mantissa am =
|
||||||
compute_float<binary_format<T>>(decimal_exponent, mantissa);
|
compute_float<binary_format<double>>(decimal_exponent, mantissa);
|
||||||
to_float(false, am, value);
|
to_float(
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
false,
|
||||||
|
#endif
|
||||||
|
am, value);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
FASTFLOAT_CONSTEXPR20
|
FASTFLOAT_CONSTEXPR20
|
||||||
typename std::enable_if<is_supported_float_type<T>::value, T>::type
|
typename std::enable_if<is_supported_float_type<T>::value, T>::type
|
||||||
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept {
|
integer_times_pow10(int64_t const mantissa,
|
||||||
|
int16_t const decimal_exponent) noexcept {
|
||||||
|
#ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
FASTFLOAT_ASSUME(mantissa > 0);
|
||||||
|
const am_mant_t m = static_cast<am_mant_t>(mantissa);
|
||||||
|
#else
|
||||||
const bool is_negative = mantissa < 0;
|
const bool is_negative = mantissa < 0;
|
||||||
const uint64_t m = static_cast<uint64_t>(is_negative ? -mantissa : mantissa);
|
const am_mant_t m =
|
||||||
|
static_cast<am_mant_t>(is_negative ? -mantissa : mantissa);
|
||||||
|
#endif
|
||||||
T value;
|
T value;
|
||||||
if (clinger_fast_path_impl(m, decimal_exponent, is_negative, value))
|
if (clinger_fast_path_impl(m, decimal_exponent,
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
is_negative,
|
||||||
|
#endif
|
||||||
|
value))
|
||||||
return value;
|
return value;
|
||||||
|
|
||||||
adjusted_mantissa am = compute_float<binary_format<T>>(decimal_exponent, m);
|
adjusted_mantissa am =
|
||||||
to_float(is_negative, am, value);
|
compute_float<binary_format<double>>(decimal_exponent, m);
|
||||||
|
to_float(
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
is_negative,
|
||||||
|
#endif
|
||||||
|
am, value);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
FASTFLOAT_CONSTEXPR20 inline double
|
FASTFLOAT_CONSTEXPR20 inline double
|
||||||
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept {
|
integer_times_pow10(uint64_t const mantissa,
|
||||||
|
int16_t const decimal_exponent) noexcept {
|
||||||
return integer_times_pow10<double>(mantissa, decimal_exponent);
|
return integer_times_pow10<double>(mantissa, decimal_exponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
FASTFLOAT_CONSTEXPR20 inline double
|
FASTFLOAT_CONSTEXPR20 inline double
|
||||||
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept {
|
integer_times_pow10(int64_t const mantissa,
|
||||||
|
int16_t const decimal_exponent) noexcept {
|
||||||
return integer_times_pow10<double>(mantissa, decimal_exponent);
|
return integer_times_pow10<double>(mantissa, decimal_exponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,7 +458,7 @@ FASTFLOAT_CONSTEXPR20
|
|||||||
std::is_integral<Int>::value &&
|
std::is_integral<Int>::value &&
|
||||||
!std::is_signed<Int>::value,
|
!std::is_signed<Int>::value,
|
||||||
T>::type
|
T>::type
|
||||||
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
|
integer_times_pow10(Int mantissa, int16_t decimal_exponent) noexcept {
|
||||||
return integer_times_pow10<T>(static_cast<uint64_t>(mantissa),
|
return integer_times_pow10<T>(static_cast<uint64_t>(mantissa),
|
||||||
decimal_exponent);
|
decimal_exponent);
|
||||||
}
|
}
|
||||||
@ -403,7 +469,7 @@ FASTFLOAT_CONSTEXPR20
|
|||||||
std::is_integral<Int>::value &&
|
std::is_integral<Int>::value &&
|
||||||
std::is_signed<Int>::value,
|
std::is_signed<Int>::value,
|
||||||
T>::type
|
T>::type
|
||||||
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
|
integer_times_pow10(Int mantissa, int16_t decimal_exponent) noexcept {
|
||||||
return integer_times_pow10<T>(static_cast<int64_t>(mantissa),
|
return integer_times_pow10<T>(static_cast<int64_t>(mantissa),
|
||||||
decimal_exponent);
|
decimal_exponent);
|
||||||
}
|
}
|
||||||
@ -411,37 +477,44 @@ FASTFLOAT_CONSTEXPR20
|
|||||||
template <typename Int>
|
template <typename Int>
|
||||||
FASTFLOAT_CONSTEXPR20 typename std::enable_if<
|
FASTFLOAT_CONSTEXPR20 typename std::enable_if<
|
||||||
std::is_integral<Int>::value && !std::is_signed<Int>::value, double>::type
|
std::is_integral<Int>::value && !std::is_signed<Int>::value, double>::type
|
||||||
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
|
integer_times_pow10(Int mantissa, int16_t decimal_exponent) noexcept {
|
||||||
return integer_times_pow10(static_cast<uint64_t>(mantissa), decimal_exponent);
|
return integer_times_pow10(static_cast<uint64_t>(mantissa), decimal_exponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Int>
|
template <typename Int>
|
||||||
FASTFLOAT_CONSTEXPR20 typename std::enable_if<
|
FASTFLOAT_CONSTEXPR20 typename std::enable_if<
|
||||||
std::is_integral<Int>::value && std::is_signed<Int>::value, double>::type
|
std::is_integral<Int>::value && std::is_signed<Int>::value, double>::type
|
||||||
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
|
integer_times_pow10(Int mantissa, int16_t decimal_exponent) noexcept {
|
||||||
return integer_times_pow10(static_cast<int64_t>(mantissa), decimal_exponent);
|
return integer_times_pow10(static_cast<int64_t>(mantissa), decimal_exponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename UC>
|
template <typename T, typename UC>
|
||||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||||
from_chars_int_advanced(UC const *first, UC const *last, T &value,
|
from_chars_int_advanced(UC const *first, UC const *last, T &value,
|
||||||
parse_options_t<UC> options) noexcept {
|
parse_options_t<UC> const &options) noexcept {
|
||||||
|
|
||||||
static_assert(is_supported_integer_type<T>::value,
|
static_assert(is_supported_integer_type<T>::value,
|
||||||
"only integer types are supported");
|
"only integer types are supported");
|
||||||
static_assert(is_supported_char_type<UC>::value,
|
static_assert(is_supported_char_type<UC>::value,
|
||||||
"only char, wchar_t, char16_t and char32_t are supported");
|
"only char, wchar_t, char16_t and char32_t are supported");
|
||||||
|
|
||||||
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
|
#ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
int const base = options.base;
|
// We are in parser code with external loop that checks bounds.
|
||||||
|
FASTFLOAT_ASSUME(first < last);
|
||||||
from_chars_result_t<UC> answer;
|
// base is already checked in the parse_options_t constructor.
|
||||||
if (uint64_t(fmt & chars_format::skip_white_space)) {
|
#else
|
||||||
|
if (chars_format_t(options.format & chars_format::skip_white_space)) {
|
||||||
while ((first != last) && fast_float::is_space(*first)) {
|
while ((first != last) && fast_float::is_space(*first)) {
|
||||||
first++;
|
++first;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (first == last || base < 2 || base > 36) {
|
#endif
|
||||||
|
if (
|
||||||
|
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||||
|
first == last ||
|
||||||
|
#endif
|
||||||
|
options.base < 2 || options.base > 36) {
|
||||||
|
from_chars_result_t<UC> answer;
|
||||||
answer.ec = std::errc::invalid_argument;
|
answer.ec = std::errc::invalid_argument;
|
||||||
answer.ptr = first;
|
answer.ptr = first;
|
||||||
return answer;
|
return answer;
|
||||||
@ -458,7 +531,7 @@ template <> struct from_chars_advanced_caller<1> {
|
|||||||
template <typename T, typename UC>
|
template <typename T, typename UC>
|
||||||
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
|
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
|
||||||
call(UC const *first, UC const *last, T &value,
|
call(UC const *first, UC const *last, T &value,
|
||||||
parse_options_t<UC> options) noexcept {
|
parse_options_t<UC> const &options) noexcept {
|
||||||
return from_chars_float_advanced(first, last, value, options);
|
return from_chars_float_advanced(first, last, value, options);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -467,7 +540,7 @@ template <> struct from_chars_advanced_caller<2> {
|
|||||||
template <typename T, typename UC>
|
template <typename T, typename UC>
|
||||||
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
|
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
|
||||||
call(UC const *first, UC const *last, T &value,
|
call(UC const *first, UC const *last, T &value,
|
||||||
parse_options_t<UC> options) noexcept {
|
parse_options_t<UC> const &options) noexcept {
|
||||||
return from_chars_int_advanced(first, last, value, options);
|
return from_chars_int_advanced(first, last, value, options);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -475,7 +548,7 @@ template <> struct from_chars_advanced_caller<2> {
|
|||||||
template <typename T, typename UC>
|
template <typename T, typename UC>
|
||||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||||
from_chars_advanced(UC const *first, UC const *last, T &value,
|
from_chars_advanced(UC const *first, UC const *last, T &value,
|
||||||
parse_options_t<UC> options) noexcept {
|
parse_options_t<UC> const &options) noexcept {
|
||||||
return from_chars_advanced_caller<
|
return from_chars_advanced_caller<
|
||||||
size_t(is_supported_float_type<T>::value) +
|
size_t(is_supported_float_type<T>::value) +
|
||||||
2 * size_t(is_supported_integer_type<T>::value)>::call(first, last, value,
|
2 * size_t(is_supported_integer_type<T>::value)>::call(first, last, value,
|
||||||
|
|||||||
@ -69,7 +69,7 @@ template <typename T> std::string fHexAndDec(T v) {
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
char const *round_name(int d) {
|
const std::string_view round_name(int const d) {
|
||||||
switch (d) {
|
switch (d) {
|
||||||
case FE_UPWARD:
|
case FE_UPWARD:
|
||||||
return "FE_UPWARD";
|
return "FE_UPWARD";
|
||||||
@ -107,9 +107,9 @@ TEST_CASE("system_info") {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef FASTFLOAT_IS_BIG_ENDIAN
|
#ifdef FASTFLOAT_IS_BIG_ENDIAN
|
||||||
#if FASTFLOAT_IS_BIG_ENDIAN
|
#if FASTFLOAT_IS_BIG_ENDIAN
|
||||||
printf("big endian\n");
|
std::cout << "big endian" << std::endl;
|
||||||
#else
|
#else
|
||||||
printf("little endian\n");
|
std::cout << "little endian" << std::endl;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#ifdef FASTFLOAT_32BIT
|
#ifdef FASTFLOAT_32BIT
|
||||||
@ -2126,8 +2126,9 @@ TEST_CASE("bfloat16.general") {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <typename Int, typename T, typename U>
|
template <typename Int, typename T, typename U>
|
||||||
void verify_integer_times_pow10_result(Int mantissa, int decimal_exponent,
|
void verify_integer_times_pow10_result(Int const mantissa,
|
||||||
T actual, U expected) {
|
int16_t const decimal_exponent,
|
||||||
|
T const actual, U const expected) {
|
||||||
static_assert(std::is_same<T, U>::value,
|
static_assert(std::is_same<T, U>::value,
|
||||||
"expected and actual types must match");
|
"expected and actual types must match");
|
||||||
|
|
||||||
@ -2144,8 +2145,8 @@ void verify_integer_times_pow10_result(Int mantissa, int decimal_exponent,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Int>
|
template <typename T, typename Int>
|
||||||
T calculate_integer_times_pow10_expected_result(Int mantissa,
|
T calculate_integer_times_pow10_expected_result(
|
||||||
int decimal_exponent) {
|
Int const mantissa, int16_t const decimal_exponent) {
|
||||||
std::string constructed_string =
|
std::string constructed_string =
|
||||||
std::to_string(mantissa) + "e" + std::to_string(decimal_exponent);
|
std::to_string(mantissa) + "e" + std::to_string(decimal_exponent);
|
||||||
T expected_result;
|
T expected_result;
|
||||||
@ -2158,8 +2159,9 @@ T calculate_integer_times_pow10_expected_result(Int mantissa,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Int>
|
template <typename Int>
|
||||||
void verify_integer_times_pow10_dflt(Int mantissa, int decimal_exponent,
|
void verify_integer_times_pow10_dflt(Int const mantissa,
|
||||||
double expected) {
|
int16_t const decimal_exponent,
|
||||||
|
double const expected) {
|
||||||
static_assert(std::is_integral<Int>::value);
|
static_assert(std::is_integral<Int>::value);
|
||||||
|
|
||||||
// the "default" overload
|
// the "default" overload
|
||||||
@ -2171,7 +2173,8 @@ void verify_integer_times_pow10_dflt(Int mantissa, int decimal_exponent,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Int>
|
template <typename Int>
|
||||||
void verify_integer_times_pow10_dflt(Int mantissa, int decimal_exponent) {
|
void verify_integer_times_pow10_dflt(Int const mantissa,
|
||||||
|
int16_t const decimal_exponent) {
|
||||||
static_assert(std::is_integral<Int>::value);
|
static_assert(std::is_integral<Int>::value);
|
||||||
|
|
||||||
const auto expected_result =
|
const auto expected_result =
|
||||||
@ -2182,8 +2185,9 @@ void verify_integer_times_pow10_dflt(Int mantissa, int decimal_exponent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Int>
|
template <typename T, typename Int>
|
||||||
void verify_integer_times_pow10(Int mantissa, int decimal_exponent,
|
void verify_integer_times_pow10(Int const mantissa,
|
||||||
T expected) {
|
int16_t const decimal_exponent,
|
||||||
|
T const expected) {
|
||||||
static_assert(std::is_floating_point<T>::value);
|
static_assert(std::is_floating_point<T>::value);
|
||||||
static_assert(std::is_integral<Int>::value);
|
static_assert(std::is_integral<Int>::value);
|
||||||
|
|
||||||
@ -2196,7 +2200,8 @@ void verify_integer_times_pow10(Int mantissa, int decimal_exponent,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Int>
|
template <typename T, typename Int>
|
||||||
void verify_integer_times_pow10(Int mantissa, int decimal_exponent) {
|
void verify_integer_times_pow10(Int const mantissa,
|
||||||
|
int16_t const decimal_exponent) {
|
||||||
static_assert(std::is_floating_point<T>::value);
|
static_assert(std::is_floating_point<T>::value);
|
||||||
static_assert(std::is_integral<Int>::value);
|
static_assert(std::is_integral<Int>::value);
|
||||||
|
|
||||||
@ -2208,7 +2213,8 @@ void verify_integer_times_pow10(Int mantissa, int decimal_exponent) {
|
|||||||
|
|
||||||
namespace all_supported_types {
|
namespace all_supported_types {
|
||||||
template <typename Int>
|
template <typename Int>
|
||||||
void verify_integer_times_pow10(Int mantissa, int decimal_exponent) {
|
void verify_integer_times_pow10(Int const mantissa,
|
||||||
|
int16_t const decimal_exponent) {
|
||||||
static_assert(std::is_integral<Int>::value);
|
static_assert(std::is_integral<Int>::value);
|
||||||
|
|
||||||
// verify the "default" overload
|
// verify the "default" overload
|
||||||
@ -2313,7 +2319,7 @@ TEST_CASE("integer_times_pow10") {
|
|||||||
|
|
||||||
for (int mode : {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, FE_TONEAREST}) {
|
for (int mode : {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, FE_TONEAREST}) {
|
||||||
fesetround(mode);
|
fesetround(mode);
|
||||||
INFO("fesetround(): " << std::string{round_name(mode)});
|
INFO("fesetround(): " << round_name(mode));
|
||||||
|
|
||||||
struct Guard {
|
struct Guard {
|
||||||
~Guard() { fesetround(FE_TONEAREST); }
|
~Guard() { fesetround(FE_TONEAREST); }
|
||||||
|
|||||||
@ -31,6 +31,7 @@ int main() {
|
|||||||
"1d-1", "1d-2", "1d-3", "1d-4"};
|
"1d-1", "1d-2", "1d-3", "1d-4"};
|
||||||
std::vector<std::string> const fmt3{"+1+4", "+1+3", "+1+2", "+1+1", "+1+0",
|
std::vector<std::string> const fmt3{"+1+4", "+1+3", "+1+2", "+1+1", "+1+0",
|
||||||
"+1-1", "+1-2", "+1-3", "+1-4"};
|
"+1-1", "+1-2", "+1-3", "+1-4"};
|
||||||
|
|
||||||
fast_float::parse_options const options{
|
fast_float::parse_options const options{
|
||||||
fast_float::chars_format::fortran |
|
fast_float::chars_format::fortran |
|
||||||
fast_float::chars_format::allow_leading_plus};
|
fast_float::chars_format::allow_leading_plus};
|
||||||
|
|||||||
@ -10,7 +10,7 @@ int main_readme() {
|
|||||||
fast_float::parse_options options{
|
fast_float::parse_options options{
|
||||||
fast_float::chars_format::json |
|
fast_float::chars_format::json |
|
||||||
fast_float::chars_format::allow_leading_plus}; // should be ignored
|
fast_float::chars_format::allow_leading_plus}; // should be ignored
|
||||||
auto answer = fast_float::from_chars_advanced(
|
auto const answer = fast_float::from_chars_advanced(
|
||||||
input.data(), input.data() + input.size(), result, options);
|
input.data(), input.data() + input.size(), result, options);
|
||||||
if (answer.ec == std::errc()) {
|
if (answer.ec == std::errc()) {
|
||||||
std::cerr << "should have failed\n";
|
std::cerr << "should have failed\n";
|
||||||
@ -25,7 +25,7 @@ int main_readme2() {
|
|||||||
fast_float::parse_options options{
|
fast_float::parse_options options{
|
||||||
fast_float::chars_format::json |
|
fast_float::chars_format::json |
|
||||||
fast_float::chars_format::allow_leading_plus}; // should be ignored
|
fast_float::chars_format::allow_leading_plus}; // should be ignored
|
||||||
auto answer = fast_float::from_chars_advanced(
|
auto const answer = fast_float::from_chars_advanced(
|
||||||
input.data(), input.data() + input.size(), result, options);
|
input.data(), input.data() + input.size(), result, options);
|
||||||
if (answer.ec == std::errc()) {
|
if (answer.ec == std::errc()) {
|
||||||
std::cerr << "should have failed\n";
|
std::cerr << "should have failed\n";
|
||||||
@ -41,7 +41,7 @@ int main_readme3() {
|
|||||||
fast_float::parse_options options{
|
fast_float::parse_options options{
|
||||||
fast_float::chars_format::json_or_infnan |
|
fast_float::chars_format::json_or_infnan |
|
||||||
fast_float::chars_format::allow_leading_plus}; // should be ignored
|
fast_float::chars_format::allow_leading_plus}; // should be ignored
|
||||||
auto answer = fast_float::from_chars_advanced(
|
auto const answer = fast_float::from_chars_advanced(
|
||||||
input.data(), input.data() + input.size(), result, options);
|
input.data(), input.data() + input.size(), result, options);
|
||||||
if (answer.ec != std::errc() || (!std::isinf(result))) {
|
if (answer.ec != std::errc() || (!std::isinf(result))) {
|
||||||
std::cerr << "should have parsed infinity\n";
|
std::cerr << "should have parsed infinity\n";
|
||||||
@ -97,7 +97,7 @@ int main() {
|
|||||||
auto const &s = accept[i].input;
|
auto const &s = accept[i].input;
|
||||||
auto const &expected = accept[i].expected;
|
auto const &expected = accept[i].expected;
|
||||||
double result;
|
double result;
|
||||||
auto answer =
|
auto const answer =
|
||||||
fast_float::from_chars(s.data(), s.data() + s.size(), result,
|
fast_float::from_chars(s.data(), s.data() + s.size(), result,
|
||||||
fast_float::chars_format::json_or_infnan);
|
fast_float::chars_format::json_or_infnan);
|
||||||
if (answer.ec != std::errc()) {
|
if (answer.ec != std::errc()) {
|
||||||
@ -120,8 +120,8 @@ int main() {
|
|||||||
for (std::size_t i = 0; i < reject.size(); ++i) {
|
for (std::size_t i = 0; i < reject.size(); ++i) {
|
||||||
auto const &s = reject[i].input;
|
auto const &s = reject[i].input;
|
||||||
double result;
|
double result;
|
||||||
auto answer = fast_float::from_chars(s.data(), s.data() + s.size(), result,
|
auto const answer = fast_float::from_chars(
|
||||||
fast_float::chars_format::json);
|
s.data(), s.data() + s.size(), result, fast_float::chars_format::json);
|
||||||
if (answer.ec == std::errc()) {
|
if (answer.ec == std::errc()) {
|
||||||
std::cerr << "json fmt accepted invalid json " << s << std::endl;
|
std::cerr << "json fmt accepted invalid json " << s << std::endl;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@ -131,12 +131,13 @@ int main() {
|
|||||||
for (std::size_t i = 0; i < reject.size(); ++i) {
|
for (std::size_t i = 0; i < reject.size(); ++i) {
|
||||||
auto const &f = reject[i].input;
|
auto const &f = reject[i].input;
|
||||||
auto const &expected_reason = reject[i].reason;
|
auto const &expected_reason = reject[i].reason;
|
||||||
auto answer = fast_float::parse_number_string<true>(
|
auto const answer = fast_float::parse_number_string<true>(
|
||||||
f.data(), f.data() + f.size(),
|
f.data(), f.data() + f.size(),
|
||||||
fast_float::parse_options(
|
fast_float::parse_options(
|
||||||
fast_float::chars_format::json |
|
fast_float::chars_format::json |
|
||||||
fast_float::chars_format::allow_leading_plus)); // should be ignored
|
fast_float::chars_format::allow_leading_plus)); // should be
|
||||||
if (answer.valid) {
|
// ignored
|
||||||
|
if (!answer.invalid) {
|
||||||
std::cerr << "json parse accepted invalid json " << f << std::endl;
|
std::cerr << "json parse accepted invalid json " << f << std::endl;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user