mirror of
https://github.com/fastfloat/fast_float.git
synced 2025-12-06 08:46:49 +08:00
lint
This commit is contained in:
parent
8f3dae6b9f
commit
5ad6aae0b1
0
clang-format-ignore.txt
Normal file
0
clang-format-ignore.txt
Normal file
@ -4,19 +4,19 @@
|
||||
#include <system_error>
|
||||
|
||||
fast_float::chars_format arbitrary_format(FuzzedDataProvider &fdp) {
|
||||
using fast_float::chars_format;
|
||||
switch (fdp.ConsumeIntegralInRange<int>(0,3)) {
|
||||
case 0:
|
||||
return chars_format::scientific;
|
||||
break;
|
||||
case 1:
|
||||
return chars_format::fixed;
|
||||
break;
|
||||
case 2:
|
||||
return chars_format::fixed;
|
||||
break;
|
||||
}
|
||||
return chars_format::general;
|
||||
using fast_float::chars_format;
|
||||
switch (fdp.ConsumeIntegralInRange<int>(0, 3)) {
|
||||
case 0:
|
||||
return chars_format::scientific;
|
||||
break;
|
||||
case 1:
|
||||
return chars_format::fixed;
|
||||
break;
|
||||
case 2:
|
||||
return chars_format::fixed;
|
||||
break;
|
||||
}
|
||||
return chars_format::general;
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
@ -24,15 +24,15 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
fast_float::chars_format format = arbitrary_format(fdp);
|
||||
double result_d = 0.0;
|
||||
std::string input_d = fdp.ConsumeRandomLengthString(128);
|
||||
auto answer =
|
||||
fast_float::from_chars(input_d.data(), input_d.data() + input_d.size(), result_d, format);
|
||||
auto answer = fast_float::from_chars(
|
||||
input_d.data(), input_d.data() + input_d.size(), result_d, format);
|
||||
std::string input_f = fdp.ConsumeRandomLengthString(128);
|
||||
float result_f = 0.0;
|
||||
answer =
|
||||
fast_float::from_chars(input_f.data(), input_f.data() + input_f.size(), result_f, format);
|
||||
answer = fast_float::from_chars(
|
||||
input_f.data(), input_f.data() + input_f.size(), result_f, format);
|
||||
int result_i = 0;
|
||||
std::string input_i = fdp.ConsumeRandomLengthString(128);
|
||||
answer =
|
||||
fast_float::from_chars(input_i.data(), input_i.data() + input_i.size(), result_i);
|
||||
answer = fast_float::from_chars(input_i.data(),
|
||||
input_i.data() + input_i.size(), result_i);
|
||||
return 0;
|
||||
}
|
||||
@ -20,8 +20,7 @@
|
||||
|
||||
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() {
|
||||
#ifdef FASTFLOAT_HAS_SIMD
|
||||
return std::is_same<UC, char16_t>::value;
|
||||
#else
|
||||
@ -37,24 +36,20 @@ fastfloat_really_inline constexpr bool is_integer(UC c) noexcept {
|
||||
}
|
||||
|
||||
fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
|
||||
return (val & 0xFF00000000000000) >> 56
|
||||
| (val & 0x00FF000000000000) >> 40
|
||||
| (val & 0x0000FF0000000000) >> 24
|
||||
| (val & 0x000000FF00000000) >> 8
|
||||
| (val & 0x00000000FF000000) << 8
|
||||
| (val & 0x0000000000FF0000) << 24
|
||||
| (val & 0x000000000000FF00) << 40
|
||||
| (val & 0x00000000000000FF) << 56;
|
||||
return (val & 0xFF00000000000000) >> 56 | (val & 0x00FF000000000000) >> 40 |
|
||||
(val & 0x0000FF0000000000) >> 24 | (val & 0x000000FF00000000) >> 8 |
|
||||
(val & 0x00000000FF000000) << 8 | (val & 0x0000000000FF0000) << 24 |
|
||||
(val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56;
|
||||
}
|
||||
|
||||
// Read 8 UC into a u64. Truncates UC if not char.
|
||||
template <typename UC>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
uint64_t read8_to_u64(const UC *chars) {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
|
||||
read8_to_u64(const UC *chars) {
|
||||
if (cpp20_and_in_constexpr() || !std::is_same<UC, char>::value) {
|
||||
uint64_t val = 0;
|
||||
for(int i = 0; i < 8; ++i) {
|
||||
val |= uint64_t(uint8_t(*chars)) << (i*8);
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
val |= uint64_t(uint8_t(*chars)) << (i * 8);
|
||||
++chars;
|
||||
}
|
||||
return val;
|
||||
@ -70,44 +65,41 @@ uint64_t read8_to_u64(const UC *chars) {
|
||||
|
||||
#ifdef FASTFLOAT_SSE2
|
||||
|
||||
fastfloat_really_inline
|
||||
uint64_t simd_read8_to_u64(const __m128i data) {
|
||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
fastfloat_really_inline uint64_t simd_read8_to_u64(const __m128i data) {
|
||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
const __m128i packed = _mm_packus_epi16(data, data);
|
||||
#ifdef FASTFLOAT_64BIT
|
||||
return uint64_t(_mm_cvtsi128_si64(packed));
|
||||
#else
|
||||
uint64_t value;
|
||||
// Visual Studio + older versions of GCC don't support _mm_storeu_si64
|
||||
_mm_storel_epi64(reinterpret_cast<__m128i*>(&value), packed);
|
||||
_mm_storel_epi64(reinterpret_cast<__m128i *>(&value), packed);
|
||||
return value;
|
||||
#endif
|
||||
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||
}
|
||||
|
||||
fastfloat_really_inline
|
||||
uint64_t simd_read8_to_u64(const char16_t* chars) {
|
||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
return simd_read8_to_u64(_mm_loadu_si128(reinterpret_cast<const __m128i*>(chars)));
|
||||
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||
fastfloat_really_inline uint64_t simd_read8_to_u64(const char16_t *chars) {
|
||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
return simd_read8_to_u64(
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i *>(chars)));
|
||||
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||
}
|
||||
|
||||
#elif defined(FASTFLOAT_NEON)
|
||||
|
||||
|
||||
fastfloat_really_inline
|
||||
uint64_t simd_read8_to_u64(const uint16x8_t data) {
|
||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
fastfloat_really_inline uint64_t simd_read8_to_u64(const uint16x8_t data) {
|
||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
uint8x8_t utf8_packed = vmovn_u16(data);
|
||||
return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0);
|
||||
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||
}
|
||||
|
||||
fastfloat_really_inline
|
||||
uint64_t simd_read8_to_u64(const char16_t* chars) {
|
||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
return simd_read8_to_u64(vld1q_u16(reinterpret_cast<const uint16_t*>(chars)));
|
||||
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||
fastfloat_really_inline uint64_t simd_read8_to_u64(const char16_t *chars) {
|
||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
return simd_read8_to_u64(
|
||||
vld1q_u16(reinterpret_cast<const uint16_t *>(chars)));
|
||||
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||
}
|
||||
|
||||
#endif // FASTFLOAT_SSE2
|
||||
@ -119,13 +111,13 @@ template <typename UC>
|
||||
template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
|
||||
#endif
|
||||
// dummy for compile
|
||||
uint64_t simd_read8_to_u64(UC const*) {
|
||||
uint64_t simd_read8_to_u64(UC const *) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// credit @aqrit
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||
uint32_t parse_eight_digits_unrolled(uint64_t val) {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t
|
||||
parse_eight_digits_unrolled(uint64_t val) {
|
||||
const uint64_t mask = 0x000000FF000000FF;
|
||||
const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
|
||||
const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
|
||||
@ -135,38 +127,38 @@ uint32_t parse_eight_digits_unrolled(uint64_t val) {
|
||||
return uint32_t(val);
|
||||
}
|
||||
|
||||
|
||||
// Call this if chars are definitely 8 digits.
|
||||
template <typename UC>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
uint32_t parse_eight_digits_unrolled(UC const * chars) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint32_t
|
||||
parse_eight_digits_unrolled(UC const *chars) noexcept {
|
||||
if (cpp20_and_in_constexpr() || !has_simd_opt<UC>()) {
|
||||
return parse_eight_digits_unrolled(read8_to_u64(chars)); // truncation okay
|
||||
}
|
||||
return parse_eight_digits_unrolled(simd_read8_to_u64(chars));
|
||||
}
|
||||
|
||||
|
||||
// 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)) &
|
||||
0x8080808080808080));
|
||||
0x8080808080808080));
|
||||
}
|
||||
|
||||
|
||||
#ifdef FASTFLOAT_HAS_SIMD
|
||||
|
||||
// Call this if chars might not be 8 digits.
|
||||
// Using this style (instead of is_made_of_eight_digits_fast() then parse_eight_digits_unrolled())
|
||||
// ensures we don't load SIMD registers twice.
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
bool simd_parse_if_eight_digits_unrolled(const char16_t* chars, uint64_t& i) noexcept {
|
||||
// Using this style (instead of is_made_of_eight_digits_fast() then
|
||||
// parse_eight_digits_unrolled()) ensures we don't load SIMD registers twice.
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
|
||||
simd_parse_if_eight_digits_unrolled(const char16_t *chars,
|
||||
uint64_t &i) noexcept {
|
||||
if (cpp20_and_in_constexpr()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#ifdef FASTFLOAT_SSE2
|
||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
const __m128i data = _mm_loadu_si128(reinterpret_cast<const __m128i*>(chars));
|
||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
const __m128i data =
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i *>(chars));
|
||||
|
||||
// (x - '0') <= 9
|
||||
// http://0x80.pl/articles/simd-parsing-int-sequences.html
|
||||
@ -176,13 +168,13 @@ FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
if (_mm_movemask_epi8(t1) == 0) {
|
||||
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||
} else
|
||||
return false;
|
||||
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||
#elif defined(FASTFLOAT_NEON)
|
||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
const uint16x8_t data = vld1q_u16(reinterpret_cast<const uint16_t*>(chars));
|
||||
|
||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
const uint16x8_t data = vld1q_u16(reinterpret_cast<const uint16_t *>(chars));
|
||||
|
||||
// (x - '0') <= 9
|
||||
// http://0x80.pl/articles/simd-parsing-int-sequences.html
|
||||
const uint16x8_t t0 = vsubq_u16(data, vmovq_n_u16('0'));
|
||||
@ -191,11 +183,12 @@ FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
if (vminvq_u16(mask) == 0xFFFF) {
|
||||
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||
} else
|
||||
return false;
|
||||
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||
#else
|
||||
(void)chars; (void)i;
|
||||
(void)chars;
|
||||
(void)i;
|
||||
return false;
|
||||
#endif // FASTFLOAT_SSE2
|
||||
}
|
||||
@ -209,27 +202,32 @@ template <typename UC>
|
||||
template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
|
||||
#endif
|
||||
// dummy for compile
|
||||
bool simd_parse_if_eight_digits_unrolled(UC const*, uint64_t&) {
|
||||
bool simd_parse_if_eight_digits_unrolled(UC const *, uint64_t &) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
template <typename UC, FASTFLOAT_ENABLE_IF(!std::is_same<UC, char>::value) = 0>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
void loop_parse_if_eight_digits(const UC*& p, const UC* const pend, uint64_t& i) {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
||||
loop_parse_if_eight_digits(const UC *&p, const UC *const pend, uint64_t &i) {
|
||||
if (!has_simd_opt<UC>()) {
|
||||
return;
|
||||
}
|
||||
while ((std::distance(p, pend) >= 8) && simd_parse_if_eight_digits_unrolled(p, i)) { // in rare cases, this will overflow, but that's ok
|
||||
while ((std::distance(p, pend) >= 8) &&
|
||||
simd_parse_if_eight_digits_unrolled(
|
||||
p, i)) { // in rare cases, this will overflow, but that's ok
|
||||
p += 8;
|
||||
}
|
||||
}
|
||||
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
void loop_parse_if_eight_digits(const char*& p, const char* const pend, uint64_t& i) {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
||||
loop_parse_if_eight_digits(const char *&p, const char *const pend,
|
||||
uint64_t &i) {
|
||||
// optimizes better than parse_if_eight_digits_unrolled() for UC = char.
|
||||
while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(read8_to_u64(p))) {
|
||||
i = i * 100000000 + parse_eight_digits_unrolled(read8_to_u64(p)); // in rare cases, this will overflow, but that's ok
|
||||
while ((std::distance(p, pend) >= 8) &&
|
||||
is_made_of_eight_digits_fast(read8_to_u64(p))) {
|
||||
i = i * 100000000 +
|
||||
parse_eight_digits_unrolled(read8_to_u64(
|
||||
p)); // in rare cases, this will overflow, but that's ok
|
||||
p += 8;
|
||||
}
|
||||
}
|
||||
@ -253,11 +251,10 @@ enum class parse_error {
|
||||
missing_exponential_part,
|
||||
};
|
||||
|
||||
template <typename UC>
|
||||
struct parsed_number_string_t {
|
||||
template <typename UC> struct parsed_number_string_t {
|
||||
int64_t exponent{0};
|
||||
uint64_t mantissa{0};
|
||||
UC const * lastmatch{nullptr};
|
||||
UC const *lastmatch{nullptr};
|
||||
bool negative{false};
|
||||
bool valid{false};
|
||||
bool too_many_digits{false};
|
||||
@ -272,7 +269,7 @@ using parsed_number_string = parsed_number_string_t<char>;
|
||||
|
||||
template <typename UC>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
|
||||
report_parse_error(UC const* p, parse_error error) {
|
||||
report_parse_error(UC const *p, parse_error error) {
|
||||
parsed_number_string_t<UC> answer;
|
||||
answer.valid = false;
|
||||
answer.lastmatch = p;
|
||||
@ -283,8 +280,9 @@ report_parse_error(UC const* p, parse_error error) {
|
||||
// Assuming that you use no more than 19 digits, this will
|
||||
// parse an ASCII string.
|
||||
template <typename UC>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, parse_options_t<UC> options) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
|
||||
parse_number_string(UC const *p, UC const *pend,
|
||||
parse_options_t<UC> options) noexcept {
|
||||
chars_format const fmt = options.format;
|
||||
UC const decimal_point = options.decimal_point;
|
||||
|
||||
@ -299,20 +297,24 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
|
||||
#endif
|
||||
++p;
|
||||
if (p == pend) {
|
||||
return report_parse_error<UC>(
|
||||
p, parse_error::missing_integer_or_dot_after_sign);
|
||||
return report_parse_error<UC>(
|
||||
p, parse_error::missing_integer_or_dot_after_sign);
|
||||
}
|
||||
if (fmt & FASTFLOAT_JSONFMT) {
|
||||
if (!is_integer(*p)) { // a sign must be followed by an integer
|
||||
return report_parse_error<UC>(p, parse_error::missing_integer_after_sign);
|
||||
}
|
||||
return report_parse_error<UC>(p,
|
||||
parse_error::missing_integer_after_sign);
|
||||
}
|
||||
} else {
|
||||
if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot
|
||||
return report_parse_error<UC>(p, parse_error::missing_integer_or_dot_after_sign);
|
||||
if (!is_integer(*p) &&
|
||||
(*p !=
|
||||
decimal_point)) { // a sign must be followed by an integer or the dot
|
||||
return report_parse_error<UC>(
|
||||
p, parse_error::missing_integer_or_dot_after_sign);
|
||||
}
|
||||
}
|
||||
}
|
||||
UC const * const start_digits = p;
|
||||
UC const *const start_digits = p;
|
||||
|
||||
uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
|
||||
|
||||
@ -320,10 +322,11 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
|
||||
// a multiplication by 10 is cheaper than an arbitrary integer
|
||||
// multiplication
|
||||
i = 10 * i +
|
||||
uint64_t(*p - UC('0')); // might overflow, we will handle the overflow later
|
||||
uint64_t(*p -
|
||||
UC('0')); // might overflow, we will handle the overflow later
|
||||
++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);
|
||||
answer.integer = span<const UC>(start_digits, size_t(digit_count));
|
||||
if (fmt & FASTFLOAT_JSONFMT) {
|
||||
@ -341,7 +344,7 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
|
||||
const bool has_decimal_point = (p != pend) && (*p == decimal_point);
|
||||
if (has_decimal_point) {
|
||||
++p;
|
||||
UC const * before = p;
|
||||
UC const *before = p;
|
||||
// can occur at most twice without overflowing, but let it occur more, since
|
||||
// for integers with many digits, digit parsing is the primary bottleneck.
|
||||
loop_parse_if_eight_digits(p, pend, i);
|
||||
@ -358,32 +361,35 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
|
||||
if (fmt & FASTFLOAT_JSONFMT) {
|
||||
// at least 1 digit in fractional part
|
||||
if (has_decimal_point && exponent == 0) {
|
||||
return report_parse_error<UC>(p, parse_error::no_digits_in_fractional_part);
|
||||
return report_parse_error<UC>(p,
|
||||
parse_error::no_digits_in_fractional_part);
|
||||
}
|
||||
} else if (digit_count == 0) { // we must have encountered at least one integer!
|
||||
} else if (digit_count ==
|
||||
0) { // we must have encountered at least one integer!
|
||||
return report_parse_error<UC>(p, parse_error::no_digits_in_mantissa);
|
||||
}
|
||||
int64_t exp_number = 0; // explicit exponential part
|
||||
if ( ((fmt & chars_format::scientific) &&
|
||||
(p != pend) &&
|
||||
((UC('e') == *p) || (UC('E') == *p)))
|
||||
||
|
||||
((fmt & FASTFLOAT_FORTRANFMT) &&
|
||||
(p != pend) &&
|
||||
((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) || (UC('D') == *p)))) {
|
||||
UC const * location_of_e = p;
|
||||
if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) || (UC('D') == *p)) {
|
||||
int64_t exp_number = 0; // explicit exponential part
|
||||
if (((fmt & chars_format::scientific) && (p != pend) &&
|
||||
((UC('e') == *p) || (UC('E') == *p))) ||
|
||||
((fmt & FASTFLOAT_FORTRANFMT) && (p != pend) &&
|
||||
((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) ||
|
||||
(UC('D') == *p)))) {
|
||||
UC const *location_of_e = p;
|
||||
if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) ||
|
||||
(UC('D') == *p)) {
|
||||
++p;
|
||||
}
|
||||
bool neg_exp = false;
|
||||
if ((p != pend) && (UC('-') == *p)) {
|
||||
neg_exp = true;
|
||||
++p;
|
||||
} else if ((p != pend) && (UC('+') == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
|
||||
} else if ((p != pend) &&
|
||||
(UC('+') ==
|
||||
*p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
|
||||
++p;
|
||||
}
|
||||
if ((p == pend) || !is_integer(*p)) {
|
||||
if(!(fmt & chars_format::fixed)) {
|
||||
if (!(fmt & chars_format::fixed)) {
|
||||
// The exponential part is invalid for scientific notation, so it must
|
||||
// be a trailing token for fixed notation. However, fixed notation is
|
||||
// disabled, so report a scientific notation error.
|
||||
@ -399,7 +405,9 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
|
||||
}
|
||||
++p;
|
||||
}
|
||||
if(neg_exp) { exp_number = - exp_number; }
|
||||
if (neg_exp) {
|
||||
exp_number = -exp_number;
|
||||
}
|
||||
exponent += exp_number;
|
||||
}
|
||||
} else {
|
||||
@ -421,9 +429,11 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
|
||||
// We have to handle the case where we have 0.0000somenumber.
|
||||
// We need to be mindful of the case where we only have zeroes...
|
||||
// E.g., 0.000000000...000.
|
||||
UC const * start = start_digits;
|
||||
UC const *start = start_digits;
|
||||
while ((start != pend) && (*start == UC('0') || *start == decimal_point)) {
|
||||
if(*start == UC('0')) { digit_count --; }
|
||||
if (*start == UC('0')) {
|
||||
digit_count--;
|
||||
}
|
||||
start++;
|
||||
}
|
||||
|
||||
@ -434,18 +444,17 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
|
||||
// pre-tokenized spans from above.
|
||||
i = 0;
|
||||
p = answer.integer.ptr;
|
||||
UC const* int_end = p + answer.integer.len();
|
||||
const uint64_t minimal_nineteen_digit_integer{ 1000000000000000000 };
|
||||
UC const *int_end = p + answer.integer.len();
|
||||
const uint64_t minimal_nineteen_digit_integer{1000000000000000000};
|
||||
while ((i < minimal_nineteen_digit_integer) && (p != int_end)) {
|
||||
i = i * 10 + uint64_t(*p - UC('0'));
|
||||
++p;
|
||||
}
|
||||
if (i >= minimal_nineteen_digit_integer) { // We have a big integers
|
||||
exponent = end_of_integer_part - p + exp_number;
|
||||
}
|
||||
else { // We have a value with a fractional component.
|
||||
} else { // We have a value with a fractional component.
|
||||
p = answer.fraction.ptr;
|
||||
UC const* frac_end = p + answer.fraction.len();
|
||||
UC const *frac_end = p + answer.fraction.len();
|
||||
while ((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
|
||||
i = i * 10 + uint64_t(*p - UC('0'));
|
||||
++p;
|
||||
@ -461,11 +470,11 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
|
||||
}
|
||||
|
||||
template <typename T, typename UC>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
from_chars_result_t<UC> parse_int_string(UC const* p, UC const* pend, T& value, int base) {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||
parse_int_string(UC const *p, UC const *pend, T &value, int base) {
|
||||
from_chars_result_t<UC> answer;
|
||||
|
||||
UC const* const first = p;
|
||||
|
||||
UC const *const first = p;
|
||||
|
||||
bool negative = (*p == UC('-'));
|
||||
if (!std::is_signed<T>::value && negative) {
|
||||
@ -481,15 +490,15 @@ from_chars_result_t<UC> parse_int_string(UC const* p, UC const* pend, T& value,
|
||||
++p;
|
||||
}
|
||||
|
||||
UC const* const start_num = p;
|
||||
UC const *const start_num = p;
|
||||
|
||||
while (p!= pend && *p == UC('0')) {
|
||||
++p;
|
||||
while (p != pend && *p == UC('0')) {
|
||||
++p;
|
||||
}
|
||||
|
||||
const bool has_leading_zeros = p > start_num;
|
||||
|
||||
UC const* const start_digits = p;
|
||||
UC const *const start_digits = p;
|
||||
|
||||
uint64_t i = 0;
|
||||
if (base == 10) {
|
||||
@ -501,9 +510,9 @@ from_chars_result_t<UC> parse_int_string(UC const* p, UC const* pend, T& value,
|
||||
break;
|
||||
}
|
||||
i = uint64_t(base) * i + digit; // might overflow, check this later
|
||||
p++;
|
||||
p++;
|
||||
}
|
||||
|
||||
|
||||
size_t digit_count = size_t(p - start_digits);
|
||||
|
||||
if (digit_count == 0) {
|
||||
@ -511,12 +520,11 @@ from_chars_result_t<UC> parse_int_string(UC const* p, UC const* pend, T& value,
|
||||
value = 0;
|
||||
answer.ec = std::errc();
|
||||
answer.ptr = p;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
answer.ec = std::errc::invalid_argument;
|
||||
answer.ptr = first;
|
||||
}
|
||||
return answer;
|
||||
return answer;
|
||||
}
|
||||
|
||||
answer.ptr = p;
|
||||
@ -527,7 +535,8 @@ from_chars_result_t<UC> parse_int_string(UC const* p, UC const* pend, T& value,
|
||||
answer.ec = std::errc::result_out_of_range;
|
||||
return answer;
|
||||
}
|
||||
// this check can be eliminated for all other types, but they will all require a max_digits(base) equivalent
|
||||
// this check can be eliminated for all other types, but they will all require
|
||||
// a max_digits(base) equivalent
|
||||
if (digit_count == max_digits && i < min_safe_u64(base)) {
|
||||
answer.ec = std::errc::result_out_of_range;
|
||||
return answer;
|
||||
@ -544,18 +553,22 @@ from_chars_result_t<UC> parse_int_string(UC const* p, UC const* pend, T& value,
|
||||
if (negative) {
|
||||
#ifdef FASTFLOAT_VISUAL_STUDIO
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4146)
|
||||
#pragma warning(disable : 4146)
|
||||
#endif
|
||||
// this weird workaround is required because:
|
||||
// - converting unsigned to signed when its value is greater than signed max is UB pre-C++23.
|
||||
// - converting unsigned to signed when its value is greater than signed max
|
||||
// is UB pre-C++23.
|
||||
// - reinterpret_casting (~i + 1) would work, but it is not constexpr
|
||||
// this is always optimized into a neg instruction (note: T is an integer type)
|
||||
value = T(-std::numeric_limits<T>::max() - T(i - uint64_t(std::numeric_limits<T>::max())));
|
||||
// this is always optimized into a neg instruction (note: T is an integer
|
||||
// type)
|
||||
value = T(-std::numeric_limits<T>::max() -
|
||||
T(i - uint64_t(std::numeric_limits<T>::max())));
|
||||
#ifdef FASTFLOAT_VISUAL_STUDIO
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
} else {
|
||||
value = T(i);
|
||||
}
|
||||
else { value = T(i); }
|
||||
|
||||
answer.ec = std::errc();
|
||||
return answer;
|
||||
|
||||
@ -37,8 +37,7 @@ constexpr size_t bigint_limbs = bigint_bits / limb_bits;
|
||||
|
||||
// vector-like type that is allocated on the stack. the entire
|
||||
// buffer is pre-allocated, and only the length changes.
|
||||
template <uint16_t size>
|
||||
struct stackvec {
|
||||
template <uint16_t size> struct stackvec {
|
||||
limb data[size];
|
||||
// we never need more than 150 limbs
|
||||
uint16_t length{0};
|
||||
@ -54,16 +53,16 @@ struct stackvec {
|
||||
FASTFLOAT_ASSERT(try_extend(s));
|
||||
}
|
||||
|
||||
FASTFLOAT_CONSTEXPR14 limb& operator[](size_t index) noexcept {
|
||||
FASTFLOAT_CONSTEXPR14 limb &operator[](size_t index) noexcept {
|
||||
FASTFLOAT_DEBUG_ASSERT(index < length);
|
||||
return data[index];
|
||||
}
|
||||
FASTFLOAT_CONSTEXPR14 const limb& operator[](size_t index) const noexcept {
|
||||
FASTFLOAT_CONSTEXPR14 const limb &operator[](size_t index) const noexcept {
|
||||
FASTFLOAT_DEBUG_ASSERT(index < length);
|
||||
return data[index];
|
||||
}
|
||||
// index from the end of the container
|
||||
FASTFLOAT_CONSTEXPR14 const limb& rindex(size_t index) const noexcept {
|
||||
FASTFLOAT_CONSTEXPR14 const limb &rindex(size_t index) const noexcept {
|
||||
FASTFLOAT_DEBUG_ASSERT(index < length);
|
||||
size_t rindex = length - index - 1;
|
||||
return data[rindex];
|
||||
@ -73,15 +72,9 @@ struct stackvec {
|
||||
FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept {
|
||||
length = uint16_t(len);
|
||||
}
|
||||
constexpr size_t len() const noexcept {
|
||||
return length;
|
||||
}
|
||||
constexpr bool is_empty() const noexcept {
|
||||
return length == 0;
|
||||
}
|
||||
constexpr size_t capacity() const noexcept {
|
||||
return size;
|
||||
}
|
||||
constexpr size_t len() const noexcept { return length; }
|
||||
constexpr bool is_empty() const noexcept { return length == 0; }
|
||||
constexpr size_t capacity() const noexcept { return size; }
|
||||
// append item to vector, without bounds checking
|
||||
FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept {
|
||||
data[length] = value;
|
||||
@ -98,7 +91,7 @@ struct stackvec {
|
||||
}
|
||||
// add items to the vector, from a span, without bounds checking
|
||||
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);
|
||||
set_len(len() + s.len());
|
||||
}
|
||||
@ -118,8 +111,8 @@ struct stackvec {
|
||||
void resize_unchecked(size_t new_len, limb value) noexcept {
|
||||
if (new_len > len()) {
|
||||
size_t count = new_len - len();
|
||||
limb* first = data + len();
|
||||
limb* last = first + count;
|
||||
limb *first = data + len();
|
||||
limb *last = first + count;
|
||||
::std::fill(first, last, value);
|
||||
set_len(new_len);
|
||||
} else {
|
||||
@ -155,21 +148,21 @@ struct stackvec {
|
||||
}
|
||||
};
|
||||
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||
uint64_t empty_hi64(bool& truncated) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t
|
||||
empty_hi64(bool &truncated) noexcept {
|
||||
truncated = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
uint64_t uint64_hi64(uint64_t r0, bool& truncated) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
|
||||
uint64_hi64(uint64_t r0, bool &truncated) noexcept {
|
||||
truncated = false;
|
||||
int shl = leading_zeroes(r0);
|
||||
return r0 << shl;
|
||||
}
|
||||
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
uint64_t uint64_hi64(uint64_t r0, uint64_t r1, bool& truncated) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
|
||||
uint64_hi64(uint64_t r0, uint64_t r1, bool &truncated) noexcept {
|
||||
int shl = leading_zeroes(r0);
|
||||
if (shl == 0) {
|
||||
truncated = r1 != 0;
|
||||
@ -181,20 +174,20 @@ uint64_t uint64_hi64(uint64_t r0, uint64_t r1, bool& truncated) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
uint64_t uint32_hi64(uint32_t r0, bool& truncated) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
|
||||
uint32_hi64(uint32_t r0, bool &truncated) noexcept {
|
||||
return uint64_hi64(r0, truncated);
|
||||
}
|
||||
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
uint64_t uint32_hi64(uint32_t r0, uint32_t r1, bool& truncated) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
|
||||
uint32_hi64(uint32_t r0, uint32_t r1, bool &truncated) noexcept {
|
||||
uint64_t x0 = r0;
|
||||
uint64_t x1 = r1;
|
||||
return uint64_hi64((x0 << 32) | x1, truncated);
|
||||
}
|
||||
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
uint64_t uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool& truncated) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
|
||||
uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool &truncated) noexcept {
|
||||
uint64_t x0 = r0;
|
||||
uint64_t x1 = r1;
|
||||
uint64_t x2 = r2;
|
||||
@ -205,17 +198,17 @@ uint64_t uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool& truncated) noe
|
||||
// we want an efficient operation. for msvc, where
|
||||
// we don't have built-in intrinsics, this is still
|
||||
// pretty fast.
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
limb scalar_add(limb x, limb y, bool& overflow) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb
|
||||
scalar_add(limb x, limb y, bool &overflow) noexcept {
|
||||
limb z;
|
||||
// gcc and clang
|
||||
#if defined(__has_builtin)
|
||||
#if __has_builtin(__builtin_add_overflow)
|
||||
if (!cpp20_and_in_constexpr()) {
|
||||
overflow = __builtin_add_overflow(x, y, &z);
|
||||
return z;
|
||||
}
|
||||
#endif
|
||||
#if __has_builtin(__builtin_add_overflow)
|
||||
if (!cpp20_and_in_constexpr()) {
|
||||
overflow = __builtin_add_overflow(x, y, &z);
|
||||
return z;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// generic, this still optimizes correctly on MSVC.
|
||||
@ -225,24 +218,24 @@ limb scalar_add(limb x, limb y, bool& overflow) noexcept {
|
||||
}
|
||||
|
||||
// multiply two small integers, getting both the high and low bits.
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
limb scalar_mul(limb x, limb y, limb& carry) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb
|
||||
scalar_mul(limb x, limb y, limb &carry) noexcept {
|
||||
#ifdef FASTFLOAT_64BIT_LIMB
|
||||
#if defined(__SIZEOF_INT128__)
|
||||
#if defined(__SIZEOF_INT128__)
|
||||
// GCC and clang both define it as an extension.
|
||||
__uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry);
|
||||
carry = limb(z >> limb_bits);
|
||||
return limb(z);
|
||||
#else
|
||||
#else
|
||||
// fallback, no native 128-bit integer multiplication with carry.
|
||||
// on msvc, this optimizes identically, somehow.
|
||||
value128 z = full_multiplication(x, y);
|
||||
bool overflow;
|
||||
z.low = scalar_add(z.low, carry, overflow);
|
||||
z.high += uint64_t(overflow); // cannot overflow
|
||||
z.high += uint64_t(overflow); // cannot overflow
|
||||
carry = z.high;
|
||||
return z.low;
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry);
|
||||
carry = limb(z >> limb_bits);
|
||||
@ -253,8 +246,8 @@ limb scalar_mul(limb x, limb y, limb& carry) noexcept {
|
||||
// add scalar value to bigint starting from offset.
|
||||
// used in grade school multiplication
|
||||
template <uint16_t size>
|
||||
inline FASTFLOAT_CONSTEXPR20
|
||||
bool small_add_from(stackvec<size>& vec, limb y, size_t start) noexcept {
|
||||
inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec<size> &vec, limb y,
|
||||
size_t start) noexcept {
|
||||
size_t index = start;
|
||||
limb carry = y;
|
||||
bool overflow;
|
||||
@ -271,15 +264,15 @@ bool small_add_from(stackvec<size>& vec, limb y, size_t start) noexcept {
|
||||
|
||||
// add scalar value to bigint.
|
||||
template <uint16_t size>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
bool small_add(stackvec<size>& vec, limb y) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
|
||||
small_add(stackvec<size> &vec, limb y) noexcept {
|
||||
return small_add_from(vec, y, 0);
|
||||
}
|
||||
|
||||
// multiply bigint by scalar value.
|
||||
template <uint16_t size>
|
||||
inline FASTFLOAT_CONSTEXPR20
|
||||
bool small_mul(stackvec<size>& vec, limb y) noexcept {
|
||||
inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec<size> &vec,
|
||||
limb y) noexcept {
|
||||
limb carry = 0;
|
||||
for (size_t index = 0; index < vec.len(); index++) {
|
||||
vec[index] = scalar_mul(vec[index], y, carry);
|
||||
@ -293,12 +286,12 @@ bool small_mul(stackvec<size>& vec, limb y) noexcept {
|
||||
// add bigint to bigint starting from index.
|
||||
// used in grade school multiplication
|
||||
template <uint16_t size>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
bool large_add_from(stackvec<size>& x, limb_span y, size_t start) noexcept {
|
||||
FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec<size> &x, limb_span y,
|
||||
size_t start) noexcept {
|
||||
// the effective x buffer is from `xstart..x.len()`, so exit early
|
||||
// if we can't get that current range.
|
||||
if (x.len() < start || y.len() > x.len() - start) {
|
||||
FASTFLOAT_TRY(x.try_resize(y.len() + start, 0));
|
||||
FASTFLOAT_TRY(x.try_resize(y.len() + start, 0));
|
||||
}
|
||||
|
||||
bool carry = false;
|
||||
@ -324,15 +317,14 @@ bool large_add_from(stackvec<size>& x, limb_span y, size_t start) noexcept {
|
||||
|
||||
// add bigint to bigint.
|
||||
template <uint16_t size>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
bool large_add_from(stackvec<size>& x, limb_span y) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
|
||||
large_add_from(stackvec<size> &x, limb_span y) noexcept {
|
||||
return large_add_from(x, y, 0);
|
||||
}
|
||||
|
||||
// grade-school multiplication algorithm
|
||||
template <uint16_t size>
|
||||
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());
|
||||
stackvec<size> z(xs);
|
||||
limb_span zs = limb_span(z.data, z.len());
|
||||
@ -360,8 +352,7 @@ bool long_mul(stackvec<size>& x, limb_span y) noexcept {
|
||||
|
||||
// grade-school multiplication algorithm
|
||||
template <uint16_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) {
|
||||
FASTFLOAT_TRY(small_mul(x, y[0]));
|
||||
} else {
|
||||
@ -370,36 +361,54 @@ bool large_mul(stackvec<size>& x, limb_span y) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename = void>
|
||||
struct pow5_tables {
|
||||
template <typename = void> struct pow5_tables {
|
||||
static constexpr uint32_t large_step = 135;
|
||||
static constexpr uint64_t small_power_of_5[] = {
|
||||
1UL, 5UL, 25UL, 125UL, 625UL, 3125UL, 15625UL, 78125UL, 390625UL,
|
||||
1953125UL, 9765625UL, 48828125UL, 244140625UL, 1220703125UL,
|
||||
6103515625UL, 30517578125UL, 152587890625UL, 762939453125UL,
|
||||
3814697265625UL, 19073486328125UL, 95367431640625UL, 476837158203125UL,
|
||||
2384185791015625UL, 11920928955078125UL, 59604644775390625UL,
|
||||
298023223876953125UL, 1490116119384765625UL, 7450580596923828125UL,
|
||||
1UL,
|
||||
5UL,
|
||||
25UL,
|
||||
125UL,
|
||||
625UL,
|
||||
3125UL,
|
||||
15625UL,
|
||||
78125UL,
|
||||
390625UL,
|
||||
1953125UL,
|
||||
9765625UL,
|
||||
48828125UL,
|
||||
244140625UL,
|
||||
1220703125UL,
|
||||
6103515625UL,
|
||||
30517578125UL,
|
||||
152587890625UL,
|
||||
762939453125UL,
|
||||
3814697265625UL,
|
||||
19073486328125UL,
|
||||
95367431640625UL,
|
||||
476837158203125UL,
|
||||
2384185791015625UL,
|
||||
11920928955078125UL,
|
||||
59604644775390625UL,
|
||||
298023223876953125UL,
|
||||
1490116119384765625UL,
|
||||
7450580596923828125UL,
|
||||
};
|
||||
#ifdef FASTFLOAT_64BIT_LIMB
|
||||
constexpr static limb large_power_of_5[] = {
|
||||
1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL,
|
||||
10482974169319127550UL, 198276706040285095UL};
|
||||
1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL,
|
||||
10482974169319127550UL, 198276706040285095UL};
|
||||
#else
|
||||
constexpr static limb large_power_of_5[] = {
|
||||
4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U,
|
||||
1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U};
|
||||
4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U,
|
||||
1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U};
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr uint32_t pow5_tables<T>::large_step;
|
||||
template <typename T> constexpr uint32_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[];
|
||||
|
||||
template <typename T>
|
||||
constexpr limb pow5_tables<T>::large_power_of_5[];
|
||||
template <typename T> constexpr limb pow5_tables<T>::large_power_of_5[];
|
||||
|
||||
// big integer type. implements a small subset of big integer
|
||||
// arithmetic, using simple algorithms since asymptotically
|
||||
@ -409,13 +418,13 @@ struct bigint : pow5_tables<> {
|
||||
// storage of the limbs, in little-endian order.
|
||||
stackvec<bigint_limbs> vec;
|
||||
|
||||
FASTFLOAT_CONSTEXPR20 bigint(): vec() {}
|
||||
FASTFLOAT_CONSTEXPR20 bigint() : vec() {}
|
||||
bigint(const bigint &) = delete;
|
||||
bigint &operator=(const bigint &) = delete;
|
||||
bigint(bigint &&) = delete;
|
||||
bigint &operator=(bigint &&other) = delete;
|
||||
|
||||
FASTFLOAT_CONSTEXPR20 bigint(uint64_t value): vec() {
|
||||
FASTFLOAT_CONSTEXPR20 bigint(uint64_t value) : vec() {
|
||||
#ifdef FASTFLOAT_64BIT_LIMB
|
||||
vec.push_unchecked(value);
|
||||
#else
|
||||
@ -427,7 +436,7 @@ struct bigint : pow5_tables<> {
|
||||
|
||||
// get the high 64 bits from the vector, and if bits were truncated.
|
||||
// this is to get the significant digits for the float.
|
||||
FASTFLOAT_CONSTEXPR20 uint64_t hi64(bool& truncated) const noexcept {
|
||||
FASTFLOAT_CONSTEXPR20 uint64_t hi64(bool &truncated) const noexcept {
|
||||
#ifdef FASTFLOAT_64BIT_LIMB
|
||||
if (vec.len() == 0) {
|
||||
return empty_hi64(truncated);
|
||||
@ -446,7 +455,8 @@ struct bigint : pow5_tables<> {
|
||||
} else if (vec.len() == 2) {
|
||||
return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated);
|
||||
} else {
|
||||
uint64_t result = uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated);
|
||||
uint64_t result =
|
||||
uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated);
|
||||
truncated |= vec.nonzero(3);
|
||||
return result;
|
||||
}
|
||||
@ -459,7 +469,7 @@ struct bigint : pow5_tables<> {
|
||||
// positive, this is larger, otherwise they are equal.
|
||||
// the limbs are stored in little-endian order, so we
|
||||
// must compare the limbs in ever order.
|
||||
FASTFLOAT_CONSTEXPR20 int compare(const bigint& other) const noexcept {
|
||||
FASTFLOAT_CONSTEXPR20 int compare(const bigint &other) const noexcept {
|
||||
if (vec.len() > other.vec.len()) {
|
||||
return 1;
|
||||
} else if (vec.len() < other.vec.len()) {
|
||||
@ -512,12 +522,12 @@ struct bigint : pow5_tables<> {
|
||||
return false;
|
||||
} else if (!vec.is_empty()) {
|
||||
// move limbs
|
||||
limb* dst = vec.data + n;
|
||||
const limb* src = vec.data;
|
||||
limb *dst = vec.data + n;
|
||||
const limb *src = vec.data;
|
||||
std::copy_backward(src, src + vec.len(), dst + vec.len());
|
||||
// fill in empty limbs
|
||||
limb* first = vec.data;
|
||||
limb* last = first + n;
|
||||
limb *first = vec.data;
|
||||
limb *last = first + n;
|
||||
::std::fill(first, last, 0);
|
||||
vec.set_len(n + vec.len());
|
||||
return true;
|
||||
@ -560,18 +570,12 @@ struct bigint : pow5_tables<> {
|
||||
return int(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); }
|
||||
|
||||
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.
|
||||
FASTFLOAT_CONSTEXPR20 bool pow2(uint32_t exp) noexcept {
|
||||
return shl(exp);
|
||||
}
|
||||
FASTFLOAT_CONSTEXPR20 bool pow2(uint32_t exp) noexcept { return shl(exp); }
|
||||
|
||||
// multiply as if by 5 raised to a power.
|
||||
FASTFLOAT_CONSTEXPR20 bool pow5(uint32_t exp) noexcept {
|
||||
@ -597,9 +601,8 @@ struct bigint : pow5_tables<> {
|
||||
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
||||
// This is similar to https://github.com/llvm/llvm-project/issues/47746,
|
||||
// except the workaround described there don't work here
|
||||
FASTFLOAT_TRY(
|
||||
small_mul(vec, limb(((void)small_power_of_5[0], small_power_of_5[exp])))
|
||||
);
|
||||
FASTFLOAT_TRY(small_mul(
|
||||
vec, limb(((void)small_power_of_5[0], small_power_of_5[exp]))));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@ -20,16 +20,16 @@
|
||||
#define FASTFLOAT_HAS_BIT_CAST 0
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_lib_is_constant_evaluated) && __cpp_lib_is_constant_evaluated >= 201811L
|
||||
#if defined(__cpp_lib_is_constant_evaluated) && \
|
||||
__cpp_lib_is_constant_evaluated >= 201811L
|
||||
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1
|
||||
#else
|
||||
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0
|
||||
#endif
|
||||
|
||||
// Testing for relevant C++20 constexpr library features
|
||||
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED \
|
||||
&& FASTFLOAT_HAS_BIT_CAST \
|
||||
&& __cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/
|
||||
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED && FASTFLOAT_HAS_BIT_CAST && \
|
||||
__cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/
|
||||
#define FASTFLOAT_CONSTEXPR20 constexpr
|
||||
#define FASTFLOAT_IS_CONSTEXPR 1
|
||||
#else
|
||||
|
||||
@ -12,27 +12,34 @@
|
||||
|
||||
namespace fast_float {
|
||||
|
||||
// This will compute or rather approximate w * 5**q and return a pair of 64-bit words approximating
|
||||
// the result, with the "high" part corresponding to the most significant bits and the
|
||||
// low part corresponding to the least significant bits.
|
||||
// This will compute or rather approximate w * 5**q and return a pair of 64-bit
|
||||
// words approximating the result, with the "high" part corresponding to the
|
||||
// most significant bits and the low part corresponding to the least significant
|
||||
// bits.
|
||||
//
|
||||
template <int bit_precision>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
value128 compute_product_approximation(int64_t q, uint64_t w) {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128
|
||||
compute_product_approximation(int64_t q, uint64_t w) {
|
||||
const int index = 2 * int(q - powers::smallest_power_of_five);
|
||||
// For small values of q, e.g., q in [0,27], the answer is always exact because
|
||||
// The line value128 firstproduct = full_multiplication(w, power_of_five_128[index]);
|
||||
// gives the exact answer.
|
||||
value128 firstproduct = full_multiplication(w, powers::power_of_five_128[index]);
|
||||
static_assert((bit_precision >= 0) && (bit_precision <= 64), " precision should be in (0,64]");
|
||||
constexpr uint64_t precision_mask = (bit_precision < 64) ?
|
||||
(uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision)
|
||||
: uint64_t(0xFFFFFFFFFFFFFFFF);
|
||||
if((firstproduct.high & precision_mask) == precision_mask) { // could further guard with (lower + w < lower)
|
||||
// regarding the second product, we only need secondproduct.high, but our expectation is that the compiler will optimize this extra work away if needed.
|
||||
value128 secondproduct = full_multiplication(w, powers::power_of_five_128[index + 1]);
|
||||
// For small values of q, e.g., q in [0,27], the answer is always exact
|
||||
// because The line value128 firstproduct = full_multiplication(w,
|
||||
// power_of_five_128[index]); gives the exact answer.
|
||||
value128 firstproduct =
|
||||
full_multiplication(w, powers::power_of_five_128[index]);
|
||||
static_assert((bit_precision >= 0) && (bit_precision <= 64),
|
||||
" precision should be in (0,64]");
|
||||
constexpr uint64_t precision_mask =
|
||||
(bit_precision < 64) ? (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision)
|
||||
: uint64_t(0xFFFFFFFFFFFFFFFF);
|
||||
if ((firstproduct.high & precision_mask) ==
|
||||
precision_mask) { // could further guard with (lower + w < lower)
|
||||
// regarding the second product, we only need secondproduct.high, but our
|
||||
// expectation is that the compiler will optimize this extra work away if
|
||||
// needed.
|
||||
value128 secondproduct =
|
||||
full_multiplication(w, powers::power_of_five_128[index + 1]);
|
||||
firstproduct.low += secondproduct.high;
|
||||
if(secondproduct.high > firstproduct.low) {
|
||||
if (secondproduct.high > firstproduct.low) {
|
||||
firstproduct.high++;
|
||||
}
|
||||
}
|
||||
@ -55,43 +62,45 @@ namespace detail {
|
||||
* where
|
||||
* p = log(5**-q)/log(2) = -q * log(5)/log(2)
|
||||
*/
|
||||
constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept {
|
||||
return (((152170 + 65536) * q) >> 16) + 63;
|
||||
}
|
||||
constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept {
|
||||
return (((152170 + 65536) * q) >> 16) + 63;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
// create an adjusted mantissa, biased by the invalid power2
|
||||
// for significant digits already multiplied by 10 ** q.
|
||||
template <typename binary>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||
adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 adjusted_mantissa
|
||||
compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
|
||||
int hilz = int(w >> 63) ^ 1;
|
||||
adjusted_mantissa answer;
|
||||
answer.mantissa = w << hilz;
|
||||
int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent();
|
||||
answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 + invalid_am_bias);
|
||||
answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 +
|
||||
invalid_am_bias);
|
||||
return answer;
|
||||
}
|
||||
|
||||
// w * 10 ** q, without rounding the representation up.
|
||||
// the power2 in the exponent will be adjusted by invalid_am_bias.
|
||||
template <typename binary>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
adjusted_mantissa compute_error(int64_t q, uint64_t w) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
|
||||
compute_error(int64_t q, uint64_t w) noexcept {
|
||||
int lz = leading_zeroes(w);
|
||||
w <<= lz;
|
||||
value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
|
||||
value128 product =
|
||||
compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
|
||||
return compute_error_scaled<binary>(q, product.high, lz);
|
||||
}
|
||||
|
||||
// w * 10 ** q
|
||||
// The returned value should be a valid ieee64 number that simply need to be packed.
|
||||
// However, in some very rare cases, the computation will fail. In such cases, we
|
||||
// return an adjusted_mantissa with a negative power of 2: the caller should recompute
|
||||
// in such cases.
|
||||
// The returned value should be a valid ieee64 number that simply need to be
|
||||
// packed. However, in some very rare cases, the computation will fail. In such
|
||||
// cases, we return an adjusted_mantissa with a negative power of 2: the caller
|
||||
// should recompute in such cases.
|
||||
template <typename binary>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
|
||||
compute_float(int64_t q, uint64_t w) noexcept {
|
||||
adjusted_mantissa answer;
|
||||
if ((w == 0) || (q < binary::smallest_power_of_ten())) {
|
||||
answer.power2 = 0;
|
||||
@ -105,7 +114,8 @@ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
|
||||
answer.mantissa = 0;
|
||||
return answer;
|
||||
}
|
||||
// At this point in time q is in [powers::smallest_power_of_five, powers::largest_power_of_five].
|
||||
// At this point in time q is in [powers::smallest_power_of_five,
|
||||
// powers::largest_power_of_five].
|
||||
|
||||
// We want the most significant bit of i to be 1. Shift if needed.
|
||||
int lz = leading_zeroes(w);
|
||||
@ -114,27 +124,32 @@ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
|
||||
// The required precision is binary::mantissa_explicit_bits() + 3 because
|
||||
// 1. We need the implicit bit
|
||||
// 2. We need an extra bit for rounding purposes
|
||||
// 3. We might lose a bit due to the "upperbit" routine (result too small, requiring a shift)
|
||||
// 3. We might lose a bit due to the "upperbit" routine (result too small,
|
||||
// requiring a shift)
|
||||
|
||||
value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
|
||||
value128 product =
|
||||
compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
|
||||
// The computed 'product' is always sufficient.
|
||||
// Mathematical proof:
|
||||
// Noble Mushtak and Daniel Lemire, Fast Number Parsing Without Fallback (to appear)
|
||||
// See script/mushtak_lemire.py
|
||||
// Noble Mushtak and Daniel Lemire, Fast Number Parsing Without Fallback (to
|
||||
// appear) See script/mushtak_lemire.py
|
||||
|
||||
// The "compute_product_approximation" function can be slightly slower than a branchless approach:
|
||||
// value128 product = compute_product(q, w);
|
||||
// but in practice, we can win big with the compute_product_approximation if its additional branch
|
||||
// is easily predicted. Which is best is data specific.
|
||||
// The "compute_product_approximation" function can be slightly slower than a
|
||||
// branchless approach: value128 product = compute_product(q, w); but in
|
||||
// practice, we can win big with the compute_product_approximation if its
|
||||
// additional branch is easily predicted. Which is best is data specific.
|
||||
int upperbit = int(product.high >> 63);
|
||||
int shift = upperbit + 64 - binary::mantissa_explicit_bits() - 3;
|
||||
|
||||
answer.mantissa = product.high >> shift;
|
||||
|
||||
answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz - binary::minimum_exponent());
|
||||
answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz -
|
||||
binary::minimum_exponent());
|
||||
if (answer.power2 <= 0) { // we have a subnormal?
|
||||
// Here have that answer.power2 <= 0 so -answer.power2 >= 0
|
||||
if(-answer.power2 + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
|
||||
if (-answer.power2 + 1 >=
|
||||
64) { // if we have more than 64 bits below the minimum exponent, you
|
||||
// have a zero for sure.
|
||||
answer.power2 = 0;
|
||||
answer.mantissa = 0;
|
||||
// result should be zero
|
||||
@ -153,20 +168,26 @@ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
|
||||
// up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer
|
||||
// subnormal, but we can only know this after rounding.
|
||||
// So we only declare a subnormal if we are smaller than the threshold.
|
||||
answer.power2 = (answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) ? 0 : 1;
|
||||
answer.power2 =
|
||||
(answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits()))
|
||||
? 0
|
||||
: 1;
|
||||
return answer;
|
||||
}
|
||||
|
||||
// usually, we round *up*, but if we fall right in between and and we have an
|
||||
// even basis, we need to round down
|
||||
// We are only concerned with the cases where 5**q fits in single 64-bit word.
|
||||
if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) && (q <= binary::max_exponent_round_to_even()) &&
|
||||
((answer.mantissa & 3) == 1) ) { // we may fall between two floats!
|
||||
if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) &&
|
||||
(q <= binary::max_exponent_round_to_even()) &&
|
||||
((answer.mantissa & 3) == 1)) { // we may fall between two floats!
|
||||
// To be in-between two floats we need that in doing
|
||||
// answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3);
|
||||
// ... we dropped out only zeroes. But if this happened, then we can go back!!!
|
||||
if((answer.mantissa << shift) == product.high) {
|
||||
answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up
|
||||
// answer.mantissa = product.high >> (upperbit + 64 -
|
||||
// binary::mantissa_explicit_bits() - 3);
|
||||
// ... we dropped out only zeroes. But if this happened, then we can go
|
||||
// back!!!
|
||||
if ((answer.mantissa << shift) == product.high) {
|
||||
answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -13,19 +13,34 @@
|
||||
namespace fast_float {
|
||||
|
||||
// 1e0 to 1e19
|
||||
constexpr static uint64_t powers_of_ten_uint64[] = {
|
||||
1UL, 10UL, 100UL, 1000UL, 10000UL, 100000UL, 1000000UL, 10000000UL, 100000000UL,
|
||||
1000000000UL, 10000000000UL, 100000000000UL, 1000000000000UL, 10000000000000UL,
|
||||
100000000000000UL, 1000000000000000UL, 10000000000000000UL, 100000000000000000UL,
|
||||
1000000000000000000UL, 10000000000000000000UL};
|
||||
constexpr static uint64_t powers_of_ten_uint64[] = {1UL,
|
||||
10UL,
|
||||
100UL,
|
||||
1000UL,
|
||||
10000UL,
|
||||
100000UL,
|
||||
1000000UL,
|
||||
10000000UL,
|
||||
100000000UL,
|
||||
1000000000UL,
|
||||
10000000000UL,
|
||||
100000000000UL,
|
||||
1000000000000UL,
|
||||
10000000000000UL,
|
||||
100000000000000UL,
|
||||
1000000000000000UL,
|
||||
10000000000000000UL,
|
||||
100000000000000000UL,
|
||||
1000000000000000000UL,
|
||||
10000000000000000000UL};
|
||||
|
||||
// calculate the exponent, in scientific notation, of the number.
|
||||
// this algorithm is not even close to optimized, but it has no practical
|
||||
// effect on performance: in order to have a faster algorithm, we'd need
|
||||
// to slow down performance for faster algorithms, and this is still fast.
|
||||
template <typename UC>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||
int32_t scientific_exponent(parsed_number_string_t<UC> & num) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int32_t
|
||||
scientific_exponent(parsed_number_string_t<UC> &num) noexcept {
|
||||
uint64_t mantissa = num.mantissa;
|
||||
int32_t exponent = int32_t(num.exponent);
|
||||
while (mantissa >= 10000) {
|
||||
@ -45,15 +60,16 @@ int32_t scientific_exponent(parsed_number_string_t<UC> & num) noexcept {
|
||||
|
||||
// this converts a native floating-point number to an extended-precision float.
|
||||
template <typename T>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
adjusted_mantissa to_extended(T value) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
|
||||
to_extended(T value) noexcept {
|
||||
using equiv_uint = typename binary_format<T>::equiv_uint;
|
||||
constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
|
||||
constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
|
||||
constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
|
||||
|
||||
adjusted_mantissa am;
|
||||
int32_t bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent();
|
||||
int32_t bias = binary_format<T>::mantissa_explicit_bits() -
|
||||
binary_format<T>::minimum_exponent();
|
||||
equiv_uint bits;
|
||||
#if FASTFLOAT_HAS_BIT_CAST
|
||||
bits = std::bit_cast<equiv_uint>(value);
|
||||
@ -66,7 +82,8 @@ adjusted_mantissa to_extended(T value) noexcept {
|
||||
am.mantissa = bits & mantissa_mask;
|
||||
} else {
|
||||
// normal
|
||||
am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits());
|
||||
am.power2 = int32_t((bits & exponent_mask) >>
|
||||
binary_format<T>::mantissa_explicit_bits());
|
||||
am.power2 -= bias;
|
||||
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
|
||||
}
|
||||
@ -78,8 +95,8 @@ adjusted_mantissa to_extended(T value) noexcept {
|
||||
// we are given a native float that represents b, so we need to adjust it
|
||||
// halfway between b and b+u.
|
||||
template <typename T>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
adjusted_mantissa to_extended_halfway(T value) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
|
||||
to_extended_halfway(T value) noexcept {
|
||||
adjusted_mantissa am = to_extended(value);
|
||||
am.mantissa <<= 1;
|
||||
am.mantissa += 1;
|
||||
@ -89,15 +106,18 @@ adjusted_mantissa to_extended_halfway(T value) noexcept {
|
||||
|
||||
// round an extended-precision float to the nearest machine float.
|
||||
template <typename T, typename callback>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||
void round(adjusted_mantissa& am, callback cb) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
|
||||
callback cb) noexcept {
|
||||
int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1;
|
||||
if (-am.power2 >= mantissa_shift) {
|
||||
// have a denormal float
|
||||
int32_t shift = -am.power2 + 1;
|
||||
cb(am, std::min<int32_t>(shift, 64));
|
||||
// check for round-up: if rounding-nearest carried us to the hidden bit.
|
||||
am.power2 = (am.mantissa < (uint64_t(1) << binary_format<T>::mantissa_explicit_bits())) ? 0 : 1;
|
||||
am.power2 = (am.mantissa <
|
||||
(uint64_t(1) << binary_format<T>::mantissa_explicit_bits()))
|
||||
? 0
|
||||
: 1;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -105,7 +125,8 @@ void round(adjusted_mantissa& am, callback cb) noexcept {
|
||||
cb(am, mantissa_shift);
|
||||
|
||||
// check for carry
|
||||
if (am.mantissa >= (uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) {
|
||||
if (am.mantissa >=
|
||||
(uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) {
|
||||
am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
|
||||
am.power2++;
|
||||
}
|
||||
@ -119,16 +140,11 @@ void round(adjusted_mantissa& am, callback cb) noexcept {
|
||||
}
|
||||
|
||||
template <typename callback>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||
void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) noexcept {
|
||||
const uint64_t mask
|
||||
= (shift == 64)
|
||||
? UINT64_MAX
|
||||
: (uint64_t(1) << shift) - 1;
|
||||
const uint64_t halfway
|
||||
= (shift == 0)
|
||||
? 0
|
||||
: uint64_t(1) << (shift - 1);
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
|
||||
round_nearest_tie_even(adjusted_mantissa &am, int32_t shift,
|
||||
callback cb) noexcept {
|
||||
const uint64_t mask = (shift == 64) ? UINT64_MAX : (uint64_t(1) << shift) - 1;
|
||||
const uint64_t halfway = (shift == 0) ? 0 : uint64_t(1) << (shift - 1);
|
||||
uint64_t truncated_bits = am.mantissa & mask;
|
||||
bool is_above = truncated_bits > halfway;
|
||||
bool is_halfway = truncated_bits == halfway;
|
||||
@ -145,8 +161,8 @@ void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) n
|
||||
am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above));
|
||||
}
|
||||
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||
void round_down(adjusted_mantissa& am, int32_t shift) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
|
||||
round_down(adjusted_mantissa &am, int32_t shift) noexcept {
|
||||
if (shift == 64) {
|
||||
am.mantissa = 0;
|
||||
} else {
|
||||
@ -155,10 +171,11 @@ void round_down(adjusted_mantissa& am, int32_t shift) noexcept {
|
||||
am.power2 += shift;
|
||||
}
|
||||
template <typename UC>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
void skip_zeros(UC const * & first, UC const * last) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
||||
skip_zeros(UC const *&first, UC const *last) noexcept {
|
||||
uint64_t val;
|
||||
while (!cpp20_and_in_constexpr() && std::distance(first, last) >= int_cmp_len<UC>()) {
|
||||
while (!cpp20_and_in_constexpr() &&
|
||||
std::distance(first, last) >= int_cmp_len<UC>()) {
|
||||
::memcpy(&val, first, sizeof(uint64_t));
|
||||
if (val != int_cmp_zeros<UC>()) {
|
||||
break;
|
||||
@ -176,11 +193,12 @@ void skip_zeros(UC const * & first, UC const * last) noexcept {
|
||||
// determine if any non-zero digits were truncated.
|
||||
// all characters must be valid digits.
|
||||
template <typename UC>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
bool is_truncated(UC const * first, UC const * last) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
|
||||
is_truncated(UC const *first, UC const *last) noexcept {
|
||||
// do 8-bit optimizations, can just compare to 8 literal 0s.
|
||||
uint64_t val;
|
||||
while (!cpp20_and_in_constexpr() && std::distance(first, last) >= int_cmp_len<UC>()) {
|
||||
while (!cpp20_and_in_constexpr() &&
|
||||
std::distance(first, last) >= int_cmp_len<UC>()) {
|
||||
::memcpy(&val, first, sizeof(uint64_t));
|
||||
if (val != int_cmp_zeros<UC>()) {
|
||||
return true;
|
||||
@ -196,15 +214,15 @@ bool is_truncated(UC const * first, UC const * last) noexcept {
|
||||
return false;
|
||||
}
|
||||
template <typename UC>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
bool is_truncated(span<const UC> s) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
|
||||
is_truncated(span<const UC> s) noexcept {
|
||||
return is_truncated(s.ptr, s.ptr + s.len());
|
||||
}
|
||||
|
||||
|
||||
template <typename UC>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
void parse_eight_digits(const UC*& p, limb& value, size_t& counter, size_t& count) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
||||
parse_eight_digits(const UC *&p, limb &value, size_t &counter,
|
||||
size_t &count) noexcept {
|
||||
value = value * 100000000 + parse_eight_digits_unrolled(p);
|
||||
p += 8;
|
||||
counter += 8;
|
||||
@ -212,22 +230,23 @@ void parse_eight_digits(const UC*& p, limb& value, size_t& counter, size_t& coun
|
||||
}
|
||||
|
||||
template <typename UC>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||
void parse_one_digit(UC const *& p, limb& value, size_t& counter, size_t& count) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
|
||||
parse_one_digit(UC const *&p, limb &value, size_t &counter,
|
||||
size_t &count) noexcept {
|
||||
value = value * 10 + limb(*p - UC('0'));
|
||||
p++;
|
||||
counter++;
|
||||
count++;
|
||||
}
|
||||
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
void add_native(bigint& big, limb power, limb value) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
||||
add_native(bigint &big, limb power, limb value) noexcept {
|
||||
big.mul(power);
|
||||
big.add(value);
|
||||
}
|
||||
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
void round_up_bigint(bigint& big, size_t& count) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
||||
round_up_bigint(bigint &big, size_t &count) noexcept {
|
||||
// need to round-up the digits, but need to avoid rounding
|
||||
// ....9999 to ...10000, which could cause a false halfway point.
|
||||
add_native(big, 10, 1);
|
||||
@ -236,8 +255,9 @@ void round_up_bigint(bigint& big, size_t& count) noexcept {
|
||||
|
||||
// parse the significant digits into a big integer
|
||||
template <typename UC>
|
||||
inline FASTFLOAT_CONSTEXPR20
|
||||
void parse_mantissa(bigint& result, parsed_number_string_t<UC>& num, size_t max_digits, size_t& digits) noexcept {
|
||||
inline FASTFLOAT_CONSTEXPR20 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.
|
||||
// therefore, try to parse 8 digits at a time, and multiply by the largest
|
||||
// scalar value (9 or 19 digits) for each step.
|
||||
@ -251,12 +271,13 @@ void parse_mantissa(bigint& result, parsed_number_string_t<UC>& num, size_t max_
|
||||
#endif
|
||||
|
||||
// process all integer digits.
|
||||
UC const * p = num.integer.ptr;
|
||||
UC const * pend = p + num.integer.len();
|
||||
UC const *p = num.integer.ptr;
|
||||
UC const *pend = p + num.integer.len();
|
||||
skip_zeros(p, pend);
|
||||
// process all digits, in increments of step per loop
|
||||
while (p != pend) {
|
||||
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) {
|
||||
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) &&
|
||||
(max_digits - digits >= 8)) {
|
||||
parse_eight_digits(p, value, counter, digits);
|
||||
}
|
||||
while (counter < step && p != pend && digits < max_digits) {
|
||||
@ -289,7 +310,8 @@ void parse_mantissa(bigint& result, parsed_number_string_t<UC>& num, size_t max_
|
||||
}
|
||||
// process all digits, in increments of step per loop
|
||||
while (p != pend) {
|
||||
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) {
|
||||
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) &&
|
||||
(max_digits - digits >= 8)) {
|
||||
parse_eight_digits(p, value, counter, digits);
|
||||
}
|
||||
while (counter < step && p != pend && digits < max_digits) {
|
||||
@ -317,19 +339,23 @@ void parse_mantissa(bigint& result, parsed_number_string_t<UC>& num, size_t max_
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline FASTFLOAT_CONSTEXPR20
|
||||
adjusted_mantissa positive_digit_comp(bigint& bigmant, int32_t exponent) noexcept {
|
||||
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
|
||||
positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept {
|
||||
FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent)));
|
||||
adjusted_mantissa answer;
|
||||
bool truncated;
|
||||
answer.mantissa = bigmant.hi64(truncated);
|
||||
int bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent();
|
||||
int bias = binary_format<T>::mantissa_explicit_bits() -
|
||||
binary_format<T>::minimum_exponent();
|
||||
answer.power2 = bigmant.bit_length() - 64 + bias;
|
||||
|
||||
round<T>(answer, [truncated](adjusted_mantissa& a, int32_t shift) {
|
||||
round_nearest_tie_even(a, shift, [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool {
|
||||
return is_above || (is_halfway && truncated) || (is_odd && is_halfway);
|
||||
});
|
||||
round<T>(answer, [truncated](adjusted_mantissa &a, int32_t shift) {
|
||||
round_nearest_tie_even(
|
||||
a, shift,
|
||||
[truncated](bool is_odd, bool is_halfway, bool is_above) -> bool {
|
||||
return is_above || (is_halfway && truncated) ||
|
||||
(is_odd && is_halfway);
|
||||
});
|
||||
});
|
||||
|
||||
return answer;
|
||||
@ -341,15 +367,17 @@ adjusted_mantissa positive_digit_comp(bigint& bigmant, int32_t exponent) noexcep
|
||||
// we then need to scale by `2^(f- e)`, and then the two significant digits
|
||||
// are of the same magnitude.
|
||||
template <typename T>
|
||||
inline FASTFLOAT_CONSTEXPR20
|
||||
adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int32_t exponent) noexcept {
|
||||
bigint& real_digits = bigmant;
|
||||
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
|
||||
bigint &bigmant, adjusted_mantissa am, int32_t exponent) noexcept {
|
||||
bigint &real_digits = bigmant;
|
||||
int32_t real_exp = exponent;
|
||||
|
||||
// get the value of `b`, rounded down, and get a bigint representation of b+h
|
||||
adjusted_mantissa am_b = am;
|
||||
// gcc7 buf: use a lambda to remove the noexcept qualifier bug with -Wnoexcept-type.
|
||||
round<T>(am_b, [](adjusted_mantissa&a, int32_t shift) { round_down(a, shift); });
|
||||
// gcc7 buf: use a lambda to remove the noexcept qualifier bug with
|
||||
// -Wnoexcept-type.
|
||||
round<T>(am_b,
|
||||
[](adjusted_mantissa &a, int32_t shift) { round_down(a, shift); });
|
||||
T b;
|
||||
to_float(false, am_b, b);
|
||||
adjusted_mantissa theor = to_extended_halfway(b);
|
||||
@ -371,18 +399,19 @@ adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int
|
||||
// compare digits, and use it to director rounding
|
||||
int ord = real_digits.compare(theor_digits);
|
||||
adjusted_mantissa answer = am;
|
||||
round<T>(answer, [ord](adjusted_mantissa& a, int32_t shift) {
|
||||
round_nearest_tie_even(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
|
||||
if (ord > 0) {
|
||||
return true;
|
||||
} else if (ord < 0) {
|
||||
return false;
|
||||
} else {
|
||||
return is_odd;
|
||||
}
|
||||
});
|
||||
round<T>(answer, [ord](adjusted_mantissa &a, int32_t shift) {
|
||||
round_nearest_tie_even(
|
||||
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
|
||||
if (ord > 0) {
|
||||
return true;
|
||||
} else if (ord < 0) {
|
||||
return false;
|
||||
} else {
|
||||
return is_odd;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return answer;
|
||||
@ -402,8 +431,8 @@ adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int
|
||||
// the actual digits. we then compare the big integer representations
|
||||
// of both, and use that to direct rounding.
|
||||
template <typename T, typename UC>
|
||||
inline FASTFLOAT_CONSTEXPR20
|
||||
adjusted_mantissa digit_comp(parsed_number_string_t<UC>& num, adjusted_mantissa am) noexcept {
|
||||
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
|
||||
digit_comp(parsed_number_string_t<UC> &num, adjusted_mantissa am) noexcept {
|
||||
// remove the invalid exponent bias
|
||||
am.power2 -= invalid_am_bias;
|
||||
|
||||
|
||||
@ -6,42 +6,50 @@
|
||||
|
||||
namespace fast_float {
|
||||
/**
|
||||
* This function parses the character sequence [first,last) for a number. It parses floating-point numbers expecting
|
||||
* a locale-indepent format equivalent to what is used by std::strtod in the default ("C") locale.
|
||||
* The resulting floating-point value is the closest floating-point values (using either float or double),
|
||||
* using the "round to even" convention for values that would otherwise fall right in-between two values.
|
||||
* That is, we provide exact parsing according to the IEEE standard.
|
||||
* This function parses the character sequence [first,last) for a number. It
|
||||
* parses floating-point numbers expecting a locale-indepent format equivalent
|
||||
* to what is used by std::strtod in the default ("C") locale. The resulting
|
||||
* floating-point value is the closest floating-point values (using either float
|
||||
* or double), using the "round to even" convention for values that would
|
||||
* otherwise fall right in-between two values. That is, we provide exact parsing
|
||||
* according to the IEEE standard.
|
||||
*
|
||||
* Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the
|
||||
* parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned
|
||||
* `ec` contains a representative error, otherwise the default (`std::errc()`) value is stored.
|
||||
* Given a successful parse, the pointer (`ptr`) in the returned value is set to
|
||||
* point right after the parsed number, and the `value` referenced is set to the
|
||||
* parsed value. In case of error, the returned `ec` contains a representative
|
||||
* error, otherwise the default (`std::errc()`) value is stored.
|
||||
*
|
||||
* The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`).
|
||||
* The implementation does not throw and does not allocate memory (e.g., with
|
||||
* `new` or `malloc`).
|
||||
*
|
||||
* Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of
|
||||
* the type `fast_float::chars_format`. It is a bitset value: we check whether
|
||||
* `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set
|
||||
* 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`.
|
||||
* Like the C++17 standard, the `fast_float::from_chars` functions take an
|
||||
* optional last argument of the type `fast_float::chars_format`. It is a bitset
|
||||
* value: we check whether `fmt & fast_float::chars_format::fixed` and `fmt &
|
||||
* fast_float::chars_format::scientific` are set 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`.
|
||||
*/
|
||||
template<typename T, typename UC = char, typename = FASTFLOAT_ENABLE_IF(is_supported_float_type<T>())>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
from_chars_result_t<UC> from_chars(UC const * first, UC const * last,
|
||||
T &value, chars_format fmt = chars_format::general) noexcept;
|
||||
template <typename T, typename UC = char,
|
||||
typename = FASTFLOAT_ENABLE_IF(is_supported_float_type<T>())>
|
||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||
from_chars(UC const *first, UC const *last, T &value,
|
||||
chars_format fmt = chars_format::general) noexcept;
|
||||
|
||||
/**
|
||||
* Like from_chars, but accepts an `options` argument to govern number parsing.
|
||||
*/
|
||||
template<typename T, typename UC = char>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
|
||||
T &value, parse_options_t<UC> options) noexcept;
|
||||
template <typename T, typename UC = char>
|
||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||
from_chars_advanced(UC const *first, UC const *last, T &value,
|
||||
parse_options_t<UC> options) noexcept;
|
||||
/**
|
||||
* from_chars for integer types.
|
||||
*/
|
||||
template <typename T, typename UC = char, typename = FASTFLOAT_ENABLE_IF(!is_supported_float_type<T>())>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
from_chars_result_t<UC> from_chars(UC const * first, UC const * last, T& value, int base = 10) noexcept;
|
||||
* from_chars for integer types.
|
||||
*/
|
||||
template <typename T, typename UC = char,
|
||||
typename = FASTFLOAT_ENABLE_IF(!is_supported_float_type<T>())>
|
||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||
from_chars(UC const *first, UC const *last, T &value, int base = 10) noexcept;
|
||||
|
||||
} // namespace fast_float
|
||||
#include "parse_number.h"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -8,9 +8,9 @@
|
||||
#include <type_traits>
|
||||
#include <system_error>
|
||||
#ifdef __has_include
|
||||
#if __has_include(<stdfloat>) && (__cplusplus > 202002L || _MSVC_LANG > 202002L)
|
||||
#include <stdfloat>
|
||||
#endif
|
||||
#if __has_include(<stdfloat>) && (__cplusplus > 202002L || _MSVC_LANG > 202002L)
|
||||
#include <stdfloat>
|
||||
#endif
|
||||
#endif
|
||||
#include "constexpr_feature_detect.h"
|
||||
|
||||
@ -32,18 +32,16 @@ enum chars_format {
|
||||
general = fixed | scientific
|
||||
};
|
||||
|
||||
template <typename UC>
|
||||
struct from_chars_result_t {
|
||||
UC const* ptr;
|
||||
template <typename UC> struct from_chars_result_t {
|
||||
UC const *ptr;
|
||||
std::errc ec;
|
||||
};
|
||||
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,
|
||||
UC dot = UC('.'))
|
||||
: format(fmt), decimal_point(dot) {}
|
||||
UC dot = UC('.'))
|
||||
: format(fmt), decimal_point(dot) {}
|
||||
|
||||
/** Which number formats are accepted */
|
||||
chars_format format;
|
||||
@ -52,39 +50,40 @@ struct parse_options_t {
|
||||
};
|
||||
using parse_options = parse_options_t<char>;
|
||||
|
||||
}
|
||||
} // namespace fast_float
|
||||
|
||||
#if FASTFLOAT_HAS_BIT_CAST
|
||||
#include <bit>
|
||||
#endif
|
||||
|
||||
#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \
|
||||
|| defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \
|
||||
|| defined(__MINGW64__) \
|
||||
|| defined(__s390x__) \
|
||||
|| (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) \
|
||||
|| defined(__loongarch64) )
|
||||
#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
|
||||
defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) || \
|
||||
defined(__MINGW64__) || defined(__s390x__) || \
|
||||
(defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || \
|
||||
defined(__PPC64LE__)) || \
|
||||
defined(__loongarch64))
|
||||
#define FASTFLOAT_64BIT 1
|
||||
#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \
|
||||
|| defined(__arm__) || defined(_M_ARM) || defined(__ppc__) \
|
||||
|| defined(__MINGW32__) || defined(__EMSCRIPTEN__))
|
||||
#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) || \
|
||||
defined(__arm__) || defined(_M_ARM) || defined(__ppc__) || \
|
||||
defined(__MINGW32__) || defined(__EMSCRIPTEN__))
|
||||
#define FASTFLOAT_32BIT 1
|
||||
#else
|
||||
// Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow.
|
||||
// We can never tell the register width, but the SIZE_MAX is a good approximation.
|
||||
// UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max portability.
|
||||
#if SIZE_MAX == 0xffff
|
||||
#error Unknown platform (16-bit, unsupported)
|
||||
#elif SIZE_MAX == 0xffffffff
|
||||
#define FASTFLOAT_32BIT 1
|
||||
#elif SIZE_MAX == 0xffffffffffffffff
|
||||
#define FASTFLOAT_64BIT 1
|
||||
#else
|
||||
#error Unknown platform (not 32-bit, not 64-bit?)
|
||||
#endif
|
||||
// We can never tell the register width, but the SIZE_MAX is a good
|
||||
// approximation. UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max
|
||||
// portability.
|
||||
#if SIZE_MAX == 0xffff
|
||||
#error Unknown platform (16-bit, unsupported)
|
||||
#elif SIZE_MAX == 0xffffffff
|
||||
#define FASTFLOAT_32BIT 1
|
||||
#elif SIZE_MAX == 0xffffffffffffffff
|
||||
#define FASTFLOAT_64BIT 1
|
||||
#else
|
||||
#error Unknown platform (not 32-bit, not 64-bit?)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__)) || \
|
||||
#if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__)) || \
|
||||
(defined(_M_ARM64) && !defined(__MINGW32__))
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
@ -129,9 +128,9 @@ using parse_options = parse_options_t<char>;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__SSE2__) || \
|
||||
(defined(FASTFLOAT_VISUAL_STUDIO) && \
|
||||
(defined(_M_AMD64) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP == 2)))
|
||||
#if defined(__SSE2__) || (defined(FASTFLOAT_VISUAL_STUDIO) && \
|
||||
(defined(_M_AMD64) || defined(_M_X64) || \
|
||||
(defined(_M_IX86_FP) && _M_IX86_FP == 2)))
|
||||
#define FASTFLOAT_SSE2 1
|
||||
#endif
|
||||
|
||||
@ -145,22 +144,19 @@ using parse_options = parse_options_t<char>;
|
||||
|
||||
#if defined(__GNUC__)
|
||||
// disable -Wcast-align=strict (GCC only)
|
||||
#define FASTFLOAT_SIMD_DISABLE_WARNINGS \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wcast-align\"")
|
||||
#define FASTFLOAT_SIMD_DISABLE_WARNINGS \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wcast-align\"")
|
||||
#else
|
||||
#define FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define FASTFLOAT_SIMD_RESTORE_WARNINGS \
|
||||
_Pragma("GCC diagnostic pop")
|
||||
#define FASTFLOAT_SIMD_RESTORE_WARNINGS _Pragma("GCC diagnostic pop")
|
||||
#else
|
||||
#define FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef FASTFLOAT_VISUAL_STUDIO
|
||||
#define fastfloat_really_inline __forceinline
|
||||
#else
|
||||
@ -168,18 +164,24 @@ using parse_options = parse_options_t<char>;
|
||||
#endif
|
||||
|
||||
#ifndef FASTFLOAT_ASSERT
|
||||
#define FASTFLOAT_ASSERT(x) { ((void)(x)); }
|
||||
#define FASTFLOAT_ASSERT(x) \
|
||||
{ ((void)(x)); }
|
||||
#endif
|
||||
|
||||
#ifndef FASTFLOAT_DEBUG_ASSERT
|
||||
#define FASTFLOAT_DEBUG_ASSERT(x) { ((void)(x)); }
|
||||
#define FASTFLOAT_DEBUG_ASSERT(x) \
|
||||
{ ((void)(x)); }
|
||||
#endif
|
||||
|
||||
// rust style `try!()` macro, or `?` operator
|
||||
#define FASTFLOAT_TRY(x) { if (!(x)) return false; }
|
||||
|
||||
#define FASTFLOAT_ENABLE_IF(...) typename std::enable_if<(__VA_ARGS__), int>::type
|
||||
#define FASTFLOAT_TRY(x) \
|
||||
{ \
|
||||
if (!(x)) \
|
||||
return false; \
|
||||
}
|
||||
|
||||
#define FASTFLOAT_ENABLE_IF(...) \
|
||||
typename std::enable_if<(__VA_ARGS__), int>::type
|
||||
|
||||
namespace fast_float {
|
||||
|
||||
@ -195,27 +197,24 @@ template <typename T>
|
||||
fastfloat_really_inline constexpr bool is_supported_float_type() {
|
||||
return std::is_same<T, float>::value || std::is_same<T, double>::value
|
||||
#if __STDCPP_FLOAT32_T__
|
||||
|| std::is_same<T, std::float32_t>::value
|
||||
|| std::is_same<T, std::float32_t>::value
|
||||
#endif
|
||||
#if __STDCPP_FLOAT64_T__
|
||||
|| std::is_same<T, std::float64_t>::value
|
||||
|| std::is_same<T, std::float64_t>::value
|
||||
#endif
|
||||
;
|
||||
;
|
||||
}
|
||||
|
||||
template <typename UC>
|
||||
fastfloat_really_inline constexpr bool is_supported_char_type() {
|
||||
return
|
||||
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;
|
||||
return 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;
|
||||
}
|
||||
|
||||
// Compares two ASCII strings in a case insensitive manner.
|
||||
template <typename UC>
|
||||
inline FASTFLOAT_CONSTEXPR14 bool
|
||||
fastfloat_strncasecmp(UC const * input1, UC const * input2, size_t length) {
|
||||
fastfloat_strncasecmp(UC const *input1, UC const *input2, size_t length) {
|
||||
char running_diff{0};
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
running_diff |= (char(input1[i]) ^ char(input2[i]));
|
||||
@ -228,18 +227,15 @@ fastfloat_strncasecmp(UC const * input1, UC const * input2, size_t length) {
|
||||
#endif
|
||||
|
||||
// a pointer and a length to a contiguous block of memory
|
||||
template <typename T>
|
||||
struct span {
|
||||
const T* ptr;
|
||||
template <typename T> struct span {
|
||||
const T *ptr;
|
||||
size_t length;
|
||||
constexpr span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {}
|
||||
constexpr span(const T *_ptr, size_t _length) : ptr(_ptr), length(_length) {}
|
||||
constexpr span() : ptr(nullptr), length(0) {}
|
||||
|
||||
constexpr size_t len() const noexcept {
|
||||
return length;
|
||||
}
|
||||
constexpr size_t len() const noexcept { return length; }
|
||||
|
||||
FASTFLOAT_CONSTEXPR14 const T& operator[](size_t index) const noexcept {
|
||||
FASTFLOAT_CONSTEXPR14 const T &operator[](size_t index) const noexcept {
|
||||
FASTFLOAT_DEBUG_ASSERT(index < length);
|
||||
return ptr[index];
|
||||
}
|
||||
@ -253,34 +249,51 @@ struct value128 {
|
||||
};
|
||||
|
||||
/* Helper C++14 constexpr generic implementation of leading_zeroes */
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||
int leading_zeroes_generic(uint64_t input_num, int last_bit = 0) {
|
||||
if(input_num & uint64_t(0xffffffff00000000)) { input_num >>= 32; last_bit |= 32; }
|
||||
if(input_num & uint64_t( 0xffff0000)) { input_num >>= 16; last_bit |= 16; }
|
||||
if(input_num & uint64_t( 0xff00)) { input_num >>= 8; last_bit |= 8; }
|
||||
if(input_num & uint64_t( 0xf0)) { input_num >>= 4; last_bit |= 4; }
|
||||
if(input_num & uint64_t( 0xc)) { input_num >>= 2; last_bit |= 2; }
|
||||
if(input_num & uint64_t( 0x2)) { /* input_num >>= 1; */ last_bit |= 1; }
|
||||
return 63 - last_bit;
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int
|
||||
leading_zeroes_generic(uint64_t input_num, int last_bit = 0) {
|
||||
if (input_num & uint64_t(0xffffffff00000000)) {
|
||||
input_num >>= 32;
|
||||
last_bit |= 32;
|
||||
}
|
||||
if (input_num & uint64_t(0xffff0000)) {
|
||||
input_num >>= 16;
|
||||
last_bit |= 16;
|
||||
}
|
||||
if (input_num & uint64_t(0xff00)) {
|
||||
input_num >>= 8;
|
||||
last_bit |= 8;
|
||||
}
|
||||
if (input_num & uint64_t(0xf0)) {
|
||||
input_num >>= 4;
|
||||
last_bit |= 4;
|
||||
}
|
||||
if (input_num & uint64_t(0xc)) {
|
||||
input_num >>= 2;
|
||||
last_bit |= 2;
|
||||
}
|
||||
if (input_num & uint64_t(0x2)) { /* input_num >>= 1; */
|
||||
last_bit |= 1;
|
||||
}
|
||||
return 63 - last_bit;
|
||||
}
|
||||
|
||||
/* result might be undefined when input_num is zero */
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
int leading_zeroes(uint64_t input_num) {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 int
|
||||
leading_zeroes(uint64_t input_num) {
|
||||
assert(input_num > 0);
|
||||
if (cpp20_and_in_constexpr()) {
|
||||
return leading_zeroes_generic(input_num);
|
||||
}
|
||||
#ifdef FASTFLOAT_VISUAL_STUDIO
|
||||
#if defined(_M_X64) || defined(_M_ARM64)
|
||||
#if defined(_M_X64) || defined(_M_ARM64)
|
||||
unsigned long leading_zero = 0;
|
||||
// Search the mask data from most significant bit (MSB)
|
||||
// to least significant bit (LSB) for a set bit (1).
|
||||
_BitScanReverse64(&leading_zero, input_num);
|
||||
return (int)(63 - leading_zero);
|
||||
#else
|
||||
#else
|
||||
return leading_zeroes_generic(input_num);
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
return __builtin_clzll(input_num);
|
||||
#endif
|
||||
@ -288,11 +301,11 @@ int leading_zeroes(uint64_t input_num) {
|
||||
|
||||
// slow emulation routine for 32-bit
|
||||
fastfloat_really_inline constexpr uint64_t emulu(uint32_t x, uint32_t y) {
|
||||
return x * (uint64_t)y;
|
||||
return x * (uint64_t)y;
|
||||
}
|
||||
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||
uint64_t umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t
|
||||
umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) {
|
||||
uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
|
||||
uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
|
||||
uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
|
||||
@ -307,18 +320,18 @@ uint64_t umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) {
|
||||
|
||||
// slow emulation routine for 32-bit
|
||||
#if !defined(__MINGW64__)
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||
uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t _umul128(uint64_t ab,
|
||||
uint64_t cd,
|
||||
uint64_t *hi) {
|
||||
return umul128_generic(ab, cd, hi);
|
||||
}
|
||||
#endif // !__MINGW64__
|
||||
|
||||
#endif // FASTFLOAT_32BIT
|
||||
|
||||
|
||||
// compute 64-bit a*b
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
value128 full_multiplication(uint64_t a, uint64_t b) {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128
|
||||
full_multiplication(uint64_t a, uint64_t b) {
|
||||
if (cpp20_and_in_constexpr()) {
|
||||
value128 answer;
|
||||
answer.low = umul128_generic(a, b, &answer.high);
|
||||
@ -360,22 +373,24 @@ constexpr static int32_t invalid_am_bias = -0x8000;
|
||||
// used for binary_format_lookup_tables<T>::max_mantissa
|
||||
constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5;
|
||||
|
||||
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> {
|
||||
using equiv_uint = typename std::conditional<sizeof(T) == 4, uint32_t, uint64_t>::type;
|
||||
using equiv_uint =
|
||||
typename std::conditional<sizeof(T) == 4, uint32_t, uint64_t>::type;
|
||||
|
||||
static inline constexpr int mantissa_explicit_bits();
|
||||
static inline constexpr int minimum_exponent();
|
||||
static inline constexpr int infinite_power();
|
||||
static inline constexpr int sign_index();
|
||||
static inline constexpr int min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST
|
||||
static inline constexpr int
|
||||
min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST
|
||||
static inline constexpr int max_exponent_fast_path();
|
||||
static inline constexpr int max_exponent_round_to_even();
|
||||
static inline constexpr int min_exponent_round_to_even();
|
||||
static inline constexpr uint64_t max_mantissa_fast_path(int64_t power);
|
||||
static inline constexpr uint64_t max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST
|
||||
static inline constexpr uint64_t
|
||||
max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST
|
||||
static inline constexpr int largest_power_of_ten();
|
||||
static inline constexpr int smallest_power_of_ten();
|
||||
static inline constexpr T exact_power_of_ten(int64_t power);
|
||||
@ -385,8 +400,7 @@ template <typename T> struct binary_format : binary_format_lookup_tables<T> {
|
||||
static inline constexpr equiv_uint hidden_bit_mask();
|
||||
};
|
||||
|
||||
template <typename U>
|
||||
struct binary_format_lookup_tables<double, U> {
|
||||
template <typename U> struct binary_format_lookup_tables<double, U> {
|
||||
static constexpr double powers_of_ten[] = {
|
||||
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,
|
||||
1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
|
||||
@ -410,14 +424,22 @@ struct binary_format_lookup_tables<double, U> {
|
||||
0x20000000000000 / (constant_55555 * constant_55555 * 5 * 5 * 5),
|
||||
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555),
|
||||
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5),
|
||||
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5),
|
||||
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5),
|
||||
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5),
|
||||
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555),
|
||||
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5),
|
||||
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5),
|
||||
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5),
|
||||
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5)};
|
||||
0x20000000000000 /
|
||||
(constant_55555 * constant_55555 * constant_55555 * 5 * 5),
|
||||
0x20000000000000 /
|
||||
(constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5),
|
||||
0x20000000000000 /
|
||||
(constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5),
|
||||
0x20000000000000 /
|
||||
(constant_55555 * constant_55555 * constant_55555 * constant_55555),
|
||||
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 *
|
||||
constant_55555 * 5),
|
||||
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 *
|
||||
constant_55555 * 5 * 5),
|
||||
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 *
|
||||
constant_55555 * 5 * 5 * 5),
|
||||
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 *
|
||||
constant_55555 * 5 * 5 * 5 * 5)};
|
||||
};
|
||||
|
||||
template <typename U>
|
||||
@ -426,26 +448,25 @@ constexpr double binary_format_lookup_tables<double, U>::powers_of_ten[];
|
||||
template <typename U>
|
||||
constexpr uint64_t binary_format_lookup_tables<double, U>::max_mantissa[];
|
||||
|
||||
template <typename U>
|
||||
struct binary_format_lookup_tables<float, U> {
|
||||
template <typename U> struct binary_format_lookup_tables<float, U> {
|
||||
static constexpr float powers_of_ten[] = {1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f,
|
||||
1e6f, 1e7f, 1e8f, 1e9f, 1e10f};
|
||||
1e6f, 1e7f, 1e8f, 1e9f, 1e10f};
|
||||
|
||||
// Largest integer value v so that (5**index * v) <= 1<<24.
|
||||
// 0x1000000 == 1<<24
|
||||
static constexpr uint64_t max_mantissa[] = {
|
||||
0x1000000,
|
||||
0x1000000 / 5,
|
||||
0x1000000 / (5 * 5),
|
||||
0x1000000 / (5 * 5 * 5),
|
||||
0x1000000 / (5 * 5 * 5 * 5),
|
||||
0x1000000 / (constant_55555),
|
||||
0x1000000 / (constant_55555 * 5),
|
||||
0x1000000 / (constant_55555 * 5 * 5),
|
||||
0x1000000 / (constant_55555 * 5 * 5 * 5),
|
||||
0x1000000 / (constant_55555 * 5 * 5 * 5 * 5),
|
||||
0x1000000 / (constant_55555 * constant_55555),
|
||||
0x1000000 / (constant_55555 * constant_55555 * 5)};
|
||||
0x1000000,
|
||||
0x1000000 / 5,
|
||||
0x1000000 / (5 * 5),
|
||||
0x1000000 / (5 * 5 * 5),
|
||||
0x1000000 / (5 * 5 * 5 * 5),
|
||||
0x1000000 / (constant_55555),
|
||||
0x1000000 / (constant_55555 * 5),
|
||||
0x1000000 / (constant_55555 * 5 * 5),
|
||||
0x1000000 / (constant_55555 * 5 * 5 * 5),
|
||||
0x1000000 / (constant_55555 * 5 * 5 * 5 * 5),
|
||||
0x1000000 / (constant_55555 * constant_55555),
|
||||
0x1000000 / (constant_55555 * constant_55555 * 5)};
|
||||
};
|
||||
|
||||
template <typename U>
|
||||
@ -454,7 +475,8 @@ constexpr float binary_format_lookup_tables<float, U>::powers_of_ten[];
|
||||
template <typename U>
|
||||
constexpr uint64_t binary_format_lookup_tables<float, U>::max_mantissa[];
|
||||
|
||||
template <> inline constexpr int binary_format<double>::min_exponent_fast_path() {
|
||||
template <>
|
||||
inline constexpr int binary_format<double>::min_exponent_fast_path() {
|
||||
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
|
||||
return 0;
|
||||
#else
|
||||
@ -462,7 +484,8 @@ template <> inline constexpr int binary_format<double>::min_exponent_fast_path()
|
||||
#endif
|
||||
}
|
||||
|
||||
template <> inline constexpr int binary_format<float>::min_exponent_fast_path() {
|
||||
template <>
|
||||
inline constexpr int binary_format<float>::min_exponent_fast_path() {
|
||||
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
|
||||
return 0;
|
||||
#else
|
||||
@ -470,26 +493,32 @@ template <> inline constexpr int binary_format<float>::min_exponent_fast_path()
|
||||
#endif
|
||||
}
|
||||
|
||||
template <> inline constexpr int binary_format<double>::mantissa_explicit_bits() {
|
||||
template <>
|
||||
inline constexpr int binary_format<double>::mantissa_explicit_bits() {
|
||||
return 52;
|
||||
}
|
||||
template <> inline constexpr int binary_format<float>::mantissa_explicit_bits() {
|
||||
template <>
|
||||
inline constexpr int binary_format<float>::mantissa_explicit_bits() {
|
||||
return 23;
|
||||
}
|
||||
|
||||
template <> inline constexpr int binary_format<double>::max_exponent_round_to_even() {
|
||||
template <>
|
||||
inline constexpr int binary_format<double>::max_exponent_round_to_even() {
|
||||
return 23;
|
||||
}
|
||||
|
||||
template <> inline constexpr int binary_format<float>::max_exponent_round_to_even() {
|
||||
template <>
|
||||
inline constexpr int binary_format<float>::max_exponent_round_to_even() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
template <> inline constexpr int binary_format<double>::min_exponent_round_to_even() {
|
||||
template <>
|
||||
inline constexpr int binary_format<double>::min_exponent_round_to_even() {
|
||||
return -4;
|
||||
}
|
||||
|
||||
template <> inline constexpr int binary_format<float>::min_exponent_round_to_even() {
|
||||
template <>
|
||||
inline constexpr int binary_format<float>::min_exponent_round_to_even() {
|
||||
return -17;
|
||||
}
|
||||
|
||||
@ -507,30 +536,42 @@ template <> inline constexpr int binary_format<float>::infinite_power() {
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
template <> inline constexpr int binary_format<double>::sign_index() { return 63; }
|
||||
template <> inline constexpr int binary_format<float>::sign_index() { return 31; }
|
||||
template <> inline constexpr int binary_format<double>::sign_index() {
|
||||
return 63;
|
||||
}
|
||||
template <> inline constexpr int binary_format<float>::sign_index() {
|
||||
return 31;
|
||||
}
|
||||
|
||||
template <> inline constexpr int binary_format<double>::max_exponent_fast_path() {
|
||||
template <>
|
||||
inline constexpr int binary_format<double>::max_exponent_fast_path() {
|
||||
return 22;
|
||||
}
|
||||
template <> inline constexpr int binary_format<float>::max_exponent_fast_path() {
|
||||
template <>
|
||||
inline constexpr int binary_format<float>::max_exponent_fast_path() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
template <> inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() {
|
||||
template <>
|
||||
inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() {
|
||||
return uint64_t(2) << mantissa_explicit_bits();
|
||||
}
|
||||
template <> inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path(int64_t power) {
|
||||
template <>
|
||||
inline constexpr uint64_t
|
||||
binary_format<double>::max_mantissa_fast_path(int64_t power) {
|
||||
// caller is responsible to ensure that
|
||||
// power >= 0 && power <= 22
|
||||
//
|
||||
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
||||
return (void)max_mantissa[0], max_mantissa[power];
|
||||
}
|
||||
template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() {
|
||||
template <>
|
||||
inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() {
|
||||
return uint64_t(2) << mantissa_explicit_bits();
|
||||
}
|
||||
template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path(int64_t power) {
|
||||
template <>
|
||||
inline constexpr uint64_t
|
||||
binary_format<float>::max_mantissa_fast_path(int64_t power) {
|
||||
// caller is responsible to ensure that
|
||||
// power >= 0 && power <= 10
|
||||
//
|
||||
@ -539,7 +580,8 @@ template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_pa
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr double binary_format<double>::exact_power_of_ten(int64_t power) {
|
||||
inline constexpr double
|
||||
binary_format<double>::exact_power_of_ten(int64_t power) {
|
||||
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
||||
return (void)powers_of_ten[0], powers_of_ten[power];
|
||||
}
|
||||
@ -549,13 +591,10 @@ inline constexpr float binary_format<float>::exact_power_of_ten(int64_t 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 int binary_format<double>::largest_power_of_ten() {
|
||||
return 308;
|
||||
}
|
||||
template <>
|
||||
inline constexpr int binary_format<float>::largest_power_of_ten() {
|
||||
template <> inline constexpr int binary_format<float>::largest_power_of_ten() {
|
||||
return 38;
|
||||
}
|
||||
|
||||
@ -563,8 +602,7 @@ template <>
|
||||
inline constexpr int binary_format<double>::smallest_power_of_ten() {
|
||||
return -342;
|
||||
}
|
||||
template <>
|
||||
inline constexpr int binary_format<float>::smallest_power_of_ten() {
|
||||
template <> inline constexpr int binary_format<float>::smallest_power_of_ten() {
|
||||
return -64;
|
||||
}
|
||||
|
||||
@ -575,39 +613,46 @@ template <> inline constexpr size_t binary_format<float>::max_digits() {
|
||||
return 114;
|
||||
}
|
||||
|
||||
template <> inline constexpr binary_format<float>::equiv_uint
|
||||
binary_format<float>::exponent_mask() {
|
||||
template <>
|
||||
inline constexpr binary_format<float>::equiv_uint
|
||||
binary_format<float>::exponent_mask() {
|
||||
return 0x7F800000;
|
||||
}
|
||||
template <> inline constexpr binary_format<double>::equiv_uint
|
||||
binary_format<double>::exponent_mask() {
|
||||
template <>
|
||||
inline constexpr binary_format<double>::equiv_uint
|
||||
binary_format<double>::exponent_mask() {
|
||||
return 0x7FF0000000000000;
|
||||
}
|
||||
|
||||
template <> inline constexpr binary_format<float>::equiv_uint
|
||||
binary_format<float>::mantissa_mask() {
|
||||
template <>
|
||||
inline constexpr binary_format<float>::equiv_uint
|
||||
binary_format<float>::mantissa_mask() {
|
||||
return 0x007FFFFF;
|
||||
}
|
||||
template <> inline constexpr binary_format<double>::equiv_uint
|
||||
binary_format<double>::mantissa_mask() {
|
||||
template <>
|
||||
inline constexpr binary_format<double>::equiv_uint
|
||||
binary_format<double>::mantissa_mask() {
|
||||
return 0x000FFFFFFFFFFFFF;
|
||||
}
|
||||
|
||||
template <> inline constexpr binary_format<float>::equiv_uint
|
||||
binary_format<float>::hidden_bit_mask() {
|
||||
template <>
|
||||
inline constexpr binary_format<float>::equiv_uint
|
||||
binary_format<float>::hidden_bit_mask() {
|
||||
return 0x00800000;
|
||||
}
|
||||
template <> inline constexpr binary_format<double>::equiv_uint
|
||||
binary_format<double>::hidden_bit_mask() {
|
||||
template <>
|
||||
inline constexpr binary_format<double>::equiv_uint
|
||||
binary_format<double>::hidden_bit_mask() {
|
||||
return 0x0010000000000000;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||
void to_float(bool negative, adjusted_mantissa am, T &value) {
|
||||
template <typename T>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
||||
to_float(bool negative, adjusted_mantissa am, T &value) {
|
||||
using fastfloat_uint = typename binary_format<T>::equiv_uint;
|
||||
fastfloat_uint word = (fastfloat_uint)am.mantissa;
|
||||
word |= fastfloat_uint(am.power2) << binary_format<T>::mantissa_explicit_bits();
|
||||
word |= fastfloat_uint(am.power2)
|
||||
<< binary_format<T>::mantissa_explicit_bits();
|
||||
word |= fastfloat_uint(negative) << binary_format<T>::sign_index();
|
||||
#if FASTFLOAT_HAS_BIT_CAST
|
||||
value = std::bit_cast<T>(word);
|
||||
@ -617,151 +662,123 @@ void to_float(bool negative, adjusted_mantissa am, T &value) {
|
||||
}
|
||||
|
||||
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
|
||||
template <typename = void>
|
||||
struct space_lut {
|
||||
template <typename = void> struct space_lut {
|
||||
static constexpr bool value[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
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, 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, 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, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
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, 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, 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};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool space_lut<T>::value[];
|
||||
template <typename T> constexpr bool space_lut<T>::value[];
|
||||
|
||||
inline constexpr bool is_space(uint8_t c) { return space_lut<>::value[c]; }
|
||||
#endif
|
||||
|
||||
template<typename UC>
|
||||
static constexpr uint64_t int_cmp_zeros()
|
||||
{
|
||||
static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4), "Unsupported character size");
|
||||
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 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 int int_cmp_len() {
|
||||
return sizeof(uint64_t) / sizeof(UC);
|
||||
}
|
||||
template<typename UC>
|
||||
static constexpr UC const * str_const_nan()
|
||||
{
|
||||
return nullptr;
|
||||
template <typename UC> static constexpr UC const *str_const_nan() {
|
||||
return nullptr;
|
||||
}
|
||||
template<>
|
||||
constexpr char const * str_const_nan<char>()
|
||||
{
|
||||
return "nan";
|
||||
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 wchar_t const * str_const_nan<wchar_t>()
|
||||
{
|
||||
return L"nan";
|
||||
template <> constexpr char32_t const *str_const_nan<char32_t>() {
|
||||
return U"nan";
|
||||
}
|
||||
template<>
|
||||
constexpr char16_t const * str_const_nan<char16_t>()
|
||||
{
|
||||
return u"nan";
|
||||
template <typename UC> static constexpr UC const *str_const_inf() {
|
||||
return nullptr;
|
||||
}
|
||||
template<>
|
||||
constexpr char32_t const * str_const_nan<char32_t>()
|
||||
{
|
||||
return U"nan";
|
||||
template <> constexpr char const *str_const_inf<char>() { return "infinity"; }
|
||||
template <> constexpr wchar_t const *str_const_inf<wchar_t>() {
|
||||
return L"infinity";
|
||||
}
|
||||
template<typename UC>
|
||||
static constexpr UC const * str_const_inf()
|
||||
{
|
||||
return nullptr;
|
||||
template <> constexpr char16_t const *str_const_inf<char16_t>() {
|
||||
return u"infinity";
|
||||
}
|
||||
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";
|
||||
template <> constexpr char32_t const *str_const_inf<char32_t>() {
|
||||
return U"infinity";
|
||||
}
|
||||
|
||||
|
||||
template <typename = void>
|
||||
struct int_luts {
|
||||
template <typename = void> struct int_luts {
|
||||
static constexpr uint8_t chdigit[] = {
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 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, 255,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
|
||||
255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255,
|
||||
255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 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, 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, 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, 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, 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, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255,
|
||||
255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
|
||||
35, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17,
|
||||
18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
33, 34, 35, 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, 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, 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, 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, 255, 255, 255, 255,
|
||||
255};
|
||||
|
||||
static constexpr size_t maxdigits_u64[] = {
|
||||
64, 41, 32, 28, 25, 23, 22, 21,
|
||||
20, 19, 18, 18, 17, 17, 16, 16,
|
||||
16, 16, 15, 15, 15, 15, 14, 14,
|
||||
14, 14, 14, 14, 14, 13, 13, 13,
|
||||
13, 13, 13
|
||||
};
|
||||
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};
|
||||
|
||||
static constexpr uint64_t min_safe_u64[] = {
|
||||
9223372036854775808ull, 12157665459056928801ull, 4611686018427387904, 7450580596923828125, 4738381338321616896,
|
||||
3909821048582988049, 9223372036854775808ull, 12157665459056928801ull, 10000000000000000000ull, 5559917313492231481,
|
||||
2218611106740436992, 8650415919381337933, 2177953337809371136, 6568408355712890625, 1152921504606846976,
|
||||
2862423051509815793, 6746640616477458432, 15181127029874798299ull, 1638400000000000000, 3243919932521508681,
|
||||
6221821273427820544, 11592836324538749809ull, 876488338465357824, 1490116119384765625, 2481152873203736576,
|
||||
4052555153018976267, 6502111422497947648, 10260628712958602189ull, 15943230000000000000ull, 787662783788549761,
|
||||
1152921504606846976, 1667889514952984961, 2386420683693101056, 3379220508056640625, 4738381338321616896
|
||||
};
|
||||
9223372036854775808ull, 12157665459056928801ull, 4611686018427387904,
|
||||
7450580596923828125, 4738381338321616896, 3909821048582988049,
|
||||
9223372036854775808ull, 12157665459056928801ull, 10000000000000000000ull,
|
||||
5559917313492231481, 2218611106740436992, 8650415919381337933,
|
||||
2177953337809371136, 6568408355712890625, 1152921504606846976,
|
||||
2862423051509815793, 6746640616477458432, 15181127029874798299ull,
|
||||
1638400000000000000, 3243919932521508681, 6221821273427820544,
|
||||
11592836324538749809ull, 876488338465357824, 1490116119384765625,
|
||||
2481152873203736576, 4052555153018976267, 6502111422497947648,
|
||||
10260628712958602189ull, 15943230000000000000ull, 787662783788549761,
|
||||
1152921504606846976, 1667889514952984961, 2386420683693101056,
|
||||
3379220508056640625, 4738381338321616896};
|
||||
};
|
||||
|
||||
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 size_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[];
|
||||
|
||||
template <typename UC>
|
||||
fastfloat_really_inline
|
||||
constexpr uint8_t ch_to_digit(UC c) { return int_luts<>::chdigit[static_cast<unsigned char>(c)]; }
|
||||
fastfloat_really_inline constexpr uint8_t ch_to_digit(UC c) {
|
||||
return int_luts<>::chdigit[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
fastfloat_really_inline
|
||||
constexpr size_t max_digits_u64(int base) { return int_luts<>::maxdigits_u64[base - 2]; }
|
||||
fastfloat_really_inline constexpr size_t max_digits_u64(int base) {
|
||||
return int_luts<>::maxdigits_u64[base - 2];
|
||||
}
|
||||
|
||||
// If a u64 is exactly max_digits_u64() in length, this is
|
||||
// the value below which it has definitely overflowed.
|
||||
fastfloat_really_inline
|
||||
constexpr uint64_t min_safe_u64(int base) { return int_luts<>::min_safe_u64[base - 2]; }
|
||||
// the value below which it has definitely overflowed.
|
||||
fastfloat_really_inline constexpr uint64_t min_safe_u64(int base) {
|
||||
return int_luts<>::min_safe_u64[base - 2];
|
||||
}
|
||||
|
||||
} // namespace fast_float
|
||||
|
||||
|
||||
@ -12,7 +12,6 @@
|
||||
#include <system_error>
|
||||
namespace fast_float {
|
||||
|
||||
|
||||
namespace detail {
|
||||
/**
|
||||
* Special case +inf, -inf, nan, infinity, -infinity.
|
||||
@ -20,45 +19,53 @@ namespace detail {
|
||||
* strings a null-free and fixed.
|
||||
**/
|
||||
template <typename T, typename UC>
|
||||
from_chars_result_t<UC> FASTFLOAT_CONSTEXPR14
|
||||
parse_infnan(UC const * first, UC const * last, T &value) noexcept {
|
||||
from_chars_result_t<UC> FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first,
|
||||
UC const *last,
|
||||
T &value) noexcept {
|
||||
from_chars_result_t<UC> answer{};
|
||||
answer.ptr = first;
|
||||
answer.ec = std::errc(); // be optimistic
|
||||
bool minusSign = false;
|
||||
if (*first == UC('-')) { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here
|
||||
minusSign = true;
|
||||
++first;
|
||||
if (*first ==
|
||||
UC('-')) { // assume first < last, so dereference without checks;
|
||||
// C++17 20.19.3.(7.1) explicitly forbids '+' here
|
||||
minusSign = true;
|
||||
++first;
|
||||
}
|
||||
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
|
||||
if (*first == UC('+')) {
|
||||
++first;
|
||||
++first;
|
||||
}
|
||||
#endif
|
||||
if (last - first >= 3) {
|
||||
if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
|
||||
answer.ptr = (first += 3);
|
||||
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).
|
||||
if(first != last && *first == UC('(')) {
|
||||
for(UC const * ptr = first + 1; ptr != last; ++ptr) {
|
||||
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).
|
||||
if (first != last && *first == UC('(')) {
|
||||
for (UC const *ptr = first + 1; ptr != last; ++ptr) {
|
||||
if (*ptr == UC(')')) {
|
||||
answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
|
||||
break;
|
||||
}
|
||||
else if(!((UC('a') <= *ptr && *ptr <= UC('z')) || (UC('A') <= *ptr && *ptr <= UC('Z')) || (UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_')))
|
||||
} else if (!((UC('a') <= *ptr && *ptr <= UC('z')) ||
|
||||
(UC('A') <= *ptr && *ptr <= UC('Z')) ||
|
||||
(UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_')))
|
||||
break; // forbidden char, not nan(n-char-seq-opt)
|
||||
}
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) {
|
||||
if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, str_const_inf<UC>() + 3, 5)) {
|
||||
if ((last - first >= 8) &&
|
||||
fastfloat_strncasecmp(first + 3, str_const_inf<UC>() + 3, 5)) {
|
||||
answer.ptr = first + 8;
|
||||
} else {
|
||||
answer.ptr = first + 3;
|
||||
}
|
||||
value = minusSign ? -std::numeric_limits<T>::infinity() : std::numeric_limits<T>::infinity();
|
||||
value = minusSign ? -std::numeric_limits<T>::infinity()
|
||||
: std::numeric_limits<T>::infinity();
|
||||
return answer;
|
||||
}
|
||||
}
|
||||
@ -88,71 +95,69 @@ fastfloat_really_inline bool rounds_to_nearest() noexcept {
|
||||
//
|
||||
// The volatile keywoard prevents the compiler from computing the function
|
||||
// at compile-time.
|
||||
// There might be other ways to prevent compile-time optimizations (e.g., asm).
|
||||
// The value does not need to be std::numeric_limits<float>::min(), any small
|
||||
// value so that 1 + x should round to 1 would do (after accounting for excess
|
||||
// precision, as in 387 instructions).
|
||||
// There might be other ways to prevent compile-time optimizations (e.g.,
|
||||
// asm). The value does not need to be std::numeric_limits<float>::min(), any
|
||||
// small value so that 1 + x should round to 1 would do (after accounting for
|
||||
// excess precision, as in 387 instructions).
|
||||
static volatile float fmin = std::numeric_limits<float>::min();
|
||||
float fmini = fmin; // we copy it so that it gets loaded at most once.
|
||||
//
|
||||
// Explanation:
|
||||
// Only when fegetround() == FE_TONEAREST do we have that
|
||||
// fmin + 1.0f == 1.0f - fmin.
|
||||
//
|
||||
// FE_UPWARD:
|
||||
// fmin + 1.0f > 1
|
||||
// 1.0f - fmin == 1
|
||||
//
|
||||
// FE_DOWNWARD or FE_TOWARDZERO:
|
||||
// fmin + 1.0f == 1
|
||||
// 1.0f - fmin < 1
|
||||
//
|
||||
// Note: This may fail to be accurate if fast-math has been
|
||||
// enabled, as rounding conventions may not apply.
|
||||
#ifdef FASTFLOAT_VISUAL_STUDIO
|
||||
# pragma warning(push)
|
||||
// todo: is there a VS warning?
|
||||
// see https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013
|
||||
#elif defined(__clang__)
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wfloat-equal"
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||
#endif
|
||||
//
|
||||
// Explanation:
|
||||
// Only when fegetround() == FE_TONEAREST do we have that
|
||||
// fmin + 1.0f == 1.0f - fmin.
|
||||
//
|
||||
// FE_UPWARD:
|
||||
// fmin + 1.0f > 1
|
||||
// 1.0f - fmin == 1
|
||||
//
|
||||
// FE_DOWNWARD or FE_TOWARDZERO:
|
||||
// fmin + 1.0f == 1
|
||||
// 1.0f - fmin < 1
|
||||
//
|
||||
// Note: This may fail to be accurate if fast-math has been
|
||||
// enabled, as rounding conventions may not apply.
|
||||
#ifdef FASTFLOAT_VISUAL_STUDIO
|
||||
#pragma warning(push)
|
||||
// todo: is there a VS warning?
|
||||
// see
|
||||
// https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013
|
||||
#elif defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wfloat-equal"
|
||||
#elif defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||
#endif
|
||||
return (fmini + 1.0f == 1.0f - fmini);
|
||||
#ifdef FASTFLOAT_VISUAL_STUDIO
|
||||
# pragma warning(pop)
|
||||
#elif defined(__clang__)
|
||||
# pragma clang diagnostic pop
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
#ifdef FASTFLOAT_VISUAL_STUDIO
|
||||
#pragma warning(pop)
|
||||
#elif defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#elif defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T>
|
||||
struct from_chars_caller
|
||||
{
|
||||
template <typename T> struct from_chars_caller {
|
||||
template <typename UC>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
static from_chars_result_t<UC> call(UC const * first, UC const * last,
|
||||
T &value, parse_options_t<UC> options) noexcept {
|
||||
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
|
||||
call(UC const *first, UC const *last, T &value,
|
||||
parse_options_t<UC> options) noexcept {
|
||||
return from_chars_advanced(first, last, value, options);
|
||||
}
|
||||
};
|
||||
|
||||
#if __STDCPP_FLOAT32_T__ == 1
|
||||
template <>
|
||||
struct from_chars_caller<std::float32_t>
|
||||
{
|
||||
template <> struct from_chars_caller<std::float32_t> {
|
||||
template <typename UC>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
static from_chars_result_t<UC> call(UC const * first, UC const * last,
|
||||
std::float32_t &value, parse_options_t<UC> options) noexcept{
|
||||
// if std::float32_t is defined, and we are in C++23 mode; macro set for float32;
|
||||
// set value to float due to equivalence between float and float32_t
|
||||
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
|
||||
call(UC const *first, UC const *last, std::float32_t &value,
|
||||
parse_options_t<UC> options) noexcept {
|
||||
// if std::float32_t is defined, and we are in C++23 mode; macro set for
|
||||
// float32; set value to float due to equivalence between float and
|
||||
// float32_t
|
||||
float val;
|
||||
auto ret = from_chars_advanced(first, last, val, options);
|
||||
value = val;
|
||||
@ -162,15 +167,14 @@ struct from_chars_caller<std::float32_t>
|
||||
#endif
|
||||
|
||||
#if __STDCPP_FLOAT64_T__ == 1
|
||||
template <>
|
||||
struct from_chars_caller<std::float64_t>
|
||||
{
|
||||
template <> struct from_chars_caller<std::float64_t> {
|
||||
template <typename UC>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
static from_chars_result_t<UC> call(UC const * first, UC const * last,
|
||||
std::float64_t &value, parse_options_t<UC> options) noexcept{
|
||||
// if std::float64_t is defined, and we are in C++23 mode; macro set for float64;
|
||||
// set value as double due to equivalence between double and float64_t
|
||||
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
|
||||
call(UC const *first, UC const *last, std::float64_t &value,
|
||||
parse_options_t<UC> options) noexcept {
|
||||
// if std::float64_t is defined, and we are in C++23 mode; macro set for
|
||||
// float64; set value as double due to equivalence between double and
|
||||
// float64_t
|
||||
double val;
|
||||
auto ret = from_chars_advanced(first, last, val, options);
|
||||
value = val;
|
||||
@ -179,26 +183,27 @@ struct from_chars_caller<std::float64_t>
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
template<typename T, typename UC, typename>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
from_chars_result_t<UC> from_chars(UC const * first, UC const * last,
|
||||
T &value, chars_format fmt /*= chars_format::general*/) noexcept {
|
||||
return from_chars_caller<T>::call(first, last, value, parse_options_t<UC>(fmt));
|
||||
template <typename T, typename UC, typename>
|
||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||
from_chars(UC const *first, UC const *last, T &value,
|
||||
chars_format fmt /*= chars_format::general*/) noexcept {
|
||||
return from_chars_caller<T>::call(first, last, value,
|
||||
parse_options_t<UC>(fmt));
|
||||
}
|
||||
|
||||
/**
|
||||
* This function overload takes parsed_number_string_t structure that is created and populated
|
||||
* either by from_chars_advanced function taking chars range and parsing options
|
||||
* or other parsing custom function implemented by user.
|
||||
* This function overload takes parsed_number_string_t structure that is created
|
||||
* and populated either by from_chars_advanced function taking chars range and
|
||||
* parsing options or other parsing custom function implemented by user.
|
||||
*/
|
||||
template<typename T, typename UC>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
from_chars_result_t<UC> from_chars_advanced(parsed_number_string_t<UC>& pns,
|
||||
T &value) noexcept {
|
||||
template <typename T, typename UC>
|
||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||
from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
|
||||
|
||||
static_assert (is_supported_float_type<T>(), "only some floating-point types are supported");
|
||||
static_assert (is_supported_char_type<UC>(), "only char, wchar_t, char16_t and char32_t are supported");
|
||||
static_assert(is_supported_float_type<T>(),
|
||||
"only some floating-point types are supported");
|
||||
static_assert(is_supported_char_type<UC>(),
|
||||
"only char, wchar_t, char16_t and char32_t are supported");
|
||||
|
||||
from_chars_result_t<UC> answer;
|
||||
|
||||
@ -207,9 +212,11 @@ from_chars_result_t<UC> from_chars_advanced(parsed_number_string_t<UC>& pns,
|
||||
// The implementation of the Clinger's fast path is convoluted because
|
||||
// we want round-to-nearest in all cases, irrespective of the rounding mode
|
||||
// selected on the thread.
|
||||
// We proceed optimistically, assuming that detail::rounds_to_nearest() returns
|
||||
// true.
|
||||
if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && !pns.too_many_digits) {
|
||||
// We proceed optimistically, assuming that detail::rounds_to_nearest()
|
||||
// returns true.
|
||||
if (binary_format<T>::min_exponent_fast_path() <= pns.exponent &&
|
||||
pns.exponent <= binary_format<T>::max_exponent_fast_path() &&
|
||||
!pns.too_many_digits) {
|
||||
// Unfortunately, the conventional Clinger's fast path is only possible
|
||||
// when the system rounds to the nearest float.
|
||||
//
|
||||
@ -217,60 +224,78 @@ from_chars_result_t<UC> from_chars_advanced(parsed_number_string_t<UC>& pns,
|
||||
// We could check it first (before the previous branch), but
|
||||
// there might be performance advantages at having the check
|
||||
// be last.
|
||||
if(!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) {
|
||||
if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) {
|
||||
// We have that fegetround() == FE_TONEAREST.
|
||||
// Next is Clinger's fast path.
|
||||
if (pns.mantissa <=binary_format<T>::max_mantissa_fast_path()) {
|
||||
if (pns.mantissa <= binary_format<T>::max_mantissa_fast_path()) {
|
||||
value = T(pns.mantissa);
|
||||
if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); }
|
||||
else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); }
|
||||
if (pns.negative) { value = -value; }
|
||||
if (pns.exponent < 0) {
|
||||
value = value / binary_format<T>::exact_power_of_ten(-pns.exponent);
|
||||
} else {
|
||||
value = value * binary_format<T>::exact_power_of_ten(pns.exponent);
|
||||
}
|
||||
if (pns.negative) {
|
||||
value = -value;
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
} else {
|
||||
// We do not have that fegetround() == FE_TONEAREST.
|
||||
// Next is a modified Clinger's fast path, inspired by Jakub Jelínek's proposal
|
||||
if (pns.exponent >= 0 && pns.mantissa <=binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
|
||||
// Next is a modified Clinger's fast path, inspired by Jakub Jelínek's
|
||||
// proposal
|
||||
if (pns.exponent >= 0 &&
|
||||
pns.mantissa <=
|
||||
binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
|
||||
#if defined(__clang__) || defined(FASTFLOAT_32BIT)
|
||||
// Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
|
||||
if(pns.mantissa == 0) {
|
||||
if (pns.mantissa == 0) {
|
||||
value = pns.negative ? T(-0.) : T(0.);
|
||||
return answer;
|
||||
}
|
||||
#endif
|
||||
value = T(pns.mantissa) * binary_format<T>::exact_power_of_ten(pns.exponent);
|
||||
if (pns.negative) { value = -value; }
|
||||
value = T(pns.mantissa) *
|
||||
binary_format<T>::exact_power_of_ten(pns.exponent);
|
||||
if (pns.negative) {
|
||||
value = -value;
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
}
|
||||
}
|
||||
adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
|
||||
if(pns.too_many_digits && am.power2 >= 0) {
|
||||
if(am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {
|
||||
adjusted_mantissa am =
|
||||
compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
|
||||
if (pns.too_many_digits && am.power2 >= 0) {
|
||||
if (am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {
|
||||
am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa);
|
||||
}
|
||||
}
|
||||
// If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0),
|
||||
// then we need to go the long way around again. This is very uncommon.
|
||||
if(am.power2 < 0) { am = digit_comp<T>(pns, am); }
|
||||
// If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa)
|
||||
// and we have an invalid power (am.power2 < 0), then we need to go the long
|
||||
// way around again. This is very uncommon.
|
||||
if (am.power2 < 0) {
|
||||
am = digit_comp<T>(pns, am);
|
||||
}
|
||||
to_float(pns.negative, am, value);
|
||||
// Test for over/underflow.
|
||||
if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) || am.power2 == binary_format<T>::infinite_power()) {
|
||||
if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) ||
|
||||
am.power2 == binary_format<T>::infinite_power()) {
|
||||
answer.ec = std::errc::result_out_of_range;
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
template<typename T, typename UC>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
|
||||
T &value, parse_options_t<UC> options) noexcept {
|
||||
template <typename T, typename UC>
|
||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||
from_chars_advanced(UC const *first, UC const *last, T &value,
|
||||
parse_options_t<UC> options) noexcept {
|
||||
|
||||
static_assert (is_supported_float_type<T>(), "only some floating-point types are supported");
|
||||
static_assert (is_supported_char_type<UC>(), "only char, wchar_t, char16_t and char32_t are supported");
|
||||
static_assert(is_supported_float_type<T>(),
|
||||
"only some floating-point types are supported");
|
||||
static_assert(is_supported_char_type<UC>(),
|
||||
"only char, wchar_t, char16_t and char32_t are supported");
|
||||
|
||||
from_chars_result_t<UC> 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))) {
|
||||
first++;
|
||||
}
|
||||
@ -280,7 +305,8 @@ from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
|
||||
answer.ptr = first;
|
||||
return answer;
|
||||
}
|
||||
parsed_number_string_t<UC> pns = parse_number_string<UC>(first, last, options);
|
||||
parsed_number_string_t<UC> pns =
|
||||
parse_number_string<UC>(first, last, options);
|
||||
if (!pns.valid) {
|
||||
if (options.format & chars_format::no_infnan) {
|
||||
answer.ec = std::errc::invalid_argument;
|
||||
@ -295,14 +321,14 @@ from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
|
||||
return from_chars_advanced(pns, value);
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename UC, typename>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
from_chars_result_t<UC> from_chars(UC const* first, UC const* last, T& value, int base) noexcept {
|
||||
static_assert (is_supported_char_type<UC>(), "only char, wchar_t, char16_t and char32_t are supported");
|
||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||
from_chars(UC const *first, UC const *last, T &value, int base) noexcept {
|
||||
static_assert(is_supported_char_type<UC>(),
|
||||
"only char, wchar_t, char16_t and char32_t are supported");
|
||||
|
||||
from_chars_result_t<UC> 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))) {
|
||||
first++;
|
||||
}
|
||||
|
||||
0
script/run-clangcldocker.sh
Normal file → Executable file
0
script/run-clangcldocker.sh
Normal file → Executable file
1136
tests/basictest.cpp
1136
tests/basictest.cpp
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,11 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
|
||||
double get1(const char* input) {
|
||||
double result_value;
|
||||
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) { return 1; }
|
||||
return result_value;
|
||||
double get1(const char *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) {
|
||||
return 1;
|
||||
}
|
||||
return result_value;
|
||||
}
|
||||
@ -1,9 +1,11 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
|
||||
double get10(const char* input) {
|
||||
double result_value;
|
||||
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) { return 10; }
|
||||
return result_value;
|
||||
double get10(const char *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) {
|
||||
return 10;
|
||||
}
|
||||
return result_value;
|
||||
}
|
||||
@ -1,9 +1,11 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
|
||||
double get2(const char* input) {
|
||||
double result_value;
|
||||
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) { return 2; }
|
||||
return result_value;
|
||||
double get2(const char *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) {
|
||||
return 2;
|
||||
}
|
||||
return result_value;
|
||||
}
|
||||
@ -1,9 +1,11 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
|
||||
double get3(const char* input) {
|
||||
double result_value;
|
||||
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) { return 3; }
|
||||
return result_value;
|
||||
double get3(const char *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) {
|
||||
return 3;
|
||||
}
|
||||
return result_value;
|
||||
}
|
||||
@ -1,9 +1,11 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
|
||||
double get4(const char* input) {
|
||||
double result_value;
|
||||
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) { return 4; }
|
||||
return result_value;
|
||||
double get4(const char *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) {
|
||||
return 4;
|
||||
}
|
||||
return result_value;
|
||||
}
|
||||
@ -1,9 +1,11 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
|
||||
double get5(const char* input) {
|
||||
double result_value;
|
||||
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) { return 5; }
|
||||
return result_value;
|
||||
double get5(const char *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) {
|
||||
return 5;
|
||||
}
|
||||
return result_value;
|
||||
}
|
||||
@ -1,9 +1,11 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
|
||||
double get6(const char* input) {
|
||||
double result_value;
|
||||
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) { return 6; }
|
||||
return result_value;
|
||||
double get6(const char *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) {
|
||||
return 6;
|
||||
}
|
||||
return result_value;
|
||||
}
|
||||
@ -1,9 +1,11 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
|
||||
double get7(const char* input) {
|
||||
double result_value;
|
||||
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) { return 7; }
|
||||
return result_value;
|
||||
double get7(const char *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) {
|
||||
return 7;
|
||||
}
|
||||
return result_value;
|
||||
}
|
||||
@ -1,9 +1,11 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
|
||||
double get8(const char* input) {
|
||||
double result_value;
|
||||
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) { return 8; }
|
||||
return result_value;
|
||||
double get8(const char *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) {
|
||||
return 8;
|
||||
}
|
||||
return result_value;
|
||||
}
|
||||
@ -1,9 +1,11 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
|
||||
double get9(const char* input) {
|
||||
double result_value;
|
||||
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) { return 9; }
|
||||
return result_value;
|
||||
double get9(const char *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) {
|
||||
return 9;
|
||||
}
|
||||
return result_value;
|
||||
}
|
||||
@ -1,19 +1,20 @@
|
||||
|
||||
|
||||
double get1(const char* input);
|
||||
double get2(const char* input);
|
||||
double get3(const char* input);
|
||||
double get4(const char* input);
|
||||
double get5(const char* input);
|
||||
double get6(const char* input);
|
||||
double get7(const char* input);
|
||||
double get8(const char* input);
|
||||
double get9(const char* input);
|
||||
double get10(const char* input);
|
||||
double get1(const char *input);
|
||||
double get2(const char *input);
|
||||
double get3(const char *input);
|
||||
double get4(const char *input);
|
||||
double get5(const char *input);
|
||||
double get6(const char *input);
|
||||
double get7(const char *input);
|
||||
double get8(const char *input);
|
||||
double get9(const char *input);
|
||||
double get10(const char *input);
|
||||
|
||||
int main(int arg, char** argv) {
|
||||
double x = get1(argv[0]) + get2(argv[0]) + get3(argv[0]) + get4(argv[0]) + get5(argv[0])
|
||||
+ get6(argv[0]) + get7(argv[0]) + get8(argv[0]) + get9(argv[0]) + get10(argv[0]);
|
||||
int main(int arg, char **argv) {
|
||||
double x = get1(argv[0]) + get2(argv[0]) + get3(argv[0]) + get4(argv[0]) +
|
||||
get5(argv[0]) + get6(argv[0]) + get7(argv[0]) + get8(argv[0]) +
|
||||
get9(argv[0]) + get10(argv[0]);
|
||||
|
||||
return int(x);
|
||||
return int(x);
|
||||
}
|
||||
@ -1,13 +1,16 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
double get(const char * input) {
|
||||
double result_value;
|
||||
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) { return 10; }
|
||||
return result_value;
|
||||
double get(const char *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
if (result.ec != std::errc()) {
|
||||
return 10;
|
||||
}
|
||||
return result_value;
|
||||
}
|
||||
|
||||
int main(int arg, char** argv) {
|
||||
double x = get(argv[0]);
|
||||
return int(x);
|
||||
int main(int arg, char **argv) {
|
||||
double x = get(argv[0]);
|
||||
return int(x);
|
||||
}
|
||||
@ -1,2 +1,2 @@
|
||||
#include "test.h"
|
||||
void foo() { }
|
||||
void foo() {}
|
||||
@ -5,11 +5,15 @@
|
||||
#include <system_error>
|
||||
|
||||
int main() {
|
||||
const std::string input = "3,1416 xyz ";
|
||||
double result;
|
||||
fast_float::parse_options options{fast_float::chars_format::general, ','};
|
||||
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
|
||||
if((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
|
||||
std::cout << "parsed the number " << result << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
const std::string input = "3,1416 xyz ";
|
||||
double result;
|
||||
fast_float::parse_options options{fast_float::chars_format::general, ','};
|
||||
auto answer = fast_float::from_chars_advanced(
|
||||
input.data(), input.data() + input.size(), result, options);
|
||||
if ((answer.ec != std::errc()) || ((result != 3.1416))) {
|
||||
std::cerr << "parsing failure\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
std::cout << "parsed the number " << result << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@ -4,36 +4,53 @@
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
|
||||
bool many() {
|
||||
const std::string input = "234532.3426362,7869234.9823,324562.645";
|
||||
const std::string input = "234532.3426362,7869234.9823,324562.645";
|
||||
double result;
|
||||
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
||||
if(answer.ec != std::errc()) { return false; }
|
||||
if(result != 234532.3426362) { return false; }
|
||||
if(answer.ptr[0] != ',') { return false; }
|
||||
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
||||
if(answer.ec != std::errc()) { return false; }
|
||||
if(result != 7869234.9823) { return false; }
|
||||
if(answer.ptr[0] != ',') { return false; }
|
||||
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
||||
if(answer.ec != std::errc()) { return false; }
|
||||
if(result != 324562.645) { return false; }
|
||||
auto answer =
|
||||
fast_float::from_chars(input.data(), input.data() + input.size(), result);
|
||||
if (answer.ec != std::errc()) {
|
||||
return false;
|
||||
}
|
||||
if (result != 234532.3426362) {
|
||||
return false;
|
||||
}
|
||||
if (answer.ptr[0] != ',') {
|
||||
return false;
|
||||
}
|
||||
answer = fast_float::from_chars(answer.ptr + 1, input.data() + input.size(),
|
||||
result);
|
||||
if (answer.ec != std::errc()) {
|
||||
return false;
|
||||
}
|
||||
if (result != 7869234.9823) {
|
||||
return false;
|
||||
}
|
||||
if (answer.ptr[0] != ',') {
|
||||
return false;
|
||||
}
|
||||
answer = fast_float::from_chars(answer.ptr + 1, input.data() + input.size(),
|
||||
result);
|
||||
if (answer.ec != std::errc()) {
|
||||
return false;
|
||||
}
|
||||
if (result != 324562.645) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void many_loop() {
|
||||
const std::string input = "234532.3426362,7869234.9823,324562.645";
|
||||
const std::string input = "234532.3426362,7869234.9823,324562.645";
|
||||
double result;
|
||||
const char* pointer = input.data();
|
||||
const char* end_pointer = input.data() + input.size();
|
||||
const char *pointer = input.data();
|
||||
const char *end_pointer = input.data() + input.size();
|
||||
|
||||
while(pointer < end_pointer) {
|
||||
while (pointer < end_pointer) {
|
||||
auto answer = fast_float::from_chars(pointer, end_pointer, result);
|
||||
if(answer.ec != std::errc()) {
|
||||
std::cerr << "error while parsing" << std::endl;
|
||||
break;
|
||||
if (answer.ec != std::errc()) {
|
||||
std::cerr << "error while parsing" << std::endl;
|
||||
break;
|
||||
}
|
||||
std::cout << "parsed: " << result << std::endl;
|
||||
pointer = answer.ptr;
|
||||
@ -47,34 +64,39 @@ void many_loop() {
|
||||
// consteval forces compile-time evaluation of the function in C++20.
|
||||
consteval double parse(std::string_view input) {
|
||||
double result;
|
||||
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
||||
if(answer.ec != std::errc()) { return -1.0; }
|
||||
auto answer =
|
||||
fast_float::from_chars(input.data(), input.data() + input.size(), result);
|
||||
if (answer.ec != std::errc()) {
|
||||
return -1.0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// This function should compile to a function which
|
||||
// merely returns 3.1415.
|
||||
constexpr double constexptest() {
|
||||
return parse("3.1415 input");
|
||||
}
|
||||
constexpr double constexptest() { return parse("3.1415 input"); }
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
const std::string input = "3.1416 xyz ";
|
||||
double result;
|
||||
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
||||
if((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
|
||||
std::cout << "parsed the number " << result << std::endl;
|
||||
const std::string input = "3.1416 xyz ";
|
||||
double result;
|
||||
auto answer =
|
||||
fast_float::from_chars(input.data(), input.data() + input.size(), result);
|
||||
if ((answer.ec != std::errc()) || ((result != 3.1416))) {
|
||||
std::cerr << "parsing failure\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
std::cout << "parsed the number " << result << std::endl;
|
||||
|
||||
if(!many()) {
|
||||
printf("Bug\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
many_loop();
|
||||
if (!many()) {
|
||||
printf("Bug\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
many_loop();
|
||||
#if FASTFLOAT_IS_CONSTEXPR
|
||||
if constexpr(constexptest() != 3.1415) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if constexpr (constexptest() != 3.1415) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
#endif
|
||||
return EXIT_SUCCESS;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@ -30,10 +30,12 @@ void allvalues() {
|
||||
const char *string_end = to_string(v, buffer);
|
||||
float result_value;
|
||||
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
||||
// Starting with version 4.0 for fast_float, we return result_out_of_range if the
|
||||
// value is either too small (too close to zero) or too large (effectively infinity).
|
||||
// So std::errc::result_out_of_range is normal for well-formed input strings.
|
||||
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||
// Starting with version 4.0 for fast_float, we return result_out_of_range
|
||||
// if the value is either too small (too close to zero) or too large
|
||||
// (effectively infinity). So std::errc::result_out_of_range is normal for
|
||||
// well-formed input strings.
|
||||
if (result.ec != std::errc() &&
|
||||
result.ec != std::errc::result_out_of_range) {
|
||||
std::cerr << "parsing error ? " << buffer << std::endl;
|
||||
abort();
|
||||
}
|
||||
@ -42,9 +44,9 @@ void allvalues() {
|
||||
std::cerr << "not nan" << buffer << std::endl;
|
||||
abort();
|
||||
}
|
||||
} else if(copysign(1,result_value) != copysign(1,v)) {
|
||||
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v
|
||||
<< std::endl;
|
||||
} else if (copysign(1, result_value) != copysign(1, v)) {
|
||||
std::cerr << "I got " << std::hexfloat << result_value
|
||||
<< " but I was expecting " << v << std::endl;
|
||||
abort();
|
||||
} else if (result_value != v) {
|
||||
std::cerr << "no match ? " << buffer << std::endl;
|
||||
|
||||
@ -16,11 +16,10 @@ template <typename T> char *to_string(T d, char *buffer) {
|
||||
return buffer + written;
|
||||
}
|
||||
|
||||
|
||||
bool basic_test_64bit(std::string vals, double val) {
|
||||
double result_value;
|
||||
auto result = fast_float::from_chars(vals.data(), vals.data() + vals.size(),
|
||||
result_value);
|
||||
result_value);
|
||||
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||
std::cerr << " I could not parse " << vals << std::endl;
|
||||
return false;
|
||||
@ -31,14 +30,14 @@ bool basic_test_64bit(std::string vals, double val) {
|
||||
std::cerr << "not nan" << result_value << std::endl;
|
||||
return false;
|
||||
}
|
||||
} else if(copysign(1,result_value) != copysign(1,val)) {
|
||||
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << val
|
||||
<< std::endl;
|
||||
} else if (copysign(1, result_value) != copysign(1, val)) {
|
||||
std::cerr << "I got " << std::hexfloat << result_value
|
||||
<< " but I was expecting " << val << std::endl;
|
||||
return false;
|
||||
} else if (result_value != val) {
|
||||
std::cerr << vals << std::endl;
|
||||
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << val
|
||||
<< std::endl;
|
||||
std::cerr << "I got " << std::hexfloat << result_value
|
||||
<< " but I was expecting " << val << std::endl;
|
||||
std::cerr << std::dec;
|
||||
std::cerr << "string: " << vals << std::endl;
|
||||
return false;
|
||||
@ -46,7 +45,6 @@ bool basic_test_64bit(std::string vals, double val) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void all_32bit_values() {
|
||||
char buffer[64];
|
||||
for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) {
|
||||
@ -61,8 +59,8 @@ void all_32bit_values() {
|
||||
|
||||
{
|
||||
const char *string_end = to_string(v, buffer);
|
||||
std::string s(buffer, size_t(string_end-buffer));
|
||||
if(!basic_test_64bit(s,v)) {
|
||||
std::string s(buffer, size_t(string_end - buffer));
|
||||
if (!basic_test_64bit(s, v)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,29 +15,37 @@
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
// workaround for CYGWIN
|
||||
double cygwin_strtod_l(const char* start, char** end) {
|
||||
double d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
ss << start;
|
||||
ss >> d;
|
||||
if(ss.fail()) { *end = nullptr; }
|
||||
if(ss.eof()) { ss.clear(); }
|
||||
auto nread = ss.tellg();
|
||||
*end = const_cast<char*>(start) + nread;
|
||||
return d;
|
||||
double cygwin_strtod_l(const char *start, char **end) {
|
||||
double d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
ss << start;
|
||||
ss >> d;
|
||||
if (ss.fail()) {
|
||||
*end = nullptr;
|
||||
}
|
||||
if (ss.eof()) {
|
||||
ss.clear();
|
||||
}
|
||||
auto nread = ss.tellg();
|
||||
*end = const_cast<char *>(start) + nread;
|
||||
return d;
|
||||
}
|
||||
float cygwin_strtof_l(const char* start, char** end) {
|
||||
float d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
ss << start;
|
||||
ss >> d;
|
||||
if(ss.fail()) { *end = nullptr; }
|
||||
if(ss.eof()) { ss.clear(); }
|
||||
auto nread = ss.tellg();
|
||||
*end = const_cast<char*>(start) + nread;
|
||||
return d;
|
||||
float cygwin_strtof_l(const char *start, char **end) {
|
||||
float d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
ss << start;
|
||||
ss >> d;
|
||||
if (ss.fail()) {
|
||||
*end = nullptr;
|
||||
}
|
||||
if (ss.eof()) {
|
||||
ss.clear();
|
||||
}
|
||||
auto nread = ss.tellg();
|
||||
*end = const_cast<char *>(start) + nread;
|
||||
return d;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -47,20 +55,21 @@ template <typename T> char *to_string(T d, char *buffer) {
|
||||
return buffer + written;
|
||||
}
|
||||
|
||||
void strtof_from_string(const char * st, float& d) {
|
||||
char *pr = (char *)st;
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
|
||||
d = cygwin_strtof_l(st, &pr);
|
||||
void strtof_from_string(const char *st, float &d) {
|
||||
char *pr = (char *)st;
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
|
||||
defined(sun) || defined(__sun)
|
||||
d = cygwin_strtof_l(st, &pr);
|
||||
#elif defined(_WIN32)
|
||||
static _locale_t c_locale = _create_locale(LC_ALL, "C");
|
||||
d = _strtof_l(st, &pr, c_locale);
|
||||
static _locale_t c_locale = _create_locale(LC_ALL, "C");
|
||||
d = _strtof_l(st, &pr, c_locale);
|
||||
#else
|
||||
static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL);
|
||||
d = strtof_l(st, &pr, c_locale);
|
||||
static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL);
|
||||
d = strtof_l(st, &pr, c_locale);
|
||||
#endif
|
||||
if (pr == st) {
|
||||
throw std::runtime_error("bug in strtod_from_string");
|
||||
}
|
||||
if (pr == st) {
|
||||
throw std::runtime_error("bug in strtod_from_string");
|
||||
}
|
||||
}
|
||||
|
||||
bool allvalues() {
|
||||
@ -73,10 +82,14 @@ bool allvalues() {
|
||||
}
|
||||
uint32_t word = uint32_t(w);
|
||||
memcpy(&v, &word, sizeof(v));
|
||||
if(std::isfinite(v)) {
|
||||
if (std::isfinite(v)) {
|
||||
float nextf = std::nextafterf(v, INFINITY);
|
||||
if(copysign(1,v) != copysign(1,nextf)) { continue; }
|
||||
if(!std::isfinite(nextf)) { continue; }
|
||||
if (copysign(1, v) != copysign(1, nextf)) {
|
||||
continue;
|
||||
}
|
||||
if (!std::isfinite(nextf)) {
|
||||
continue;
|
||||
}
|
||||
double v1{v};
|
||||
assert(float(v1) == v);
|
||||
double v2{nextf};
|
||||
@ -90,10 +103,12 @@ bool allvalues() {
|
||||
|
||||
float result_value;
|
||||
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
||||
// Starting with version 4.0 for fast_float, we return result_out_of_range if the
|
||||
// value is either too small (too close to zero) or too large (effectively infinity).
|
||||
// So std::errc::result_out_of_range is normal for well-formed input strings.
|
||||
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||
// Starting with version 4.0 for fast_float, we return result_out_of_range
|
||||
// if the value is either too small (too close to zero) or too large
|
||||
// (effectively infinity). So std::errc::result_out_of_range is normal for
|
||||
// well-formed input strings.
|
||||
if (result.ec != std::errc() &&
|
||||
result.ec != std::errc::result_out_of_range) {
|
||||
std::cerr << "parsing error ? " << buffer << std::endl;
|
||||
return false;
|
||||
}
|
||||
@ -103,26 +118,30 @@ bool allvalues() {
|
||||
std::cerr << "v " << std::hexfloat << v << std::endl;
|
||||
std::cerr << "v2 " << std::hexfloat << v2 << std::endl;
|
||||
std::cerr << "midv " << std::hexfloat << midv << std::endl;
|
||||
std::cerr << "expected_midv " << std::hexfloat << expected_midv << std::endl;
|
||||
std::cerr << "expected_midv " << std::hexfloat << expected_midv
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
} else if(copysign(1,result_value) != copysign(1,v)) {
|
||||
} else if (copysign(1, result_value) != copysign(1, v)) {
|
||||
std::cerr << buffer << std::endl;
|
||||
std::cerr << "v " << std::hexfloat << v << std::endl;
|
||||
std::cerr << "v2 " << std::hexfloat << v2 << std::endl;
|
||||
std::cerr << "midv " << std::hexfloat << midv << std::endl;
|
||||
std::cerr << "expected_midv " << std::hexfloat << expected_midv << std::endl;
|
||||
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v
|
||||
<< std::endl;
|
||||
std::cerr << "expected_midv " << std::hexfloat << expected_midv
|
||||
<< std::endl;
|
||||
std::cerr << "I got " << std::hexfloat << result_value
|
||||
<< " but I was expecting " << v << std::endl;
|
||||
return false;
|
||||
} else if (result_value != str_answer) {
|
||||
std::cerr << "no match ? " << buffer << std::endl;
|
||||
std::cerr << "v " << std::hexfloat << v << std::endl;
|
||||
std::cerr << "v2 " << std::hexfloat << v2 << std::endl;
|
||||
std::cerr << "midv " << std::hexfloat << midv << std::endl;
|
||||
std::cerr << "expected_midv " << std::hexfloat << expected_midv << std::endl;
|
||||
std::cerr << "expected_midv " << std::hexfloat << expected_midv
|
||||
<< std::endl;
|
||||
std::cout << "started with " << std::hexfloat << midv << std::endl;
|
||||
std::cout << "round down to " << std::hexfloat << str_answer << std::endl;
|
||||
std::cout << "round down to " << std::hexfloat << str_answer
|
||||
<< std::endl;
|
||||
std::cout << "got back " << std::hexfloat << result_value << std::endl;
|
||||
std::cout << std::dec;
|
||||
return false;
|
||||
@ -134,15 +153,24 @@ bool allvalues() {
|
||||
}
|
||||
|
||||
inline void Assert(bool Assertion) {
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
|
||||
if (!Assertion) { std::cerr << "Omitting hard failure on msys/cygwin/sun systems."; }
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
|
||||
defined(sun) || defined(__sun)
|
||||
if (!Assertion) {
|
||||
std::cerr << "Omitting hard failure on msys/cygwin/sun systems.";
|
||||
}
|
||||
#else
|
||||
if (!Assertion) { throw std::runtime_error("bug"); }
|
||||
if (!Assertion) {
|
||||
throw std::runtime_error("bug");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
int main() {
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
|
||||
std::cout << "Warning: msys/cygwin or solaris detected. This particular test is likely to generate false failures due to our reliance on the underlying runtime library as a gold standard." << std::endl;
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
|
||||
defined(sun) || defined(__sun)
|
||||
std::cout << "Warning: msys/cygwin or solaris detected. This particular test "
|
||||
"is likely to generate false failures due to our reliance on "
|
||||
"the underlying runtime library as a gold standard."
|
||||
<< std::endl;
|
||||
#endif
|
||||
Assert(allvalues());
|
||||
std::cout << std::endl;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -7,14 +7,16 @@
|
||||
#include <stdfloat>
|
||||
|
||||
int main() {
|
||||
// Write some testcases for the parsing of floating point numbers in the float32_t type.
|
||||
// We use the from_chars function defined in this library.
|
||||
// Write some testcases for the parsing of floating point numbers in the
|
||||
// float32_t type. We use the from_chars function defined in this library.
|
||||
#if __STDCPP_FLOAT32_T__
|
||||
const std::vector<std::float32_t> float32_test_expected{123.456f, -78.9f, 0.0001f, 3.40282e+038f};
|
||||
const std::vector<std::string_view> float32_test{"123.456", "-78.9", "0.0001", "3.40282e+038"};
|
||||
const std::vector<std::float32_t> float32_test_expected{
|
||||
123.456f, -78.9f, 0.0001f, 3.40282e+038f};
|
||||
const std::vector<std::string_view> float32_test{"123.456", "-78.9", "0.0001",
|
||||
"3.40282e+038"};
|
||||
std::cout << "runing float32 test" << std::endl;
|
||||
for (std::size_t i = 0; i < float32_test.size(); ++i) {
|
||||
const auto& f = float32_test[i];
|
||||
const auto &f = float32_test[i];
|
||||
std::float32_t result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
|
||||
|
||||
@ -22,8 +24,9 @@ int main() {
|
||||
std::cerr << "Failed to parse: \"" << f << "\"" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(result != float32_test_expected[i]) {
|
||||
std::cerr << "Test failed for input: \"" << f << "\" expected " << float32_test_expected[i] << " got " << result << std::endl;
|
||||
if (result != float32_test_expected[i]) {
|
||||
std::cerr << "Test failed for input: \"" << f << "\" expected "
|
||||
<< float32_test_expected[i] << " got " << result << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
@ -33,11 +36,14 @@ int main() {
|
||||
|
||||
#if __STDCPP_FLOAT64_T__
|
||||
// Test cases for std::float64_t
|
||||
const std::vector<std::float64_t> float64_test_expected{1.23e4, -5.67e-8, 1.7976931348623157e+308, -1.7976931348623157e+308};
|
||||
const std::vector<std::string_view> float64_test{"1.23e4", "-5.67e-8", "1.7976931348623157e+308", "-1.7976931348623157e+308"};
|
||||
const std::vector<std::float64_t> float64_test_expected{
|
||||
1.23e4, -5.67e-8, 1.7976931348623157e+308, -1.7976931348623157e+308};
|
||||
const std::vector<std::string_view> float64_test{"1.23e4", "-5.67e-8",
|
||||
"1.7976931348623157e+308",
|
||||
"-1.7976931348623157e+308"};
|
||||
std::cout << "runing float64 test" << std::endl;
|
||||
for (std::size_t i = 0; i < float64_test.size(); ++i) {
|
||||
const auto& f = float64_test[i];
|
||||
const auto &f = float64_test[i];
|
||||
std::float64_t result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
|
||||
|
||||
@ -45,8 +51,9 @@ int main() {
|
||||
std::cerr << "Failed to parse: \"" << f << "\"" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(result != float64_test_expected[i]) {
|
||||
std::cerr << "Test failed for input: \"" << f << "\" expected " << float64_test_expected[i] << " got " << result << std::endl;
|
||||
if (result != float64_test_expected[i]) {
|
||||
std::cerr << "Test failed for input: \"" << f << "\" expected "
|
||||
<< float64_test_expected[i] << " got " << result << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,61 +9,66 @@
|
||||
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
|
||||
int main_readme() {
|
||||
const std::string input = "1d+4";
|
||||
double result;
|
||||
fast_float::parse_options options{ fast_float::chars_format::fortran };
|
||||
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
|
||||
if((answer.ec != std::errc()) || ((result != 10000))) { std::cerr << "parsing failure\n" << result <<"\n"; return EXIT_FAILURE; }
|
||||
std::cout << "parsed the number " << result << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
const std::string input = "1d+4";
|
||||
double result;
|
||||
fast_float::parse_options options{fast_float::chars_format::fortran};
|
||||
auto answer = fast_float::from_chars_advanced(
|
||||
input.data(), input.data() + input.size(), result, options);
|
||||
if ((answer.ec != std::errc()) || ((result != 10000))) {
|
||||
std::cerr << "parsing failure\n" << result << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
std::cout << "parsed the number " << result << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
const std::vector<double> expected{ 10000, 1000, 100, 10, 1, .1, .01, .001, .0001 };
|
||||
const std::vector<std::string> fmt1{ "1+4", "1+3", "1+2", "1+1", "1+0", "1-1", "1-2",
|
||||
"1-3", "1-4" };
|
||||
const std::vector<std::string> fmt2{ "1d+4", "1d+3", "1d+2", "1d+1", "1d+0", "1d-1",
|
||||
"1d-2", "1d-3", "1d-4" };
|
||||
const std::vector<std::string> fmt3{ "+1+4", "+1+3", "+1+2", "+1+1", "+1+0", "+1-1",
|
||||
"+1-2", "+1-3", "+1-4" };
|
||||
const fast_float::parse_options options{ fast_float::chars_format::fortran };
|
||||
int main() {
|
||||
const std::vector<double> expected{10000, 1000, 100, 10, 1,
|
||||
.1, .01, .001, .0001};
|
||||
const std::vector<std::string> fmt1{"1+4", "1+3", "1+2", "1+1", "1+0",
|
||||
"1-1", "1-2", "1-3", "1-4"};
|
||||
const std::vector<std::string> fmt2{"1d+4", "1d+3", "1d+2", "1d+1", "1d+0",
|
||||
"1d-1", "1d-2", "1d-3", "1d-4"};
|
||||
const std::vector<std::string> fmt3{"+1+4", "+1+3", "+1+2", "+1+1", "+1+0",
|
||||
"+1-1", "+1-2", "+1-3", "+1-4"};
|
||||
const fast_float::parse_options options{fast_float::chars_format::fortran};
|
||||
|
||||
for ( auto const& f : fmt1 ) {
|
||||
auto d{ std::distance( &fmt1[0], &f ) };
|
||||
for (auto const &f : fmt1) {
|
||||
auto d{std::distance(&fmt1[0], &f)};
|
||||
double result;
|
||||
auto answer{ fast_float::from_chars_advanced( f.data(), f.data()+f.size(), result,
|
||||
options ) };
|
||||
if ( answer.ec != std::errc() || result != expected[std::size_t(d)] ) {
|
||||
auto answer{fast_float::from_chars_advanced(f.data(), f.data() + f.size(),
|
||||
result, options)};
|
||||
if (answer.ec != std::errc() || result != expected[std::size_t(d)]) {
|
||||
std::cerr << "parsing failure on " << f << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
for ( auto const& f : fmt2 ) {
|
||||
auto d{ std::distance( &fmt2[0], &f ) };
|
||||
for (auto const &f : fmt2) {
|
||||
auto d{std::distance(&fmt2[0], &f)};
|
||||
double result;
|
||||
auto answer{ fast_float::from_chars_advanced( f.data(), f.data()+f.size(), result,
|
||||
options ) };
|
||||
if ( answer.ec != std::errc() || result != expected[std::size_t(d)] ) {
|
||||
auto answer{fast_float::from_chars_advanced(f.data(), f.data() + f.size(),
|
||||
result, options)};
|
||||
if (answer.ec != std::errc() || result != expected[std::size_t(d)]) {
|
||||
std::cerr << "parsing failure on " << f << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
for ( auto const& f : fmt3 ) {
|
||||
auto d{ std::distance( &fmt3[0], &f ) };
|
||||
for (auto const &f : fmt3) {
|
||||
auto d{std::distance(&fmt3[0], &f)};
|
||||
double result;
|
||||
auto answer{ fast_float::from_chars_advanced( f.data(), f.data()+f.size(), result,
|
||||
options ) };
|
||||
if ( answer.ec != std::errc() || result != expected[std::size_t(d)] ) {
|
||||
auto answer{fast_float::from_chars_advanced(f.data(), f.data() + f.size(),
|
||||
result, options)};
|
||||
if (answer.ec != std::errc() || result != expected[std::size_t(d)]) {
|
||||
std::cerr << "parsing failure on " << f << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
if(main_readme() != EXIT_SUCCESS) { return EXIT_FAILURE; }
|
||||
if (main_readme() != EXIT_SUCCESS) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@ -9,31 +9,43 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
int main_readme() {
|
||||
const std::string input = "+.1"; // not valid
|
||||
double result;
|
||||
fast_float::parse_options options{ fast_float::chars_format::json };
|
||||
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
|
||||
if(answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; }
|
||||
return EXIT_SUCCESS;
|
||||
const std::string input = "+.1"; // not valid
|
||||
double result;
|
||||
fast_float::parse_options options{fast_float::chars_format::json};
|
||||
auto answer = fast_float::from_chars_advanced(
|
||||
input.data(), input.data() + input.size(), result, options);
|
||||
if (answer.ec == std::errc()) {
|
||||
std::cerr << "should have failed\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int main_readme2() {
|
||||
const std::string input = "inf"; // not valid in JSON
|
||||
double result;
|
||||
fast_float::parse_options options{ fast_float::chars_format::json };
|
||||
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
|
||||
if(answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; }
|
||||
return EXIT_SUCCESS;
|
||||
const std::string input = "inf"; // not valid in JSON
|
||||
double result;
|
||||
fast_float::parse_options options{fast_float::chars_format::json};
|
||||
auto answer = fast_float::from_chars_advanced(
|
||||
input.data(), input.data() + input.size(), result, options);
|
||||
if (answer.ec == std::errc()) {
|
||||
std::cerr << "should have failed\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int main_readme3() {
|
||||
const std::string input = "inf"; // not valid in JSON but we allow it with json_or_infnan
|
||||
double result;
|
||||
fast_float::parse_options options{ fast_float::chars_format::json_or_infnan };
|
||||
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
|
||||
if(answer.ec != std::errc() || (!std::isinf(result))) { std::cerr << "should have parsed infinity\n"; return EXIT_FAILURE; }
|
||||
return EXIT_SUCCESS;
|
||||
const std::string input =
|
||||
"inf"; // not valid in JSON but we allow it with json_or_infnan
|
||||
double result;
|
||||
fast_float::parse_options options{fast_float::chars_format::json_or_infnan};
|
||||
auto answer = fast_float::from_chars_advanced(
|
||||
input.data(), input.data() + input.size(), result, options);
|
||||
if (answer.ec != std::errc() || (!std::isinf(result))) {
|
||||
std::cerr << "should have parsed infinity\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
struct ExpectedResult {
|
||||
@ -77,41 +89,44 @@ int main() {
|
||||
{"inf", {fast_float::parse_error::no_digits_in_integer_part, 0}},
|
||||
{"nan(snan)", {fast_float::parse_error::no_digits_in_integer_part, 0}}};
|
||||
|
||||
for (std::size_t i = 0; i < accept.size(); ++i)
|
||||
{
|
||||
const auto& s = accept[i].input;
|
||||
const auto& expected = accept[i].expected;
|
||||
for (std::size_t i = 0; i < accept.size(); ++i) {
|
||||
const auto &s = accept[i].input;
|
||||
const auto &expected = accept[i].expected;
|
||||
double result;
|
||||
auto answer = fast_float::from_chars(s.data(), s.data() + s.size(), result, fast_float::chars_format::json_or_infnan);
|
||||
auto answer =
|
||||
fast_float::from_chars(s.data(), s.data() + s.size(), result,
|
||||
fast_float::chars_format::json_or_infnan);
|
||||
if (answer.ec != std::errc()) {
|
||||
std::cerr << "json fmt rejected valid json " << s << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (result != expected.value) {
|
||||
std::cerr << "json fmt gave wrong result " << s << " (expected " << expected.value << " got " << result << ")" << std::endl;
|
||||
std::cerr << "json fmt gave wrong result " << s << " (expected "
|
||||
<< expected.value << " got " << result << ")" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (std::string(answer.ptr) != expected.junk_chars) {
|
||||
std::cerr << "json fmt has wrong trailing characters " << s << " (expected " << expected.junk_chars << " got " << answer.ptr << ")" << std::endl;
|
||||
std::cerr << "json fmt has wrong trailing characters " << s
|
||||
<< " (expected " << expected.junk_chars << " got " << answer.ptr
|
||||
<< ")" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < reject.size(); ++i)
|
||||
{
|
||||
const auto& s = reject[i].input;
|
||||
for (std::size_t i = 0; i < reject.size(); ++i) {
|
||||
const auto &s = reject[i].input;
|
||||
double result;
|
||||
auto answer = fast_float::from_chars(s.data(), s.data() + s.size(), result, fast_float::chars_format::json);
|
||||
auto answer = fast_float::from_chars(s.data(), s.data() + s.size(), result,
|
||||
fast_float::chars_format::json);
|
||||
if (answer.ec == std::errc()) {
|
||||
std::cerr << "json fmt accepted invalid json " << s << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < reject.size(); ++i)
|
||||
{
|
||||
const auto& f = reject[i].input;
|
||||
const auto& expected_reason = reject[i].reason;
|
||||
for (std::size_t i = 0; i < reject.size(); ++i) {
|
||||
const auto &f = reject[i].input;
|
||||
const auto &expected_reason = reject[i].reason;
|
||||
auto answer = fast_float::parse_number_string(
|
||||
f.data(), f.data() + f.size(),
|
||||
fast_float::parse_options(fast_float::chars_format::json));
|
||||
@ -133,8 +148,12 @@ int main() {
|
||||
}
|
||||
}
|
||||
|
||||
if(main_readme() != EXIT_SUCCESS) { return EXIT_FAILURE; }
|
||||
if(main_readme2() != EXIT_SUCCESS) { return EXIT_FAILURE; }
|
||||
if (main_readme() != EXIT_SUCCESS) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (main_readme2() != EXIT_SUCCESS) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@ -9,8 +9,7 @@
|
||||
#include <system_error>
|
||||
|
||||
template <typename T> char *to_string(T d, char *buffer) {
|
||||
auto written = std::snprintf(buffer, 128, "%.*e",
|
||||
64, d);
|
||||
auto written = std::snprintf(buffer, 128, "%.*e", 64, d);
|
||||
return buffer + written;
|
||||
}
|
||||
|
||||
@ -29,10 +28,12 @@ void allvalues() {
|
||||
const char *string_end = to_string(v, buffer);
|
||||
float result_value;
|
||||
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
||||
// Starting with version 4.0 for fast_float, we return result_out_of_range if the
|
||||
// value is either too small (too close to zero) or too large (effectively infinity).
|
||||
// So std::errc::result_out_of_range is normal for well-formed input strings.
|
||||
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||
// Starting with version 4.0 for fast_float, we return result_out_of_range
|
||||
// if the value is either too small (too close to zero) or too large
|
||||
// (effectively infinity). So std::errc::result_out_of_range is normal for
|
||||
// well-formed input strings.
|
||||
if (result.ec != std::errc() &&
|
||||
result.ec != std::errc::result_out_of_range) {
|
||||
std::cerr << "parsing error ? " << buffer << std::endl;
|
||||
abort();
|
||||
}
|
||||
@ -41,13 +42,14 @@ void allvalues() {
|
||||
std::cerr << "not nan" << buffer << std::endl;
|
||||
abort();
|
||||
}
|
||||
} else if(copysign(1,result_value) != copysign(1,v)) {
|
||||
} else if (copysign(1, result_value) != copysign(1, v)) {
|
||||
std::cerr << buffer << std::endl;
|
||||
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v
|
||||
<< std::endl;
|
||||
std::cerr << "I got " << std::hexfloat << result_value
|
||||
<< " but I was expecting " << v << std::endl;
|
||||
abort();
|
||||
} else if (result_value != v) {
|
||||
std::cerr << "no match ? " << buffer << " got " << result_value << " expected " << v << std::endl;
|
||||
std::cerr << "no match ? " << buffer << " got " << result_value
|
||||
<< " expected " << v << std::endl;
|
||||
std::cout << "started with " << std::hexfloat << v << std::endl;
|
||||
std::cout << "got back " << std::hexfloat << result_value << std::endl;
|
||||
std::cout << std::dec;
|
||||
|
||||
@ -7,8 +7,7 @@
|
||||
#include <iostream>
|
||||
|
||||
template <typename T> char *to_string(T d, char *buffer) {
|
||||
auto written = std::snprintf(buffer, 128, "%.*e",
|
||||
64, d);
|
||||
auto written = std::snprintf(buffer, 128, "%.*e", 64, d);
|
||||
return buffer + written;
|
||||
}
|
||||
|
||||
@ -28,10 +27,12 @@ void all_32bit_values() {
|
||||
const char *string_end = to_string(v, buffer);
|
||||
double result_value;
|
||||
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
||||
// Starting with version 4.0 for fast_float, we return result_out_of_range if the
|
||||
// value is either too small (too close to zero) or too large (effectively infinity).
|
||||
// So std::errc::result_out_of_range is normal for well-formed input strings.
|
||||
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||
// Starting with version 4.0 for fast_float, we return result_out_of_range
|
||||
// if the value is either too small (too close to zero) or too large
|
||||
// (effectively infinity). So std::errc::result_out_of_range is normal for
|
||||
// well-formed input strings.
|
||||
if (result.ec != std::errc() &&
|
||||
result.ec != std::errc::result_out_of_range) {
|
||||
std::cerr << "parsing error ? " << buffer << std::endl;
|
||||
abort();
|
||||
}
|
||||
@ -40,9 +41,9 @@ void all_32bit_values() {
|
||||
std::cerr << "not nan" << buffer << std::endl;
|
||||
abort();
|
||||
}
|
||||
} else if(copysign(1,result_value) != copysign(1,v)) {
|
||||
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v
|
||||
<< std::endl;
|
||||
} else if (copysign(1, result_value) != copysign(1, v)) {
|
||||
std::cerr << "I got " << std::hexfloat << result_value
|
||||
<< " but I was expecting " << v << std::endl;
|
||||
abort();
|
||||
} else if (std::isnan(v)) {
|
||||
if (!std::isnan(result_value)) {
|
||||
|
||||
@ -8,8 +8,7 @@
|
||||
#include <system_error>
|
||||
|
||||
template <typename T> char *to_string(T d, char *buffer) {
|
||||
auto written = std::snprintf(buffer, 128, "%.*e",
|
||||
64, d);
|
||||
auto written = std::snprintf(buffer, 128, "%.*e", 64, d);
|
||||
return buffer + written;
|
||||
}
|
||||
|
||||
@ -33,7 +32,8 @@ static inline void lehmer64_seed(uint64_t seed) {
|
||||
}
|
||||
|
||||
static inline uint64_t lehmer64() {
|
||||
fast_float::value128 v = fast_float::full_multiplication(g_lehmer64_state.low,UINT64_C(0xda942042e4dd58b5));
|
||||
fast_float::value128 v = fast_float::full_multiplication(
|
||||
g_lehmer64_state.low, UINT64_C(0xda942042e4dd58b5));
|
||||
v.high += g_lehmer64_state.high * UINT64_C(0xda942042e4dd58b5);
|
||||
g_lehmer64_state = v;
|
||||
return v.high;
|
||||
@ -56,10 +56,12 @@ void random_values(size_t N) {
|
||||
const char *string_end = to_string(v, buffer);
|
||||
double result_value;
|
||||
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
||||
// Starting with version 4.0 for fast_float, we return result_out_of_range if the
|
||||
// value is either too small (too close to zero) or too large (effectively infinity).
|
||||
// So std::errc::result_out_of_range is normal for well-formed input strings.
|
||||
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||
// Starting with version 4.0 for fast_float, we return result_out_of_range
|
||||
// if the value is either too small (too close to zero) or too large
|
||||
// (effectively infinity). So std::errc::result_out_of_range is normal for
|
||||
// well-formed input strings.
|
||||
if (result.ec != std::errc() &&
|
||||
result.ec != std::errc::result_out_of_range) {
|
||||
std::cerr << "parsing error ? " << buffer << std::endl;
|
||||
errors++;
|
||||
if (errors > 10) {
|
||||
@ -75,10 +77,10 @@ void random_values(size_t N) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
} else if(copysign(1,result_value) != copysign(1,v)) {
|
||||
} else if (copysign(1, result_value) != copysign(1, v)) {
|
||||
std::cerr << buffer << std::endl;
|
||||
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v
|
||||
<< std::endl;
|
||||
std::cerr << "I got " << std::hexfloat << result_value
|
||||
<< " but I was expecting " << v << std::endl;
|
||||
abort();
|
||||
} else if (result_value != v) {
|
||||
std::cerr << "no match ? '" << buffer << "'" << std::endl;
|
||||
@ -97,7 +99,8 @@ void random_values(size_t N) {
|
||||
|
||||
int main() {
|
||||
errors = 0;
|
||||
size_t N = size_t(1) << (sizeof(size_t) * 4); // shift: 32 for 64bit, 16 for 32bit
|
||||
size_t N =
|
||||
size_t(1) << (sizeof(size_t) * 4); // shift: 32 for 64bit, 16 for 32bit
|
||||
random_values(N);
|
||||
if (errors == 0) {
|
||||
std::cout << std::endl;
|
||||
|
||||
@ -8,39 +8,44 @@
|
||||
#include <vector>
|
||||
|
||||
inline void Assert(bool Assertion) {
|
||||
if (!Assertion) { throw std::runtime_error("bug"); }
|
||||
if (!Assertion) {
|
||||
throw std::runtime_error("bug");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool test() {
|
||||
std::string input = "0.156250000000000000000000000000000000000000 3.14159265358979323846264338327950288419716939937510 2.71828182845904523536028747135266249775724709369995";
|
||||
std::vector<T> answers = {T(0.15625), T(3.141592653589793), T(2.718281828459045)};
|
||||
const char * begin = input.data();
|
||||
const char * end = input.data() + input.size();
|
||||
for(size_t i = 0; i < answers.size(); i++) {
|
||||
template <typename T> bool test() {
|
||||
std::string input = "0.156250000000000000000000000000000000000000 "
|
||||
"3.14159265358979323846264338327950288419716939937510 "
|
||||
"2.71828182845904523536028747135266249775724709369995";
|
||||
std::vector<T> answers = {T(0.15625), T(3.141592653589793),
|
||||
T(2.718281828459045)};
|
||||
const char *begin = input.data();
|
||||
const char *end = input.data() + input.size();
|
||||
for (size_t i = 0; i < answers.size(); i++) {
|
||||
T result_value;
|
||||
while((begin < end) && (std::isspace(*begin))) { begin++; }
|
||||
auto result = fast_float::from_chars(begin, end,
|
||||
result_value);
|
||||
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||
while ((begin < end) && (std::isspace(*begin))) {
|
||||
begin++;
|
||||
}
|
||||
auto result = fast_float::from_chars(begin, end, result_value);
|
||||
if (result.ec != std::errc() &&
|
||||
result.ec != std::errc::result_out_of_range) {
|
||||
printf("parsing %.*s\n", int(end - begin), begin);
|
||||
std::cerr << " I could not parse " << std::endl;
|
||||
return false;
|
||||
}
|
||||
if(result_value != answers[i]) {
|
||||
if (result_value != answers[i]) {
|
||||
printf("parsing %.*s\n", int(end - begin), begin);
|
||||
std::cerr << " Mismatch " << std::endl;
|
||||
std::cerr << " Expected " << answers[i] << std::endl;
|
||||
std::cerr << " Got " << result_value << std::endl;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
begin = result.ptr;
|
||||
}
|
||||
if(begin != end) {
|
||||
std::cerr << " bad ending " << std::endl;
|
||||
return false;
|
||||
if (begin != end) {
|
||||
std::cerr << " bad ending " << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -9,45 +9,53 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
|
||||
defined(sun) || defined(__sun)
|
||||
// Anything at all that is related to cygwin, msys and so forth will
|
||||
// always use this fallback because we cannot rely on it behaving as normal
|
||||
// gcc.
|
||||
#include <locale>
|
||||
// workaround for CYGWIN
|
||||
double cygwin_strtod_l(const char* start, char** end) {
|
||||
double d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
ss << start;
|
||||
ss >> d;
|
||||
if(ss.fail()) { *end = nullptr; }
|
||||
if(ss.eof()) { ss.clear(); }
|
||||
auto nread = ss.tellg();
|
||||
*end = const_cast<char*>(start) + nread;
|
||||
return d;
|
||||
double cygwin_strtod_l(const char *start, char **end) {
|
||||
double d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
ss << start;
|
||||
ss >> d;
|
||||
if (ss.fail()) {
|
||||
*end = nullptr;
|
||||
}
|
||||
if (ss.eof()) {
|
||||
ss.clear();
|
||||
}
|
||||
auto nread = ss.tellg();
|
||||
*end = const_cast<char *>(start) + nread;
|
||||
return d;
|
||||
}
|
||||
float cygwin_strtof_l(const char* start, char** end) {
|
||||
float d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
ss << start;
|
||||
ss >> d;
|
||||
if(ss.fail()) { *end = nullptr; }
|
||||
if(ss.eof()) { ss.clear(); }
|
||||
auto nread = ss.tellg();
|
||||
*end = const_cast<char*>(start) + nread;
|
||||
return d;
|
||||
float cygwin_strtof_l(const char *start, char **end) {
|
||||
float d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
ss << start;
|
||||
ss >> d;
|
||||
if (ss.fail()) {
|
||||
*end = nullptr;
|
||||
}
|
||||
if (ss.eof()) {
|
||||
ss.clear();
|
||||
}
|
||||
auto nread = ss.tellg();
|
||||
*end = const_cast<char *>(start) + nread;
|
||||
return d;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
std::pair<double, bool> strtod_from_string(const char *st) {
|
||||
double d;
|
||||
char *pr;
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
|
||||
d = cygwin_strtod_l(st, &pr);
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
|
||||
defined(sun) || defined(__sun)
|
||||
d = cygwin_strtod_l(st, &pr);
|
||||
#elif defined(_WIN32)
|
||||
static _locale_t c_locale = _create_locale(LC_ALL, "C");
|
||||
d = _strtod_l(st, &pr, c_locale);
|
||||
@ -65,7 +73,8 @@ std::pair<double, bool> strtod_from_string(const char *st) {
|
||||
std::pair<float, bool> strtof_from_string(char *st) {
|
||||
float d;
|
||||
char *pr;
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
|
||||
defined(sun) || defined(__sun)
|
||||
d = cygwin_strtof_l(st, &pr);
|
||||
#elif defined(_WIN32)
|
||||
static _locale_t c_locale = _create_locale(LC_ALL, "C");
|
||||
@ -103,9 +112,11 @@ bool tester() {
|
||||
std::pair<double, bool> expected_double =
|
||||
strtod_from_string(to_be_parsed.c_str());
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(to_be_parsed.data(), to_be_parsed.data() + to_be_parsed.size(), result_value);
|
||||
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||
auto result = fast_float::from_chars(
|
||||
to_be_parsed.data(), to_be_parsed.data() + to_be_parsed.size(),
|
||||
result_value);
|
||||
if (result.ec != std::errc() &&
|
||||
result.ec != std::errc::result_out_of_range) {
|
||||
std::cout << to_be_parsed << std::endl;
|
||||
std::cerr << " I could not parse " << std::endl;
|
||||
return false;
|
||||
|
||||
@ -14,7 +14,6 @@ template <typename T> char *to_string(T d, char *buffer) {
|
||||
return buffer + written;
|
||||
}
|
||||
|
||||
|
||||
static fast_float::value128 g_lehmer64_state;
|
||||
|
||||
/**
|
||||
@ -35,7 +34,8 @@ static inline void lehmer64_seed(uint64_t seed) {
|
||||
}
|
||||
|
||||
static inline uint64_t lehmer64() {
|
||||
fast_float::value128 v = fast_float::full_multiplication(g_lehmer64_state.low,UINT64_C(0xda942042e4dd58b5));
|
||||
fast_float::value128 v = fast_float::full_multiplication(
|
||||
g_lehmer64_state.low, UINT64_C(0xda942042e4dd58b5));
|
||||
v.high += g_lehmer64_state.high * UINT64_C(0xda942042e4dd58b5);
|
||||
g_lehmer64_state = v;
|
||||
return v.high;
|
||||
@ -59,10 +59,12 @@ void random_values(size_t N) {
|
||||
const char *string_end = to_string(v, buffer);
|
||||
double result_value;
|
||||
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
||||
// Starting with version 4.0 for fast_float, we return result_out_of_range if the
|
||||
// value is either too small (too close to zero) or too large (effectively infinity).
|
||||
// So std::errc::result_out_of_range is normal for well-formed input strings.
|
||||
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||
// Starting with version 4.0 for fast_float, we return result_out_of_range
|
||||
// if the value is either too small (too close to zero) or too large
|
||||
// (effectively infinity). So std::errc::result_out_of_range is normal for
|
||||
// well-formed input strings.
|
||||
if (result.ec != std::errc() &&
|
||||
result.ec != std::errc::result_out_of_range) {
|
||||
std::cerr << "parsing error ? " << buffer << std::endl;
|
||||
errors++;
|
||||
if (errors > 10) {
|
||||
@ -78,10 +80,10 @@ void random_values(size_t N) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
} else if(copysign(1,result_value) != copysign(1,v)) {
|
||||
} else if (copysign(1, result_value) != copysign(1, v)) {
|
||||
std::cerr << buffer << std::endl;
|
||||
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v
|
||||
<< std::endl;
|
||||
std::cerr << "I got " << std::hexfloat << result_value
|
||||
<< " but I was expecting " << v << std::endl;
|
||||
abort();
|
||||
} else if (result_value != v) {
|
||||
std::cerr << "no match ? " << buffer << std::endl;
|
||||
@ -100,7 +102,8 @@ void random_values(size_t N) {
|
||||
|
||||
int main() {
|
||||
errors = 0;
|
||||
size_t N = size_t(1) << (sizeof(size_t) * 4); // shift: 32 for 64bit, 16 for 32bit
|
||||
size_t N =
|
||||
size_t(1) << (sizeof(size_t) * 4); // shift: 32 for 64bit, 16 for 32bit
|
||||
random_values(N);
|
||||
if (errors == 0) {
|
||||
std::cout << std::endl;
|
||||
|
||||
@ -7,51 +7,61 @@
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
|
||||
defined(sun) || defined(__sun)
|
||||
// Anything at all that is related to cygwin, msys and so forth will
|
||||
// always use this fallback because we cannot rely on it behaving as normal
|
||||
// gcc.
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
// workaround for CYGWIN
|
||||
double cygwin_strtod_l(const char* start, char** end) {
|
||||
double d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
ss << start;
|
||||
ss >> d;
|
||||
if(ss.fail()) { *end = nullptr; }
|
||||
if(ss.eof()) { ss.clear(); }
|
||||
auto nread = ss.tellg();
|
||||
*end = const_cast<char*>(start) + nread;
|
||||
return d;
|
||||
double cygwin_strtod_l(const char *start, char **end) {
|
||||
double d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
ss << start;
|
||||
ss >> d;
|
||||
if (ss.fail()) {
|
||||
*end = nullptr;
|
||||
}
|
||||
if (ss.eof()) {
|
||||
ss.clear();
|
||||
}
|
||||
auto nread = ss.tellg();
|
||||
*end = const_cast<char *>(start) + nread;
|
||||
return d;
|
||||
}
|
||||
float cygwin_strtof_l(const char* start, char** end) {
|
||||
float d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
ss << start;
|
||||
ss >> d;
|
||||
if(ss.fail()) { *end = nullptr; }
|
||||
if(ss.eof()) { ss.clear(); }
|
||||
auto nread = ss.tellg();
|
||||
*end = const_cast<char*>(start) + nread;
|
||||
return d;
|
||||
float cygwin_strtof_l(const char *start, char **end) {
|
||||
float d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
ss << start;
|
||||
ss >> d;
|
||||
if (ss.fail()) {
|
||||
*end = nullptr;
|
||||
}
|
||||
if (ss.eof()) {
|
||||
ss.clear();
|
||||
}
|
||||
auto nread = ss.tellg();
|
||||
*end = const_cast<char *>(start) + nread;
|
||||
return d;
|
||||
}
|
||||
#endif
|
||||
|
||||
class RandomEngine {
|
||||
public:
|
||||
RandomEngine() = delete;
|
||||
RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed) {};
|
||||
RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed){};
|
||||
uint64_t next() {
|
||||
// Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h
|
||||
// Inspired from
|
||||
// https://github.com/lemire/testingRNG/blob/master/source/wyhash.h
|
||||
wyhash64_x_ += UINT64_C(0x60bee2bee120fc15);
|
||||
fast_float::value128 tmp = fast_float::full_multiplication(wyhash64_x_, UINT64_C(0xa3b195354a39b70d));
|
||||
fast_float::value128 tmp = fast_float::full_multiplication(
|
||||
wyhash64_x_, UINT64_C(0xa3b195354a39b70d));
|
||||
uint64_t m1 = (tmp.high) ^ tmp.low;
|
||||
tmp = fast_float::full_multiplication(m1, UINT64_C(0x1b03738712fad5c9));
|
||||
tmp = fast_float::full_multiplication(m1, UINT64_C(0x1b03738712fad5c9));
|
||||
uint64_t m2 = (tmp.high) ^ tmp.low;
|
||||
return m2;
|
||||
}
|
||||
@ -68,7 +78,7 @@ public:
|
||||
}*/
|
||||
uint64_t s = uint64_t(max - min + 1);
|
||||
uint64_t x = next();
|
||||
fast_float::value128 m = fast_float::full_multiplication(x, s);
|
||||
fast_float::value128 m = fast_float::full_multiplication(x, s);
|
||||
uint64_t l = m.low;
|
||||
if (l < s) {
|
||||
uint64_t t = -s % s;
|
||||
@ -92,8 +102,9 @@ size_t build_random_string(RandomEngine &rand, char *buffer) {
|
||||
buffer[pos++] = '-';
|
||||
}
|
||||
int number_of_digits = rand.next_ranged_int(1, 100);
|
||||
if(number_of_digits == 100) {
|
||||
// With low probability, we want to allow very long strings just to stress the system.
|
||||
if (number_of_digits == 100) {
|
||||
// With low probability, we want to allow very long strings just to stress
|
||||
// the system.
|
||||
number_of_digits = rand.next_ranged_int(1, 2000);
|
||||
}
|
||||
int location_of_decimal_separator = rand.next_ranged_int(1, number_of_digits);
|
||||
@ -103,7 +114,8 @@ size_t build_random_string(RandomEngine &rand, char *buffer) {
|
||||
}
|
||||
buffer[pos] = char(rand.next_digit() + '0');
|
||||
// We can have a leading zero only if location_of_decimal_separator = 1.
|
||||
while(i == 0 && 1 != size_t(location_of_decimal_separator) && buffer[pos] == '0') {
|
||||
while (i == 0 && 1 != size_t(location_of_decimal_separator) &&
|
||||
buffer[pos] == '0') {
|
||||
buffer[pos] = char(rand.next_digit() + '0');
|
||||
}
|
||||
pos++;
|
||||
@ -133,8 +145,9 @@ size_t build_random_string(RandomEngine &rand, char *buffer) {
|
||||
std::pair<double, bool> strtod_from_string(char *st) {
|
||||
double d;
|
||||
char *pr;
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
|
||||
d = cygwin_strtod_l(st, &pr);
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
|
||||
defined(sun) || defined(__sun)
|
||||
d = cygwin_strtod_l(st, &pr);
|
||||
#elif defined(_WIN32)
|
||||
static _locale_t c_locale = _create_locale(LC_ALL, "C");
|
||||
d = _strtod_l(st, &pr, c_locale);
|
||||
@ -152,8 +165,9 @@ std::pair<double, bool> strtod_from_string(char *st) {
|
||||
std::pair<float, bool> strtof_from_string(char *st) {
|
||||
float d;
|
||||
char *pr;
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
|
||||
d = cygwin_strtof_l(st, &pr);
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
|
||||
defined(sun) || defined(__sun)
|
||||
d = cygwin_strtof_l(st, &pr);
|
||||
#elif defined(_WIN32)
|
||||
static _locale_t c_locale = _create_locale(LC_ALL, "C");
|
||||
d = _strtof_l(st, &pr, c_locale);
|
||||
@ -176,14 +190,18 @@ bool tester(uint64_t seed, size_t volume) {
|
||||
char buffer[4096]; // large buffer (can't overflow)
|
||||
RandomEngine rand(seed);
|
||||
for (size_t i = 0; i < volume; i++) {
|
||||
if((i%100000) == 0) { std::cout << "."; std::cout.flush(); }
|
||||
if ((i % 100000) == 0) {
|
||||
std::cout << ".";
|
||||
std::cout.flush();
|
||||
}
|
||||
size_t length = build_random_string(rand, buffer);
|
||||
std::pair<double, bool> expected_double = strtod_from_string(buffer);
|
||||
if (expected_double.second) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(buffer, buffer + length, result_value);
|
||||
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||
if (result.ec != std::errc() &&
|
||||
result.ec != std::errc::result_out_of_range) {
|
||||
printf("parsing %.*s\n", int(length), buffer);
|
||||
std::cerr << " I could not parse " << std::endl;
|
||||
return false;
|
||||
@ -206,7 +224,8 @@ bool tester(uint64_t seed, size_t volume) {
|
||||
float result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(buffer, buffer + length, result_value);
|
||||
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||
if (result.ec != std::errc() &&
|
||||
result.ec != std::errc::result_out_of_range) {
|
||||
printf("parsing %.*s\n", int(length), buffer);
|
||||
std::cerr << " I could not parse " << std::endl;
|
||||
return false;
|
||||
@ -230,7 +249,8 @@ bool tester(uint64_t seed, size_t volume) {
|
||||
|
||||
int main() {
|
||||
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
|
||||
defined(sun) || defined(__sun)
|
||||
std::cout << "Warning: msys/cygwin or solaris detected." << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
#else
|
||||
|
||||
@ -75,7 +75,7 @@ bool eddelbuettel() {
|
||||
{true, 0.1},
|
||||
{true, 10},
|
||||
{true, 10},
|
||||
};
|
||||
};
|
||||
for (size_t i = 0; i < inputs.size(); i++) {
|
||||
const std::string &input = inputs[i];
|
||||
std::pair<bool, double> expected = expected_results[i];
|
||||
@ -121,7 +121,8 @@ bool eddelbuettel() {
|
||||
if (std::isnan(result) && std::isnan(expected.second)) {
|
||||
continue;
|
||||
}
|
||||
std::cout << "results do not match. Expected "<< expected.second << std::endl;
|
||||
std::cout << "results do not match. Expected " << expected.second
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,51 +7,61 @@
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
|
||||
defined(sun) || defined(__sun)
|
||||
// Anything at all that is related to cygwin, msys and so forth will
|
||||
// always use this fallback because we cannot rely on it behaving as normal
|
||||
// gcc.
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
// workaround for CYGWIN
|
||||
double cygwin_strtod_l(const char* start, char** end) {
|
||||
double d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
ss << start;
|
||||
ss >> d;
|
||||
if(ss.fail()) { *end = nullptr; }
|
||||
if(ss.eof()) { ss.clear(); }
|
||||
auto nread = ss.tellg();
|
||||
*end = const_cast<char*>(start) + nread;
|
||||
return d;
|
||||
double cygwin_strtod_l(const char *start, char **end) {
|
||||
double d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
ss << start;
|
||||
ss >> d;
|
||||
if (ss.fail()) {
|
||||
*end = nullptr;
|
||||
}
|
||||
if (ss.eof()) {
|
||||
ss.clear();
|
||||
}
|
||||
auto nread = ss.tellg();
|
||||
*end = const_cast<char *>(start) + nread;
|
||||
return d;
|
||||
}
|
||||
float cygwin_strtof_l(const char* start, char** end) {
|
||||
float d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
ss << start;
|
||||
ss >> d;
|
||||
if(ss.fail()) { *end = nullptr; }
|
||||
if(ss.eof()) { ss.clear(); }
|
||||
auto nread = ss.tellg();
|
||||
*end = const_cast<char*>(start) + nread;
|
||||
return d;
|
||||
float cygwin_strtof_l(const char *start, char **end) {
|
||||
float d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
ss << start;
|
||||
ss >> d;
|
||||
if (ss.fail()) {
|
||||
*end = nullptr;
|
||||
}
|
||||
if (ss.eof()) {
|
||||
ss.clear();
|
||||
}
|
||||
auto nread = ss.tellg();
|
||||
*end = const_cast<char *>(start) + nread;
|
||||
return d;
|
||||
}
|
||||
#endif
|
||||
|
||||
class RandomEngine {
|
||||
public:
|
||||
RandomEngine() = delete;
|
||||
RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed) { };
|
||||
RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed){};
|
||||
uint64_t next() {
|
||||
// Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h
|
||||
// Inspired from
|
||||
// https://github.com/lemire/testingRNG/blob/master/source/wyhash.h
|
||||
wyhash64_x_ += UINT64_C(0x60bee2bee120fc15);
|
||||
fast_float::value128 tmp = fast_float::full_multiplication(wyhash64_x_, UINT64_C(0xa3b195354a39b70d));
|
||||
fast_float::value128 tmp = fast_float::full_multiplication(
|
||||
wyhash64_x_, UINT64_C(0xa3b195354a39b70d));
|
||||
uint64_t m1 = (tmp.high) ^ tmp.low;
|
||||
tmp = fast_float::full_multiplication(m1, UINT64_C(0x1b03738712fad5c9));
|
||||
tmp = fast_float::full_multiplication(m1, UINT64_C(0x1b03738712fad5c9));
|
||||
uint64_t m2 = (tmp.high) ^ tmp.low;
|
||||
return m2;
|
||||
}
|
||||
@ -68,7 +78,7 @@ public:
|
||||
}*/
|
||||
uint64_t s = uint64_t(max - min + 1);
|
||||
uint64_t x = next();
|
||||
fast_float::value128 m = fast_float::full_multiplication(x, s);
|
||||
fast_float::value128 m = fast_float::full_multiplication(x, s);
|
||||
uint64_t l = m.low;
|
||||
if (l < s) {
|
||||
uint64_t t = -s % s;
|
||||
@ -99,7 +109,8 @@ size_t build_random_string(RandomEngine &rand, char *buffer) {
|
||||
}
|
||||
buffer[pos] = char(rand.next_digit() + '0');
|
||||
// We can have a leading zero only if location_of_decimal_separator = 1.
|
||||
while(i == 0 && 1 != size_t(location_of_decimal_separator) && buffer[pos] == '0') {
|
||||
while (i == 0 && 1 != size_t(location_of_decimal_separator) &&
|
||||
buffer[pos] == '0') {
|
||||
buffer[pos] = char(rand.next_digit() + '0');
|
||||
}
|
||||
pos++;
|
||||
@ -129,8 +140,9 @@ size_t build_random_string(RandomEngine &rand, char *buffer) {
|
||||
std::pair<double, bool> strtod_from_string(char *st) {
|
||||
double d;
|
||||
char *pr;
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
|
||||
d = cygwin_strtod_l(st, &pr);
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
|
||||
defined(sun) || defined(__sun)
|
||||
d = cygwin_strtod_l(st, &pr);
|
||||
#elif defined(_WIN32)
|
||||
static _locale_t c_locale = _create_locale(LC_ALL, "C");
|
||||
d = _strtod_l(st, &pr, c_locale);
|
||||
@ -148,8 +160,9 @@ std::pair<double, bool> strtod_from_string(char *st) {
|
||||
std::pair<float, bool> strtof_from_string(char *st) {
|
||||
float d;
|
||||
char *pr;
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
|
||||
d = cygwin_strtof_l(st, &pr);
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
|
||||
defined(sun) || defined(__sun)
|
||||
d = cygwin_strtof_l(st, &pr);
|
||||
#elif defined(_WIN32)
|
||||
static _locale_t c_locale = _create_locale(LC_ALL, "C");
|
||||
d = _strtof_l(st, &pr, c_locale);
|
||||
@ -172,14 +185,18 @@ bool tester(uint64_t seed, size_t volume) {
|
||||
char buffer[4096]; // large buffer (can't overflow)
|
||||
RandomEngine rand(seed);
|
||||
for (size_t i = 0; i < volume; i++) {
|
||||
if((i%1000000) == 0) { std::cout << "."; std::cout.flush(); }
|
||||
if ((i % 1000000) == 0) {
|
||||
std::cout << ".";
|
||||
std::cout.flush();
|
||||
}
|
||||
size_t length = build_random_string(rand, buffer);
|
||||
std::pair<double, bool> expected_double = strtod_from_string(buffer);
|
||||
if (expected_double.second) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(buffer, buffer + length, result_value);
|
||||
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||
if (result.ec != std::errc() &&
|
||||
result.ec != std::errc::result_out_of_range) {
|
||||
printf("parsing %.*s\n", int(length), buffer);
|
||||
std::cerr << " I could not parse " << std::endl;
|
||||
return false;
|
||||
@ -202,7 +219,8 @@ bool tester(uint64_t seed, size_t volume) {
|
||||
float result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(buffer, buffer + length, result_value);
|
||||
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||
if (result.ec != std::errc() &&
|
||||
result.ec != std::errc::result_out_of_range) {
|
||||
printf("parsing %.*s\n", int(length), buffer);
|
||||
std::cerr << " I could not parse " << std::endl;
|
||||
return false;
|
||||
@ -225,8 +243,12 @@ bool tester(uint64_t seed, size_t volume) {
|
||||
}
|
||||
|
||||
int main() {
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
|
||||
std::cout << "Warning: msys/cygwin detected. This particular test is likely to generate false failures due to our reliance on the underlying runtime library." << std::endl;
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
|
||||
defined(sun) || defined(__sun)
|
||||
std::cout << "Warning: msys/cygwin detected. This particular test is likely "
|
||||
"to generate false failures due to our reliance on the "
|
||||
"underlying runtime library."
|
||||
<< std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
#else
|
||||
if (tester(1234344, 100000000)) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user