mirror of
https://github.com/fastfloat/fast_float.git
synced 2026-02-06 17:59:52 +08:00
Merge from upstream/main, fix conflicts
This commit is contained in:
commit
680ccc73ed
@ -5,3 +5,4 @@ Neal Richardson
|
|||||||
Tim Paine
|
Tim Paine
|
||||||
Fabio Pellacini
|
Fabio Pellacini
|
||||||
Lénárd Szolnoki
|
Lénárd Szolnoki
|
||||||
|
Jan Pharago
|
||||||
18
README.md
18
README.md
@ -97,6 +97,24 @@ constexpr double constexptest() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Non-ASCII Inputs
|
||||||
|
|
||||||
|
We also support UTF-16 and UTF-32 inputs, as well as ASCII/UTF-8, as in the following example:
|
||||||
|
|
||||||
|
``` C++
|
||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
const std::u16string input = u"3.1416 xyz ";
|
||||||
|
double result;
|
||||||
|
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
||||||
|
if(answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
|
||||||
|
std::cout << "parsed the number " << result << std::endl;
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Using commas as decimal separator
|
## Using commas as decimal separator
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -18,9 +18,9 @@ namespace fast_float {
|
|||||||
|
|
||||||
// Next function can be micro-optimized, but compilers are entirely
|
// Next function can be micro-optimized, but compilers are entirely
|
||||||
// able to optimize it well.
|
// able to optimize it well.
|
||||||
template <typename CharT>
|
template <typename UC>
|
||||||
fastfloat_really_inline constexpr bool is_integer(CharT c) noexcept {
|
fastfloat_really_inline constexpr bool is_integer(UC c) noexcept {
|
||||||
return c >= CharT('0') && c <= CharT('9');
|
return !(c > UC('9') || c < UC('0'));
|
||||||
}
|
}
|
||||||
|
|
||||||
fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
|
fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
|
||||||
@ -70,11 +70,11 @@ uint64_t simd_read8_to_u64(const char16_t* chars) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Read 8 CharT into a u64. Truncates CharT if != char.
|
// Read 8 UC into a u64. Truncates UC if not char.
|
||||||
template <typename CharT>
|
template <typename UC>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
uint64_t read8_to_u64(const CharT *chars) {
|
uint64_t read8_to_u64(const UC *chars) {
|
||||||
if (cpp20_and_in_constexpr() || !std::is_same<CharT, 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(int i = 0; i < 8; ++i) {
|
||||||
val |= uint64_t(char(*chars)) << (i*8);
|
val |= uint64_t(char(*chars)) << (i*8);
|
||||||
@ -135,7 +135,7 @@ uint32_t parse_eight_digits_unrolled(const char16_t* chars) noexcept {
|
|||||||
#ifdef FASTFLOAT_HAS_SIMD
|
#ifdef FASTFLOAT_HAS_SIMD
|
||||||
return parse_eight_digits_unrolled(simd_read8_to_u64(chars));
|
return parse_eight_digits_unrolled(simd_read8_to_u64(chars));
|
||||||
#else
|
#else
|
||||||
// never reaches here, remove warning
|
// never reaches here, removes warning
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -150,7 +150,7 @@ uint32_t parse_eight_digits_unrolled(const char32_t* chars) noexcept {
|
|||||||
// credit @aqrit
|
// credit @aqrit
|
||||||
fastfloat_really_inline constexpr bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
|
fastfloat_really_inline constexpr bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
|
||||||
return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
|
return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
|
||||||
0x8080808080808080));
|
0x8080808080808080));
|
||||||
}
|
}
|
||||||
|
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
@ -199,50 +199,47 @@ bool parse_if_eight_digits_unrolled(const char32_t*, uint64_t&) noexcept {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename UC>
|
||||||
|
struct parsed_number_string_t {
|
||||||
typedef span<const char> byte_span;
|
|
||||||
|
|
||||||
template <typename CharT>
|
|
||||||
struct parsed_number_string {
|
|
||||||
int64_t exponent{0};
|
int64_t exponent{0};
|
||||||
uint64_t mantissa{0};
|
uint64_t mantissa{0};
|
||||||
int64_t exp_number{0};
|
int64_t exp_number{0};
|
||||||
const CharT *lastmatch{nullptr};
|
UC const * lastmatch{nullptr};
|
||||||
bool negative{false};
|
bool negative{false};
|
||||||
bool valid{false};
|
bool valid{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<const CharT> integer{}; // non-nullable
|
span<const UC> integer{}; // non-nullable
|
||||||
span<const CharT> fraction{}; // nullable
|
span<const UC> fraction{}; // nullable
|
||||||
};
|
};
|
||||||
|
using byte_span = span<const char>;
|
||||||
|
using parsed_number_string = parsed_number_string_t<char>;
|
||||||
// Assuming that you use no more than 19 digits, this will
|
// Assuming that you use no more than 19 digits, this will
|
||||||
// parse an ASCII string.
|
// parse an ASCII string.
|
||||||
template <typename CharT>
|
template <typename UC>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
parsed_number_string<CharT> parse_number_string(const CharT *p, const CharT *pend, parse_options options) noexcept {
|
parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, parse_options_t<UC> options) noexcept {
|
||||||
const chars_format fmt = options.format;
|
chars_format const fmt = options.format;
|
||||||
const CharT decimal_point = options.decimal_point;
|
UC const decimal_point = options.decimal_point;
|
||||||
|
|
||||||
parsed_number_string<CharT> answer;
|
parsed_number_string_t<UC> answer;
|
||||||
answer.valid = false;
|
answer.valid = false;
|
||||||
answer.too_many_digits = false;
|
answer.too_many_digits = false;
|
||||||
answer.negative = (*p == CharT('-'));
|
answer.negative = (*p == UC('-'));
|
||||||
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
|
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
|
||||||
if ((*p == CharT('-')) || (*p == CharT('+'))) {
|
if ((*p == UC('-')) || (*p == UC('+'))) {
|
||||||
#else
|
#else
|
||||||
if (*p == CharT('-')) { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
|
if (*p == UC('-')) { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
|
||||||
#endif
|
#endif
|
||||||
++p;
|
++p;
|
||||||
if (p == pend) {
|
if (p == pend) {
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
// a sign must be followed by an integer or the dot
|
if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot
|
||||||
if (!is_integer(*p) && *p != decimal_point)
|
return answer;
|
||||||
return answer;
|
}
|
||||||
}
|
}
|
||||||
const CharT *const start_digits = p;
|
UC const * const start_digits = p;
|
||||||
|
|
||||||
uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
|
uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
|
||||||
|
|
||||||
@ -250,43 +247,42 @@ parsed_number_string<CharT> parse_number_string(const CharT *p, const CharT *pen
|
|||||||
// 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 +
|
i = 10 * i +
|
||||||
uint64_t(*p - CharT('0')); // might overflow, we will handle the overflow later
|
uint64_t(*p - UC('0')); // might overflow, we will handle the overflow later
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
const CharT *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);
|
int64_t digit_count = int64_t(end_of_integer_part - start_digits);
|
||||||
answer.integer = span<const CharT>(start_digits, size_t(digit_count));
|
answer.integer = span<const UC>(start_digits, size_t(digit_count));
|
||||||
int64_t exponent = 0;
|
int64_t exponent = 0;
|
||||||
const bool has_decimal_point = (p != pend) && (*p == decimal_point);
|
if ((p != pend) && (*p == decimal_point)) {
|
||||||
if (has_decimal_point) {
|
|
||||||
++p;
|
++p;
|
||||||
const CharT* before = p;
|
UC 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.
|
||||||
while ((std::distance(p, pend) >= 8) && parse_if_eight_digits_unrolled(p, i)) { // in rare cases, this will overflow, but that's ok
|
while ((std::distance(p, pend) >= 8) && parse_if_eight_digits_unrolled(p, i)) { // in rare cases, this will overflow, but that's ok
|
||||||
p += 8;
|
p += 8;
|
||||||
}
|
}
|
||||||
while ((p != pend) && is_integer(*p)) {
|
while ((p != pend) && is_integer(*p)) {
|
||||||
i = i * 10 + uint64_t(*p - CharT('0')); // in rare cases, this will overflow, but that's ok
|
uint8_t digit = uint8_t(*p - UC('0'));
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
exponent = before - p;
|
exponent = before - p;
|
||||||
answer.fraction = span<const CharT>(before, size_t(p - before));
|
answer.fraction = span<const UC>(before, size_t(p - before));
|
||||||
digit_count -= exponent;
|
digit_count -= exponent;
|
||||||
}
|
}
|
||||||
// we must have encountered at least one integer
|
// we must have encountered at least one integer!
|
||||||
if (digit_count == 0) {
|
if (digit_count == 0) {
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
int64_t exp_number = 0; // explicit exponential part
|
int64_t exp_number = 0; // explicit exponential part
|
||||||
if ((fmt & chars_format::scientific) && (p != pend) && ((CharT('e') == *p) || (CharT('E') == *p))) {
|
if ((fmt & chars_format::scientific) && (p != pend) && ((UC('e') == *p) || (UC('E') == *p))) {
|
||||||
const CharT * location_of_e = p;
|
UC const * location_of_e = p;
|
||||||
++p;
|
++p;
|
||||||
bool neg_exp = false;
|
bool neg_exp = false;
|
||||||
if ((p != pend) && (CharT('-') == *p)) {
|
if ((p != pend) && (UC('-') == *p)) {
|
||||||
neg_exp = true;
|
neg_exp = true;
|
||||||
++p;
|
++p;
|
||||||
} else if ((p != pend) && (CharT('+') == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
|
} else if ((p != pend) && (UC('+') == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
if ((p == pend) || !is_integer(*p)) {
|
if ((p == pend) || !is_integer(*p)) {
|
||||||
@ -298,7 +294,7 @@ parsed_number_string<CharT> parse_number_string(const CharT *p, const CharT *pen
|
|||||||
p = location_of_e;
|
p = location_of_e;
|
||||||
} else {
|
} else {
|
||||||
while ((p != pend) && is_integer(*p)) {
|
while ((p != pend) && is_integer(*p)) {
|
||||||
uint8_t digit = uint8_t(*p - CharT('0'));
|
uint8_t digit = uint8_t(*p - UC('0'));
|
||||||
if (exp_number < 0x10000000) {
|
if (exp_number < 0x10000000) {
|
||||||
exp_number = 10 * exp_number + digit;
|
exp_number = 10 * exp_number + digit;
|
||||||
}
|
}
|
||||||
@ -325,13 +321,13 @@ parsed_number_string<CharT> parse_number_string(const CharT *p, const CharT *pen
|
|||||||
// 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.
|
||||||
const CharT *start = start_digits;
|
UC const * start = start_digits;
|
||||||
while ((start != pend) && (*start == CharT('0') || *start == decimal_point)) {
|
while ((start != pend) && (*start == UC('0') || *start == decimal_point)) {
|
||||||
if(*start == CharT('0')) { digit_count --; }
|
if(*start == UC('0')) { digit_count --; }
|
||||||
start++;
|
start++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// exponent/mantissa must be truncated later
|
// exponent/mantissa must be truncated later!
|
||||||
// this is unlikely, so don't inline truncation code with the rest of parse_number_string()
|
// this is unlikely, so don't inline truncation code with the rest of parse_number_string()
|
||||||
answer.too_many_digits = digit_count > 19;
|
answer.too_many_digits = digit_count > 19;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,9 +23,9 @@ constexpr static uint64_t powers_of_ten_uint64[] = {
|
|||||||
// 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.
|
||||||
template <typename CharT>
|
template <typename UC>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||||
int32_t scientific_exponent(parsed_number_string<CharT>& num) noexcept {
|
int32_t scientific_exponent(parsed_number_string_t<UC> & num) noexcept {
|
||||||
uint64_t mantissa = num.mantissa;
|
uint64_t mantissa = num.mantissa;
|
||||||
int32_t exponent = int32_t(num.exponent);
|
int32_t exponent = int32_t(num.exponent);
|
||||||
while (mantissa >= 10000) {
|
while (mantissa >= 10000) {
|
||||||
@ -154,22 +154,19 @@ void round_down(adjusted_mantissa& am, int32_t shift) noexcept {
|
|||||||
}
|
}
|
||||||
am.power2 += shift;
|
am.power2 += shift;
|
||||||
}
|
}
|
||||||
|
template <typename UC>
|
||||||
template <typename CharT>
|
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
void skip_zeros(const CharT*& first, const CharT* last) noexcept {
|
void skip_zeros(UC const * & first, UC const * last) noexcept {
|
||||||
if (std::is_same<CharT, char>::value) {
|
uint64_t val;
|
||||||
uint64_t val;
|
while (!cpp20_and_in_constexpr() && std::distance(first, last) >= int_cmp_len<UC>()) {
|
||||||
while (!cpp20_and_in_constexpr() && std::distance(first, last) >= 8) {
|
::memcpy(&val, first, sizeof(uint64_t));
|
||||||
::memcpy(&val, first, sizeof(uint64_t));
|
if (val != int_cmp_zeros<UC>()) {
|
||||||
if (val != 0x3030303030303030) {
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
first += 8;
|
|
||||||
}
|
}
|
||||||
|
first += int_cmp_len<UC>();
|
||||||
}
|
}
|
||||||
while (first != last) {
|
while (first != last) {
|
||||||
if (*first != CharT('0')) {
|
if (*first != UC('0')) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
first++;
|
first++;
|
||||||
@ -178,48 +175,46 @@ void skip_zeros(const CharT*& first, const CharT* last) noexcept {
|
|||||||
|
|
||||||
// determine if any non-zero digits were truncated.
|
// determine if any non-zero digits were truncated.
|
||||||
// all characters must be valid digits.
|
// all characters must be valid digits.
|
||||||
template <typename CharT>
|
template <typename UC>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
bool is_truncated(const CharT* first, const CharT* last) noexcept {
|
bool is_truncated(UC const * first, UC const * last) noexcept {
|
||||||
if (std::is_same<CharT, char>::value) {
|
// 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;
|
||||||
uint64_t val;
|
while (!cpp20_and_in_constexpr() && std::distance(first, last) >= int_cmp_len<UC>()) {
|
||||||
while (!cpp20_and_in_constexpr() && std::distance(first, last) >= 8) {
|
::memcpy(&val, first, sizeof(uint64_t));
|
||||||
::memcpy(&val, first, sizeof(uint64_t));
|
if (val != int_cmp_zeros<UC>()) {
|
||||||
if (val != 0x3030303030303030) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
first += 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (first != last) {
|
|
||||||
if (*first != CharT('0')) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
first++;
|
first += int_cmp_len<UC>();
|
||||||
|
}
|
||||||
|
while (first != last) {
|
||||||
|
if (*first != UC('0')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
++first;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
template <typename UC>
|
||||||
template <typename CharT>
|
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
bool is_truncated(span<const CharT> s) noexcept {
|
bool is_truncated(span<const UC> s) noexcept {
|
||||||
return is_truncated(s.ptr, s.ptr + s.len());
|
return is_truncated(s.ptr, s.ptr + s.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename CharT>
|
|
||||||
|
template <typename UC>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
void parse_eight_digits(const CharT*& p, limb& value, size_t& counter, size_t& count) noexcept {
|
void parse_eight_digits(const UC*& p, limb& value, size_t& counter, size_t& 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;
|
||||||
count += 8;
|
count += 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename CharT>
|
template <typename UC>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||||
void parse_one_digit(const CharT*& p, limb& value, size_t& counter, size_t& count) noexcept {
|
void parse_one_digit(UC const *& p, limb& value, size_t& counter, size_t& count) noexcept {
|
||||||
value = value * 10 + limb(*p - CharT('0'));
|
value = value * 10 + limb(*p - UC('0'));
|
||||||
p++;
|
p++;
|
||||||
counter++;
|
counter++;
|
||||||
count++;
|
count++;
|
||||||
@ -240,9 +235,9 @@ void round_up_bigint(bigint& big, size_t& count) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parse the significant digits into a big integer
|
// parse the significant digits into a big integer
|
||||||
template <typename CharT>
|
template <typename UC>
|
||||||
inline FASTFLOAT_CONSTEXPR20
|
inline FASTFLOAT_CONSTEXPR20
|
||||||
void parse_mantissa(bigint& result, parsed_number_string<CharT>& num, size_t max_digits, size_t& digits) noexcept {
|
void parse_mantissa(bigint& result, parsed_number_string_t<UC>& num, 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.
|
||||||
@ -256,8 +251,8 @@ void parse_mantissa(bigint& result, parsed_number_string<CharT>& num, size_t max
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// process all integer digits.
|
// process all integer digits.
|
||||||
const CharT* p = num.integer.ptr;
|
UC const * p = num.integer.ptr;
|
||||||
const CharT* pend = p + num.integer.len();
|
UC const * pend = p + num.integer.len();
|
||||||
skip_zeros(p, pend);
|
skip_zeros(p, pend);
|
||||||
// process all digits, in increments of step per loop
|
// process all digits, in increments of step per loop
|
||||||
while (p != pend) {
|
while (p != pend) {
|
||||||
@ -406,9 +401,9 @@ adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int
|
|||||||
// `b` as a big-integer type, scaled to the same binary exponent as
|
// `b` as a big-integer type, scaled to the same binary exponent as
|
||||||
// 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 CharT>
|
template <typename T, typename UC>
|
||||||
inline FASTFLOAT_CONSTEXPR20
|
inline FASTFLOAT_CONSTEXPR20
|
||||||
adjusted_mantissa digit_comp(parsed_number_string<CharT>& num, adjusted_mantissa am) noexcept {
|
adjusted_mantissa digit_comp(parsed_number_string_t<UC>& 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;
|
||||||
|
|
||||||
|
|||||||
@ -13,22 +13,25 @@ enum chars_format {
|
|||||||
general = fixed | scientific
|
general = fixed | scientific
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename CharT>
|
template <typename UC>
|
||||||
struct from_chars_result {
|
struct from_chars_result_t {
|
||||||
const CharT *ptr;
|
UC const * ptr;
|
||||||
std::errc ec;
|
std::errc ec;
|
||||||
};
|
};
|
||||||
|
using from_chars_result = from_chars_result_t<char>;
|
||||||
|
|
||||||
struct parse_options {
|
template <typename UC>
|
||||||
constexpr explicit parse_options(
|
struct parse_options_t {
|
||||||
chars_format fmt = chars_format::general, char dot = '.')
|
constexpr explicit parse_options_t(chars_format fmt = chars_format::general,
|
||||||
|
UC dot = UC('.'))
|
||||||
: format(fmt), decimal_point(dot) {}
|
: format(fmt), decimal_point(dot) {}
|
||||||
|
|
||||||
/** 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 */
|
||||||
char decimal_point;
|
UC decimal_point;
|
||||||
};
|
};
|
||||||
|
using parse_options = parse_options_t<char>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function parses the character sequence [first,last) for a number. It parses floating-point numbers expecting
|
* This function parses the character sequence [first,last) for a number. It parses floating-point numbers expecting
|
||||||
@ -49,21 +52,19 @@ struct parse_options {
|
|||||||
* to determine whether we allow the fixed point and scientific notation respectively.
|
* to determine whether we allow the fixed point and scientific notation respectively.
|
||||||
* The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
|
* The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
|
||||||
*/
|
*/
|
||||||
template<typename T, typename CharT>
|
template<typename T, typename UC = char>
|
||||||
FASTFLOAT_CONSTEXPR20
|
FASTFLOAT_CONSTEXPR20
|
||||||
from_chars_result<CharT> from_chars(const CharT *first, const CharT *last,
|
from_chars_result_t<UC> from_chars(UC const * first, UC const * last,
|
||||||
T &value, chars_format fmt = chars_format::general) noexcept;
|
T &value, chars_format 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.
|
||||||
*/
|
*/
|
||||||
template<typename T, typename CharT>
|
template<typename T, typename UC = char>
|
||||||
FASTFLOAT_CONSTEXPR20
|
FASTFLOAT_CONSTEXPR20
|
||||||
from_chars_result<CharT> from_chars_advanced(const CharT *first, const CharT *last,
|
from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
|
||||||
T &value, parse_options options) noexcept;
|
T &value, parse_options_t<UC> options) noexcept;
|
||||||
|
|
||||||
}
|
} // namespace fast_float
|
||||||
|
|
||||||
// namespace fast_float
|
|
||||||
#include "parse_number.h"
|
#include "parse_number.h"
|
||||||
#endif // FASTFLOAT_FAST_FLOAT_H
|
#endif // FASTFLOAT_FAST_FLOAT_H
|
||||||
|
|||||||
@ -136,13 +136,12 @@ fastfloat_really_inline constexpr bool has_simd() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compares two ASCII strings in a case insensitive manner.
|
// Compares two ASCII strings in a case insensitive manner.
|
||||||
// maya: for now, keep input2 ASCII only
|
template <typename UC>
|
||||||
template <typename CharT>
|
|
||||||
inline FASTFLOAT_CONSTEXPR14 bool
|
inline FASTFLOAT_CONSTEXPR14 bool
|
||||||
fastfloat_strncasecmp(const CharT *input1, const char *input2, size_t length) {
|
fastfloat_strncasecmp(UC const * input1, UC const * input2, size_t length) {
|
||||||
char running_diff{0};
|
char running_diff{0};
|
||||||
for (size_t i = 0; i < length; i++) {
|
for (size_t i = 0; i < length; ++i) {
|
||||||
running_diff |= (char(input1[i]) ^ input2[i]);
|
running_diff |= (char(input1[i]) ^ char(input2[i]));
|
||||||
}
|
}
|
||||||
return (running_diff == 0) || (running_diff == 32);
|
return (running_diff == 0) || (running_diff == 32);
|
||||||
}
|
}
|
||||||
@ -535,6 +534,68 @@ constexpr bool space_lut<T>::value[];
|
|||||||
|
|
||||||
inline constexpr bool is_space(uint8_t c) { return space_lut<>::value[c]; }
|
inline constexpr bool is_space(uint8_t c) { return space_lut<>::value[c]; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
template<typename UC>
|
||||||
|
static constexpr uint64_t int_cmp_zeros()
|
||||||
|
{
|
||||||
|
static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4), "Unsupported character size");
|
||||||
|
return (sizeof(UC) == 1) ? 0x3030303030303030 : (sizeof(UC) == 2) ? (uint64_t(UC('0')) << 48 | uint64_t(UC('0')) << 32 | uint64_t(UC('0')) << 16 | UC('0')) : (uint64_t(UC('0')) << 32 | UC('0'));
|
||||||
|
}
|
||||||
|
template<typename UC>
|
||||||
|
static constexpr int int_cmp_len()
|
||||||
|
{
|
||||||
|
return sizeof(uint64_t) / sizeof(UC);
|
||||||
|
}
|
||||||
|
template<typename UC>
|
||||||
|
static constexpr UC const * str_const_nan()
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
constexpr char const * str_const_nan<char>()
|
||||||
|
{
|
||||||
|
return "nan";
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
constexpr wchar_t const * str_const_nan<wchar_t>()
|
||||||
|
{
|
||||||
|
return L"nan";
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
constexpr char16_t const * str_const_nan<char16_t>()
|
||||||
|
{
|
||||||
|
return u"nan";
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
constexpr char32_t const * str_const_nan<char32_t>()
|
||||||
|
{
|
||||||
|
return U"nan";
|
||||||
|
}
|
||||||
|
template<typename UC>
|
||||||
|
static constexpr UC const * str_const_inf()
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
constexpr char const * str_const_inf<char>()
|
||||||
|
{
|
||||||
|
return "infinity";
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
constexpr wchar_t const * str_const_inf<wchar_t>()
|
||||||
|
{
|
||||||
|
return L"infinity";
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
constexpr char16_t const * str_const_inf<char16_t>()
|
||||||
|
{
|
||||||
|
return u"infinity";
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
constexpr char32_t const * str_const_inf<char32_t>()
|
||||||
|
{
|
||||||
|
return U"infinity";
|
||||||
|
}
|
||||||
} // namespace fast_float
|
} // namespace fast_float
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -19,43 +19,41 @@ namespace detail {
|
|||||||
* 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
|
||||||
* strings a null-free and fixed.
|
* strings a null-free and fixed.
|
||||||
**/
|
**/
|
||||||
template <typename T, typename CharT>
|
template <typename T, typename UC>
|
||||||
from_chars_result<CharT> FASTFLOAT_CONSTEXPR14
|
from_chars_result_t<UC> FASTFLOAT_CONSTEXPR14
|
||||||
parse_infnan(const CharT *first, const CharT *last, T &value) noexcept {
|
parse_infnan(UC const * first, UC const * last, T &value) noexcept {
|
||||||
from_chars_result<CharT> 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
|
||||||
bool minusSign = false;
|
bool minusSign = false;
|
||||||
if (*first == CharT('-')) { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here
|
if (*first == UC('-')) { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here
|
||||||
minusSign = true;
|
minusSign = true;
|
||||||
++first;
|
++first;
|
||||||
}
|
}
|
||||||
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
|
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
|
||||||
if (*first == CharT('+')) {
|
if (*first == UC('+')) {
|
||||||
++first;
|
++first;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (last - first >= 3) {
|
if (last - first >= 3) {
|
||||||
if (fastfloat_strncasecmp(first, "nan", 3)) {
|
if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
|
||||||
answer.ptr = (first += 3);
|
answer.ptr = (first += 3);
|
||||||
value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN();
|
value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN();
|
||||||
// Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan).
|
// Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan).
|
||||||
if(first != last && *first == CharT('(')) {
|
if(first != last && *first == UC('(')) {
|
||||||
for(const CharT* ptr = first + 1; ptr != last; ++ptr) {
|
for(UC const * ptr = first + 1; ptr != last; ++ptr) {
|
||||||
if (*ptr == CharT(')')) {
|
if (*ptr == UC(')')) {
|
||||||
answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
|
answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if(!((CharT('a') <= *ptr && *ptr <= CharT('z')) ||
|
else if(!((UC('a') <= *ptr && *ptr <= UC('z')) || (UC('A') <= *ptr && *ptr <= UC('Z')) || (UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_')))
|
||||||
(CharT('A') <= *ptr && *ptr <= CharT('Z')) ||
|
|
||||||
(CharT('0') <= *ptr && *ptr <= CharT('9')) || *ptr == CharT('_')))
|
|
||||||
break; // forbidden char, not nan(n-char-seq-opt)
|
break; // forbidden char, not nan(n-char-seq-opt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
if (fastfloat_strncasecmp(first, "inf", 3)) {
|
if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) {
|
||||||
if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, "inity", 5)) {
|
if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, str_const_inf<UC>() + 3, 5)) {
|
||||||
answer.ptr = first + 8;
|
answer.ptr = first + 8;
|
||||||
} else {
|
} else {
|
||||||
answer.ptr = first + 3;
|
answer.ptr = first + 3;
|
||||||
@ -134,22 +132,25 @@ fastfloat_really_inline bool rounds_to_nearest() noexcept {
|
|||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
template<typename T, typename CharT>
|
template<typename T, typename UC>
|
||||||
FASTFLOAT_CONSTEXPR20
|
FASTFLOAT_CONSTEXPR20
|
||||||
from_chars_result<CharT> from_chars(const CharT *first, const CharT *last,
|
from_chars_result_t<UC> from_chars(UC const * first, UC const * last,
|
||||||
T &value, chars_format fmt /*= chars_format::general*/) noexcept {
|
T &value, chars_format fmt /*= chars_format::general*/) noexcept {
|
||||||
return from_chars_advanced(first, last, value, parse_options{fmt});
|
return from_chars_advanced(first, last, value, parse_options_t<UC>{fmt});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename CharT>
|
template<typename T, typename UC>
|
||||||
FASTFLOAT_CONSTEXPR20
|
FASTFLOAT_CONSTEXPR20
|
||||||
from_chars_result<CharT> from_chars_advanced(const CharT *first, const CharT *last,
|
from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
|
||||||
T &value, parse_options options) noexcept {
|
T &value, parse_options_t<UC> options) noexcept {
|
||||||
{
|
|
||||||
static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported");
|
static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported");
|
||||||
|
static_assert (std::is_same<UC, char>::value ||
|
||||||
|
std::is_same<UC, wchar_t>::value ||
|
||||||
|
std::is_same<UC, char16_t>::value ||
|
||||||
|
std::is_same<UC, char32_t>::value , "only char, wchar_t, char16_t and char32_t are supported");
|
||||||
|
|
||||||
|
from_chars_result_t<UC> answer;
|
||||||
from_chars_result<CharT> answer;
|
|
||||||
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
|
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
|
||||||
while ((first != last) && fast_float::is_space(uint8_t(*first))) {
|
while ((first != last) && fast_float::is_space(uint8_t(*first))) {
|
||||||
first++;
|
first++;
|
||||||
@ -160,7 +161,7 @@ from_chars_result<CharT> from_chars_advanced(const CharT *first, const CharT *la
|
|||||||
answer.ptr = first;
|
answer.ptr = first;
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
parsed_number_string<CharT> pns = parse_number_string(first, last, options);
|
parsed_number_string_t<UC> pns = parse_number_string<UC>(first, last, options);
|
||||||
if (!pns.valid) {
|
if (!pns.valid) {
|
||||||
return detail::parse_infnan(first, last, value);
|
return detail::parse_infnan(first, last, value);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -644,9 +644,9 @@ enum class Diag { runtime, comptime };
|
|||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
template <Diag diag, class T>
|
template <Diag diag, class T, typename result_type, typename stringtype>
|
||||||
constexpr void check_basic_test_result(std::string_view str,
|
constexpr void check_basic_test_result(stringtype str,
|
||||||
fast_float::from_chars_result result,
|
result_type result,
|
||||||
T actual, T expected, std::errc expected_ec) {
|
T actual, T expected, std::errc expected_ec) {
|
||||||
if constexpr (diag == Diag::runtime) {
|
if constexpr (diag == Diag::runtime) {
|
||||||
INFO(
|
INFO(
|
||||||
@ -702,11 +702,33 @@ constexpr void check_basic_test_result(std::string_view str,
|
|||||||
#undef FASTFLOAT_CHECK_EQ
|
#undef FASTFLOAT_CHECK_EQ
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<Diag diag, class T>
|
template<Diag diag, class T>
|
||||||
constexpr void basic_test(std::string_view str, T expected, std::errc expected_ec = std::errc()) {
|
constexpr void basic_test(std::string_view str, T expected, std::errc expected_ec = std::errc()) {
|
||||||
T actual;
|
T actual;
|
||||||
auto result = fast_float::from_chars(str.data(), str.data() + str.size(), actual);
|
auto result = fast_float::from_chars(str.data(), str.data() + str.size(), actual);
|
||||||
check_basic_test_result<diag>(str, result, actual, expected, expected_ec);
|
check_basic_test_result<diag>(str, result, actual, expected, expected_ec);
|
||||||
|
constexpr size_t global_string_capacity = 2048;
|
||||||
|
|
||||||
|
if(str.size() > global_string_capacity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We give plenty of memory: 2048 characters.
|
||||||
|
char16_t u16[global_string_capacity]{};
|
||||||
|
|
||||||
|
for (size_t i = 0; i < str.size(); i++) {
|
||||||
|
u16[i] = char16_t(str[i]);
|
||||||
|
}
|
||||||
|
auto result16 = fast_float::from_chars(u16, u16 + str.size(), actual);
|
||||||
|
check_basic_test_result<diag>(std::u16string_view(u16, str.size()), result16, actual, expected, expected_ec);
|
||||||
|
|
||||||
|
char32_t u32[global_string_capacity]{};
|
||||||
|
|
||||||
|
for (size_t i = 0; i < str.size(); i++) {
|
||||||
|
u32[i] = char32_t(str[i]);
|
||||||
|
}
|
||||||
|
auto result32 = fast_float::from_chars(u32, u32 + str.size(), actual);
|
||||||
|
check_basic_test_result<diag>(std::u32string_view(u32, str.size()), result32, actual, expected, expected_ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Diag diag, class T>
|
template<Diag diag, class T>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user