This commit is contained in:
Daniel Lemire 2024-08-14 09:57:47 -04:00
parent 8f3dae6b9f
commit 5ad6aae0b1
45 changed files with 4492 additions and 2848 deletions

0
clang-format-ignore.txt Normal file
View File

View File

@ -5,7 +5,7 @@
fast_float::chars_format arbitrary_format(FuzzedDataProvider &fdp) { fast_float::chars_format arbitrary_format(FuzzedDataProvider &fdp) {
using fast_float::chars_format; using fast_float::chars_format;
switch (fdp.ConsumeIntegralInRange<int>(0,3)) { switch (fdp.ConsumeIntegralInRange<int>(0, 3)) {
case 0: case 0:
return chars_format::scientific; return chars_format::scientific;
break; break;
@ -24,15 +24,15 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
fast_float::chars_format format = arbitrary_format(fdp); fast_float::chars_format format = arbitrary_format(fdp);
double result_d = 0.0; double result_d = 0.0;
std::string input_d = fdp.ConsumeRandomLengthString(128); std::string input_d = fdp.ConsumeRandomLengthString(128);
auto answer = auto answer = fast_float::from_chars(
fast_float::from_chars(input_d.data(), input_d.data() + input_d.size(), result_d, format); input_d.data(), input_d.data() + input_d.size(), result_d, format);
std::string input_f = fdp.ConsumeRandomLengthString(128); std::string input_f = fdp.ConsumeRandomLengthString(128);
float result_f = 0.0; float result_f = 0.0;
answer = answer = fast_float::from_chars(
fast_float::from_chars(input_f.data(), input_f.data() + input_f.size(), result_f, format); input_f.data(), input_f.data() + input_f.size(), result_f, format);
int result_i = 0; int result_i = 0;
std::string input_i = fdp.ConsumeRandomLengthString(128); std::string input_i = fdp.ConsumeRandomLengthString(128);
answer = answer = fast_float::from_chars(input_i.data(),
fast_float::from_chars(input_i.data(), input_i.data() + input_i.size(), result_i); input_i.data() + input_i.size(), result_i);
return 0; return 0;
} }

View File

@ -20,8 +20,7 @@
namespace fast_float { namespace fast_float {
template <typename UC> template <typename UC> fastfloat_really_inline constexpr bool has_simd_opt() {
fastfloat_really_inline constexpr bool has_simd_opt() {
#ifdef FASTFLOAT_HAS_SIMD #ifdef FASTFLOAT_HAS_SIMD
return std::is_same<UC, char16_t>::value; return std::is_same<UC, char16_t>::value;
#else #else
@ -37,24 +36,20 @@ fastfloat_really_inline constexpr bool is_integer(UC c) noexcept {
} }
fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) { fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
return (val & 0xFF00000000000000) >> 56 return (val & 0xFF00000000000000) >> 56 | (val & 0x00FF000000000000) >> 40 |
| (val & 0x00FF000000000000) >> 40 (val & 0x0000FF0000000000) >> 24 | (val & 0x000000FF00000000) >> 8 |
| (val & 0x0000FF0000000000) >> 24 (val & 0x00000000FF000000) << 8 | (val & 0x0000000000FF0000) << 24 |
| (val & 0x000000FF00000000) >> 8 (val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56;
| (val & 0x00000000FF000000) << 8
| (val & 0x0000000000FF0000) << 24
| (val & 0x000000000000FF00) << 40
| (val & 0x00000000000000FF) << 56;
} }
// Read 8 UC into a u64. Truncates UC if not char. // Read 8 UC into a u64. Truncates UC if not char.
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
uint64_t read8_to_u64(const UC *chars) { read8_to_u64(const UC *chars) {
if (cpp20_and_in_constexpr() || !std::is_same<UC, char>::value) { if (cpp20_and_in_constexpr() || !std::is_same<UC, char>::value) {
uint64_t val = 0; uint64_t val = 0;
for(int i = 0; i < 8; ++i) { for (int i = 0; i < 8; ++i) {
val |= uint64_t(uint8_t(*chars)) << (i*8); val |= uint64_t(uint8_t(*chars)) << (i * 8);
++chars; ++chars;
} }
return val; return val;
@ -70,44 +65,41 @@ uint64_t read8_to_u64(const UC *chars) {
#ifdef FASTFLOAT_SSE2 #ifdef FASTFLOAT_SSE2
fastfloat_really_inline fastfloat_really_inline uint64_t simd_read8_to_u64(const __m128i data) {
uint64_t simd_read8_to_u64(const __m128i data) { FASTFLOAT_SIMD_DISABLE_WARNINGS
FASTFLOAT_SIMD_DISABLE_WARNINGS
const __m128i packed = _mm_packus_epi16(data, data); const __m128i packed = _mm_packus_epi16(data, data);
#ifdef FASTFLOAT_64BIT #ifdef FASTFLOAT_64BIT
return uint64_t(_mm_cvtsi128_si64(packed)); return uint64_t(_mm_cvtsi128_si64(packed));
#else #else
uint64_t value; uint64_t value;
// Visual Studio + older versions of GCC don't support _mm_storeu_si64 // Visual Studio + older versions of GCC don't support _mm_storeu_si64
_mm_storel_epi64(reinterpret_cast<__m128i*>(&value), packed); _mm_storel_epi64(reinterpret_cast<__m128i *>(&value), packed);
return value; return value;
#endif #endif
FASTFLOAT_SIMD_RESTORE_WARNINGS FASTFLOAT_SIMD_RESTORE_WARNINGS
} }
fastfloat_really_inline fastfloat_really_inline uint64_t simd_read8_to_u64(const char16_t *chars) {
uint64_t simd_read8_to_u64(const char16_t* chars) { FASTFLOAT_SIMD_DISABLE_WARNINGS
FASTFLOAT_SIMD_DISABLE_WARNINGS return simd_read8_to_u64(
return simd_read8_to_u64(_mm_loadu_si128(reinterpret_cast<const __m128i*>(chars))); _mm_loadu_si128(reinterpret_cast<const __m128i *>(chars)));
FASTFLOAT_SIMD_RESTORE_WARNINGS FASTFLOAT_SIMD_RESTORE_WARNINGS
} }
#elif defined(FASTFLOAT_NEON) #elif defined(FASTFLOAT_NEON)
fastfloat_really_inline uint64_t simd_read8_to_u64(const uint16x8_t data) {
fastfloat_really_inline FASTFLOAT_SIMD_DISABLE_WARNINGS
uint64_t simd_read8_to_u64(const uint16x8_t data) {
FASTFLOAT_SIMD_DISABLE_WARNINGS
uint8x8_t utf8_packed = vmovn_u16(data); uint8x8_t utf8_packed = vmovn_u16(data);
return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0); return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0);
FASTFLOAT_SIMD_RESTORE_WARNINGS FASTFLOAT_SIMD_RESTORE_WARNINGS
} }
fastfloat_really_inline fastfloat_really_inline uint64_t simd_read8_to_u64(const char16_t *chars) {
uint64_t simd_read8_to_u64(const char16_t* chars) { FASTFLOAT_SIMD_DISABLE_WARNINGS
FASTFLOAT_SIMD_DISABLE_WARNINGS return simd_read8_to_u64(
return simd_read8_to_u64(vld1q_u16(reinterpret_cast<const uint16_t*>(chars))); vld1q_u16(reinterpret_cast<const uint16_t *>(chars)));
FASTFLOAT_SIMD_RESTORE_WARNINGS FASTFLOAT_SIMD_RESTORE_WARNINGS
} }
#endif // FASTFLOAT_SSE2 #endif // FASTFLOAT_SSE2
@ -119,13 +111,13 @@ template <typename UC>
template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0> template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
#endif #endif
// dummy for compile // dummy for compile
uint64_t simd_read8_to_u64(UC const*) { uint64_t simd_read8_to_u64(UC const *) {
return 0; return 0;
} }
// credit @aqrit // credit @aqrit
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t
uint32_t parse_eight_digits_unrolled(uint64_t val) { parse_eight_digits_unrolled(uint64_t val) {
const uint64_t mask = 0x000000FF000000FF; const uint64_t mask = 0x000000FF000000FF;
const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 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); return uint32_t(val);
} }
// Call this if chars are definitely 8 digits. // Call this if chars are definitely 8 digits.
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint32_t
uint32_t parse_eight_digits_unrolled(UC const * chars) noexcept { parse_eight_digits_unrolled(UC const *chars) noexcept {
if (cpp20_and_in_constexpr() || !has_simd_opt<UC>()) { 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(read8_to_u64(chars)); // truncation okay
} }
return parse_eight_digits_unrolled(simd_read8_to_u64(chars)); return parse_eight_digits_unrolled(simd_read8_to_u64(chars));
} }
// credit @aqrit // credit @aqrit
fastfloat_really_inline constexpr bool is_made_of_eight_digits_fast(uint64_t val) noexcept { fastfloat_really_inline constexpr bool
is_made_of_eight_digits_fast(uint64_t val) noexcept {
return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
0x8080808080808080)); 0x8080808080808080));
} }
#ifdef FASTFLOAT_HAS_SIMD #ifdef FASTFLOAT_HAS_SIMD
// Call this if chars might not be 8 digits. // 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()) // Using this style (instead of is_made_of_eight_digits_fast() then
// ensures we don't load SIMD registers twice. // parse_eight_digits_unrolled()) ensures we don't load SIMD registers twice.
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
bool simd_parse_if_eight_digits_unrolled(const char16_t* chars, uint64_t& i) noexcept { simd_parse_if_eight_digits_unrolled(const char16_t *chars,
uint64_t &i) noexcept {
if (cpp20_and_in_constexpr()) { if (cpp20_and_in_constexpr()) {
return false; return false;
} }
#ifdef FASTFLOAT_SSE2 #ifdef FASTFLOAT_SSE2
FASTFLOAT_SIMD_DISABLE_WARNINGS FASTFLOAT_SIMD_DISABLE_WARNINGS
const __m128i data = _mm_loadu_si128(reinterpret_cast<const __m128i*>(chars)); const __m128i data =
_mm_loadu_si128(reinterpret_cast<const __m128i *>(chars));
// (x - '0') <= 9 // (x - '0') <= 9
// http://0x80.pl/articles/simd-parsing-int-sequences.html // http://0x80.pl/articles/simd-parsing-int-sequences.html
@ -176,12 +168,12 @@ FASTFLOAT_SIMD_DISABLE_WARNINGS
if (_mm_movemask_epi8(t1) == 0) { if (_mm_movemask_epi8(t1) == 0) {
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data)); i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
return true; return true;
} } else
else return false; return false;
FASTFLOAT_SIMD_RESTORE_WARNINGS FASTFLOAT_SIMD_RESTORE_WARNINGS
#elif defined(FASTFLOAT_NEON) #elif defined(FASTFLOAT_NEON)
FASTFLOAT_SIMD_DISABLE_WARNINGS FASTFLOAT_SIMD_DISABLE_WARNINGS
const uint16x8_t data = vld1q_u16(reinterpret_cast<const uint16_t*>(chars)); const uint16x8_t data = vld1q_u16(reinterpret_cast<const uint16_t *>(chars));
// (x - '0') <= 9 // (x - '0') <= 9
// http://0x80.pl/articles/simd-parsing-int-sequences.html // http://0x80.pl/articles/simd-parsing-int-sequences.html
@ -191,11 +183,12 @@ FASTFLOAT_SIMD_DISABLE_WARNINGS
if (vminvq_u16(mask) == 0xFFFF) { if (vminvq_u16(mask) == 0xFFFF) {
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data)); i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
return true; return true;
} } else
else return false; return false;
FASTFLOAT_SIMD_RESTORE_WARNINGS FASTFLOAT_SIMD_RESTORE_WARNINGS
#else #else
(void)chars; (void)i; (void)chars;
(void)i;
return false; return false;
#endif // FASTFLOAT_SSE2 #endif // FASTFLOAT_SSE2
} }
@ -209,27 +202,32 @@ template <typename UC>
template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0> template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
#endif #endif
// dummy for compile // 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; return 0;
} }
template <typename UC, FASTFLOAT_ENABLE_IF(!std::is_same<UC, char>::value) = 0> template <typename UC, FASTFLOAT_ENABLE_IF(!std::is_same<UC, char>::value) = 0>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
void loop_parse_if_eight_digits(const UC*& p, const UC* const pend, uint64_t& i) { loop_parse_if_eight_digits(const UC *&p, const UC *const pend, uint64_t &i) {
if (!has_simd_opt<UC>()) { if (!has_simd_opt<UC>()) {
return; 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; p += 8;
} }
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
void loop_parse_if_eight_digits(const char*& p, const char* const pend, uint64_t& i) { 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. // 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))) { while ((std::distance(p, pend) >= 8) &&
i = i * 100000000 + parse_eight_digits_unrolled(read8_to_u64(p)); // in rare cases, this will overflow, but that's ok 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; p += 8;
} }
} }
@ -253,11 +251,10 @@ enum class parse_error {
missing_exponential_part, missing_exponential_part,
}; };
template <typename UC> template <typename UC> struct parsed_number_string_t {
struct parsed_number_string_t {
int64_t exponent{0}; int64_t exponent{0};
uint64_t mantissa{0}; uint64_t mantissa{0};
UC const * lastmatch{nullptr}; UC const *lastmatch{nullptr};
bool negative{false}; bool negative{false};
bool valid{false}; bool valid{false};
bool too_many_digits{false}; bool too_many_digits{false};
@ -272,7 +269,7 @@ using parsed_number_string = parsed_number_string_t<char>;
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC> fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
report_parse_error(UC const* p, parse_error error) { report_parse_error(UC const *p, parse_error error) {
parsed_number_string_t<UC> answer; parsed_number_string_t<UC> answer;
answer.valid = false; answer.valid = false;
answer.lastmatch = p; 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 // Assuming that you use no more than 19 digits, this will
// parse an ASCII string. // parse an ASCII string.
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, parse_options_t<UC> options) noexcept { parse_number_string(UC const *p, UC const *pend,
parse_options_t<UC> options) noexcept {
chars_format const fmt = options.format; chars_format const fmt = options.format;
UC const decimal_point = options.decimal_point; UC const decimal_point = options.decimal_point;
@ -304,15 +302,19 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
} }
if (fmt & FASTFLOAT_JSONFMT) { if (fmt & FASTFLOAT_JSONFMT) {
if (!is_integer(*p)) { // a sign must be followed by an integer 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 { } else {
if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot if (!is_integer(*p) &&
return report_parse_error<UC>(p, parse_error::missing_integer_or_dot_after_sign); (*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) 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 // a multiplication by 10 is cheaper than an arbitrary integer
// multiplication // multiplication
i = 10 * i + 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; ++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); int64_t digit_count = int64_t(end_of_integer_part - start_digits);
answer.integer = span<const UC>(start_digits, size_t(digit_count)); answer.integer = span<const UC>(start_digits, size_t(digit_count));
if (fmt & FASTFLOAT_JSONFMT) { 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); const bool has_decimal_point = (p != pend) && (*p == decimal_point);
if (has_decimal_point) { if (has_decimal_point) {
++p; ++p;
UC const * before = p; UC const *before = p;
// can occur at most twice without overflowing, but let it occur more, since // can occur at most twice without overflowing, but let it occur more, since
// for integers with many digits, digit parsing is the primary bottleneck. // for integers with many digits, digit parsing is the primary bottleneck.
loop_parse_if_eight_digits(p, pend, i); 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) { if (fmt & FASTFLOAT_JSONFMT) {
// at least 1 digit in fractional part // at least 1 digit in fractional part
if (has_decimal_point && exponent == 0) { 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); return report_parse_error<UC>(p, parse_error::no_digits_in_mantissa);
} }
int64_t exp_number = 0; // explicit exponential part int64_t exp_number = 0; // explicit exponential part
if ( ((fmt & chars_format::scientific) && if (((fmt & chars_format::scientific) && (p != pend) &&
(p != pend) && ((UC('e') == *p) || (UC('E') == *p))) ||
((UC('e') == *p) || (UC('E') == *p))) ((fmt & FASTFLOAT_FORTRANFMT) && (p != pend) &&
|| ((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) ||
((fmt & FASTFLOAT_FORTRANFMT) && (UC('D') == *p)))) {
(p != pend) && UC const *location_of_e = p;
((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) || (UC('D') == *p)))) { if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) ||
UC const * location_of_e = p; (UC('D') == *p)) {
if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) || (UC('D') == *p)) {
++p; ++p;
} }
bool neg_exp = false; bool neg_exp = false;
if ((p != pend) && (UC('-') == *p)) { if ((p != pend) && (UC('-') == *p)) {
neg_exp = true; neg_exp = true;
++p; ++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; ++p;
} }
if ((p == pend) || !is_integer(*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 // The exponential part is invalid for scientific notation, so it must
// be a trailing token for fixed notation. However, fixed notation is // be a trailing token for fixed notation. However, fixed notation is
// disabled, so report a scientific notation error. // 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; ++p;
} }
if(neg_exp) { exp_number = - exp_number; } if (neg_exp) {
exp_number = -exp_number;
}
exponent += exp_number; exponent += exp_number;
} }
} else { } 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 have to handle the case where we have 0.0000somenumber.
// We need to be mindful of the case where we only have zeroes... // We need to be mindful of the case where we only have zeroes...
// E.g., 0.000000000...000. // E.g., 0.000000000...000.
UC const * start = start_digits; UC const *start = start_digits;
while ((start != pend) && (*start == UC('0') || *start == decimal_point)) { while ((start != pend) && (*start == UC('0') || *start == decimal_point)) {
if(*start == UC('0')) { digit_count --; } if (*start == UC('0')) {
digit_count--;
}
start++; 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. // pre-tokenized spans from above.
i = 0; i = 0;
p = answer.integer.ptr; p = answer.integer.ptr;
UC const* int_end = p + answer.integer.len(); UC const *int_end = p + answer.integer.len();
const uint64_t minimal_nineteen_digit_integer{ 1000000000000000000 }; const uint64_t minimal_nineteen_digit_integer{1000000000000000000};
while ((i < minimal_nineteen_digit_integer) && (p != int_end)) { while ((i < minimal_nineteen_digit_integer) && (p != int_end)) {
i = i * 10 + uint64_t(*p - UC('0')); i = i * 10 + uint64_t(*p - UC('0'));
++p; ++p;
} }
if (i >= minimal_nineteen_digit_integer) { // We have a big integers if (i >= minimal_nineteen_digit_integer) { // We have a big integers
exponent = end_of_integer_part - p + exp_number; 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; 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)) { while ((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
i = i * 10 + uint64_t(*p - UC('0')); i = i * 10 + uint64_t(*p - UC('0'));
++p; ++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> template <typename T, typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_result_t<UC> parse_int_string(UC const* p, UC const* pend, T& value, int base) { parse_int_string(UC const *p, UC const *pend, T &value, int base) {
from_chars_result_t<UC> answer; from_chars_result_t<UC> answer;
UC const* const first = p; UC const *const first = p;
bool negative = (*p == UC('-')); bool negative = (*p == UC('-'));
if (!std::is_signed<T>::value && negative) { 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; ++p;
} }
UC const* const start_num = p; UC const *const start_num = p;
while (p!= pend && *p == UC('0')) { while (p != pend && *p == UC('0')) {
++p; ++p;
} }
const bool has_leading_zeros = p > start_num; const bool has_leading_zeros = p > start_num;
UC const* const start_digits = p; UC const *const start_digits = p;
uint64_t i = 0; uint64_t i = 0;
if (base == 10) { if (base == 10) {
@ -511,8 +520,7 @@ from_chars_result_t<UC> parse_int_string(UC const* p, UC const* pend, T& value,
value = 0; value = 0;
answer.ec = std::errc(); answer.ec = std::errc();
answer.ptr = p; answer.ptr = p;
} } else {
else {
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
answer.ptr = first; answer.ptr = first;
} }
@ -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; answer.ec = std::errc::result_out_of_range;
return answer; return answer;
} }
// this check can be eliminated for all other types, but they will all require 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)) { if (digit_count == max_digits && i < min_safe_u64(base)) {
answer.ec = std::errc::result_out_of_range; answer.ec = std::errc::result_out_of_range;
return answer; return answer;
@ -544,18 +553,22 @@ from_chars_result_t<UC> parse_int_string(UC const* p, UC const* pend, T& value,
if (negative) { if (negative) {
#ifdef FASTFLOAT_VISUAL_STUDIO #ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(push) #pragma warning(push)
#pragma warning(disable: 4146) #pragma warning(disable : 4146)
#endif #endif
// this weird workaround is required because: // 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 // - 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) // this is always optimized into a neg instruction (note: T is an integer
value = T(-std::numeric_limits<T>::max() - T(i - uint64_t(std::numeric_limits<T>::max()))); // type)
value = T(-std::numeric_limits<T>::max() -
T(i - uint64_t(std::numeric_limits<T>::max())));
#ifdef FASTFLOAT_VISUAL_STUDIO #ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(pop) #pragma warning(pop)
#endif #endif
} else {
value = T(i);
} }
else { value = T(i); }
answer.ec = std::errc(); answer.ec = std::errc();
return answer; return answer;

View File

@ -37,8 +37,7 @@ constexpr size_t bigint_limbs = bigint_bits / limb_bits;
// vector-like type that is allocated on the stack. the entire // vector-like type that is allocated on the stack. the entire
// buffer is pre-allocated, and only the length changes. // buffer is pre-allocated, and only the length changes.
template <uint16_t size> template <uint16_t size> struct stackvec {
struct stackvec {
limb data[size]; limb data[size];
// we never need more than 150 limbs // we never need more than 150 limbs
uint16_t length{0}; uint16_t length{0};
@ -54,16 +53,16 @@ struct stackvec {
FASTFLOAT_ASSERT(try_extend(s)); 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); FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index]; return data[index];
} }
FASTFLOAT_CONSTEXPR14 const limb& operator[](size_t index) const noexcept { FASTFLOAT_CONSTEXPR14 const limb &operator[](size_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index]; return data[index];
} }
// index from the end of the container // index from the end of the container
FASTFLOAT_CONSTEXPR14 const limb& rindex(size_t index) const noexcept { FASTFLOAT_CONSTEXPR14 const limb &rindex(size_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
size_t rindex = length - index - 1; size_t rindex = length - index - 1;
return data[rindex]; return data[rindex];
@ -73,15 +72,9 @@ struct stackvec {
FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept { FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept {
length = uint16_t(len); length = uint16_t(len);
} }
constexpr size_t len() const noexcept { constexpr size_t len() const noexcept { return length; }
return length; constexpr bool is_empty() const noexcept { return length == 0; }
} constexpr size_t capacity() const noexcept { return size; }
constexpr bool is_empty() const noexcept {
return length == 0;
}
constexpr size_t capacity() const noexcept {
return size;
}
// append item to vector, without bounds checking // append item to vector, without bounds checking
FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept { FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept {
data[length] = value; data[length] = value;
@ -98,7 +91,7 @@ struct stackvec {
} }
// add items to the vector, from a span, without bounds checking // add items to the vector, from a span, without bounds checking
FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept { FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept {
limb* ptr = data + length; limb *ptr = data + length;
std::copy_n(s.ptr, s.len(), ptr); std::copy_n(s.ptr, s.len(), ptr);
set_len(len() + s.len()); set_len(len() + s.len());
} }
@ -118,8 +111,8 @@ struct stackvec {
void resize_unchecked(size_t new_len, limb value) noexcept { void resize_unchecked(size_t new_len, limb value) noexcept {
if (new_len > len()) { if (new_len > len()) {
size_t count = new_len - len(); size_t count = new_len - len();
limb* first = data + len(); limb *first = data + len();
limb* last = first + count; limb *last = first + count;
::std::fill(first, last, value); ::std::fill(first, last, value);
set_len(new_len); set_len(new_len);
} else { } else {
@ -155,21 +148,21 @@ struct stackvec {
} }
}; };
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t
uint64_t empty_hi64(bool& truncated) noexcept { empty_hi64(bool &truncated) noexcept {
truncated = false; truncated = false;
return 0; return 0;
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
uint64_t uint64_hi64(uint64_t r0, bool& truncated) noexcept { uint64_hi64(uint64_t r0, bool &truncated) noexcept {
truncated = false; truncated = false;
int shl = leading_zeroes(r0); int shl = leading_zeroes(r0);
return r0 << shl; return r0 << shl;
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
uint64_t uint64_hi64(uint64_t r0, uint64_t r1, bool& truncated) noexcept { uint64_hi64(uint64_t r0, uint64_t r1, bool &truncated) noexcept {
int shl = leading_zeroes(r0); int shl = leading_zeroes(r0);
if (shl == 0) { if (shl == 0) {
truncated = r1 != 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 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
uint64_t uint32_hi64(uint32_t r0, bool& truncated) noexcept { uint32_hi64(uint32_t r0, bool &truncated) noexcept {
return uint64_hi64(r0, truncated); return uint64_hi64(r0, truncated);
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
uint64_t uint32_hi64(uint32_t r0, uint32_t r1, bool& truncated) noexcept { uint32_hi64(uint32_t r0, uint32_t r1, bool &truncated) noexcept {
uint64_t x0 = r0; uint64_t x0 = r0;
uint64_t x1 = r1; uint64_t x1 = r1;
return uint64_hi64((x0 << 32) | x1, truncated); return uint64_hi64((x0 << 32) | x1, truncated);
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
uint64_t uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool& truncated) noexcept { uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool &truncated) noexcept {
uint64_t x0 = r0; uint64_t x0 = r0;
uint64_t x1 = r1; uint64_t x1 = r1;
uint64_t x2 = r2; 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 want an efficient operation. for msvc, where
// we don't have built-in intrinsics, this is still // we don't have built-in intrinsics, this is still
// pretty fast. // pretty fast.
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb
limb scalar_add(limb x, limb y, bool& overflow) noexcept { scalar_add(limb x, limb y, bool &overflow) noexcept {
limb z; limb z;
// gcc and clang // gcc and clang
#if defined(__has_builtin) #if defined(__has_builtin)
#if __has_builtin(__builtin_add_overflow) #if __has_builtin(__builtin_add_overflow)
if (!cpp20_and_in_constexpr()) { if (!cpp20_and_in_constexpr()) {
overflow = __builtin_add_overflow(x, y, &z); overflow = __builtin_add_overflow(x, y, &z);
return z; return z;
} }
#endif #endif
#endif #endif
// generic, this still optimizes correctly on MSVC. // generic, this still optimizes correctly on MSVC.
@ -225,15 +218,15 @@ limb scalar_add(limb x, limb y, bool& overflow) noexcept {
} }
// multiply two small integers, getting both the high and low bits. // multiply two small integers, getting both the high and low bits.
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb
limb scalar_mul(limb x, limb y, limb& carry) noexcept { scalar_mul(limb x, limb y, limb &carry) noexcept {
#ifdef FASTFLOAT_64BIT_LIMB #ifdef FASTFLOAT_64BIT_LIMB
#if defined(__SIZEOF_INT128__) #if defined(__SIZEOF_INT128__)
// GCC and clang both define it as an extension. // GCC and clang both define it as an extension.
__uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry); __uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry);
carry = limb(z >> limb_bits); carry = limb(z >> limb_bits);
return limb(z); return limb(z);
#else #else
// fallback, no native 128-bit integer multiplication with carry. // fallback, no native 128-bit integer multiplication with carry.
// on msvc, this optimizes identically, somehow. // on msvc, this optimizes identically, somehow.
value128 z = full_multiplication(x, y); value128 z = full_multiplication(x, y);
@ -242,7 +235,7 @@ limb scalar_mul(limb x, limb y, limb& carry) noexcept {
z.high += uint64_t(overflow); // cannot overflow z.high += uint64_t(overflow); // cannot overflow
carry = z.high; carry = z.high;
return z.low; return z.low;
#endif #endif
#else #else
uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry); uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry);
carry = limb(z >> limb_bits); 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. // add scalar value to bigint starting from offset.
// used in grade school multiplication // used in grade school multiplication
template <uint16_t size> template <uint16_t size>
inline FASTFLOAT_CONSTEXPR20 inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec<size> &vec, limb y,
bool small_add_from(stackvec<size>& vec, limb y, size_t start) noexcept { size_t start) noexcept {
size_t index = start; size_t index = start;
limb carry = y; limb carry = y;
bool overflow; bool overflow;
@ -271,15 +264,15 @@ bool small_add_from(stackvec<size>& vec, limb y, size_t start) noexcept {
// add scalar value to bigint. // add scalar value to bigint.
template <uint16_t size> template <uint16_t size>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
bool small_add(stackvec<size>& vec, limb y) noexcept { small_add(stackvec<size> &vec, limb y) noexcept {
return small_add_from(vec, y, 0); return small_add_from(vec, y, 0);
} }
// multiply bigint by scalar value. // multiply bigint by scalar value.
template <uint16_t size> template <uint16_t size>
inline FASTFLOAT_CONSTEXPR20 inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec<size> &vec,
bool small_mul(stackvec<size>& vec, limb y) noexcept { limb y) noexcept {
limb carry = 0; limb carry = 0;
for (size_t index = 0; index < vec.len(); index++) { for (size_t index = 0; index < vec.len(); index++) {
vec[index] = scalar_mul(vec[index], y, carry); vec[index] = scalar_mul(vec[index], y, carry);
@ -293,8 +286,8 @@ bool small_mul(stackvec<size>& vec, limb y) noexcept {
// add bigint to bigint starting from index. // add bigint to bigint starting from index.
// used in grade school multiplication // used in grade school multiplication
template <uint16_t size> template <uint16_t size>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec<size> &x, limb_span y,
bool large_add_from(stackvec<size>& x, limb_span y, size_t start) noexcept { size_t start) noexcept {
// the effective x buffer is from `xstart..x.len()`, so exit early // the effective x buffer is from `xstart..x.len()`, so exit early
// if we can't get that current range. // if we can't get that current range.
if (x.len() < start || y.len() > x.len() - start) { if (x.len() < start || y.len() > x.len() - start) {
@ -324,15 +317,14 @@ bool large_add_from(stackvec<size>& x, limb_span y, size_t start) noexcept {
// add bigint to bigint. // add bigint to bigint.
template <uint16_t size> template <uint16_t size>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
bool large_add_from(stackvec<size>& x, limb_span y) noexcept { large_add_from(stackvec<size> &x, limb_span y) noexcept {
return large_add_from(x, y, 0); return large_add_from(x, y, 0);
} }
// grade-school multiplication algorithm // grade-school multiplication algorithm
template <uint16_t size> template <uint16_t size>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept {
bool long_mul(stackvec<size>& x, limb_span y) noexcept {
limb_span xs = limb_span(x.data, x.len()); limb_span xs = limb_span(x.data, x.len());
stackvec<size> z(xs); stackvec<size> z(xs);
limb_span zs = limb_span(z.data, z.len()); 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 // grade-school multiplication algorithm
template <uint16_t size> template <uint16_t size>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20 bool large_mul(stackvec<size> &x, limb_span y) noexcept {
bool large_mul(stackvec<size>& x, limb_span y) noexcept {
if (y.len() == 1) { if (y.len() == 1) {
FASTFLOAT_TRY(small_mul(x, y[0])); FASTFLOAT_TRY(small_mul(x, y[0]));
} else { } else {
@ -370,16 +361,37 @@ bool large_mul(stackvec<size>& x, limb_span y) noexcept {
return true; return true;
} }
template <typename = void> template <typename = void> struct pow5_tables {
struct pow5_tables {
static constexpr uint32_t large_step = 135; static constexpr uint32_t large_step = 135;
static constexpr uint64_t small_power_of_5[] = { static constexpr uint64_t small_power_of_5[] = {
1UL, 5UL, 25UL, 125UL, 625UL, 3125UL, 15625UL, 78125UL, 390625UL, 1UL,
1953125UL, 9765625UL, 48828125UL, 244140625UL, 1220703125UL, 5UL,
6103515625UL, 30517578125UL, 152587890625UL, 762939453125UL, 25UL,
3814697265625UL, 19073486328125UL, 95367431640625UL, 476837158203125UL, 125UL,
2384185791015625UL, 11920928955078125UL, 59604644775390625UL, 625UL,
298023223876953125UL, 1490116119384765625UL, 7450580596923828125UL, 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 #ifdef FASTFLOAT_64BIT_LIMB
constexpr static limb large_power_of_5[] = { constexpr static limb large_power_of_5[] = {
@ -392,14 +404,11 @@ struct pow5_tables {
#endif #endif
}; };
template <typename T> template <typename T> constexpr uint32_t pow5_tables<T>::large_step;
constexpr uint32_t pow5_tables<T>::large_step;
template <typename T> template <typename T> constexpr uint64_t pow5_tables<T>::small_power_of_5[];
constexpr uint64_t pow5_tables<T>::small_power_of_5[];
template <typename T> template <typename T> constexpr limb pow5_tables<T>::large_power_of_5[];
constexpr limb pow5_tables<T>::large_power_of_5[];
// big integer type. implements a small subset of big integer // big integer type. implements a small subset of big integer
// arithmetic, using simple algorithms since asymptotically // arithmetic, using simple algorithms since asymptotically
@ -409,13 +418,13 @@ struct bigint : pow5_tables<> {
// storage of the limbs, in little-endian order. // storage of the limbs, in little-endian order.
stackvec<bigint_limbs> vec; stackvec<bigint_limbs> vec;
FASTFLOAT_CONSTEXPR20 bigint(): vec() {} FASTFLOAT_CONSTEXPR20 bigint() : vec() {}
bigint(const bigint &) = delete; bigint(const bigint &) = delete;
bigint &operator=(const bigint &) = delete; bigint &operator=(const bigint &) = delete;
bigint(bigint &&) = delete; bigint(bigint &&) = delete;
bigint &operator=(bigint &&other) = delete; bigint &operator=(bigint &&other) = delete;
FASTFLOAT_CONSTEXPR20 bigint(uint64_t value): vec() { FASTFLOAT_CONSTEXPR20 bigint(uint64_t value) : vec() {
#ifdef FASTFLOAT_64BIT_LIMB #ifdef FASTFLOAT_64BIT_LIMB
vec.push_unchecked(value); vec.push_unchecked(value);
#else #else
@ -427,7 +436,7 @@ struct bigint : pow5_tables<> {
// get the high 64 bits from the vector, and if bits were truncated. // get the high 64 bits from the vector, and if bits were truncated.
// this is to get the significant digits for the float. // 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 #ifdef FASTFLOAT_64BIT_LIMB
if (vec.len() == 0) { if (vec.len() == 0) {
return empty_hi64(truncated); return empty_hi64(truncated);
@ -446,7 +455,8 @@ struct bigint : pow5_tables<> {
} else if (vec.len() == 2) { } else if (vec.len() == 2) {
return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated); return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated);
} else { } 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); truncated |= vec.nonzero(3);
return result; return result;
} }
@ -459,7 +469,7 @@ struct bigint : pow5_tables<> {
// positive, this is larger, otherwise they are equal. // positive, this is larger, otherwise they are equal.
// the limbs are stored in little-endian order, so we // the limbs are stored in little-endian order, so we
// must compare the limbs in ever order. // 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()) { if (vec.len() > other.vec.len()) {
return 1; return 1;
} else if (vec.len() < other.vec.len()) { } else if (vec.len() < other.vec.len()) {
@ -512,12 +522,12 @@ struct bigint : pow5_tables<> {
return false; return false;
} else if (!vec.is_empty()) { } else if (!vec.is_empty()) {
// move limbs // move limbs
limb* dst = vec.data + n; limb *dst = vec.data + n;
const limb* src = vec.data; const limb *src = vec.data;
std::copy_backward(src, src + vec.len(), dst + vec.len()); std::copy_backward(src, src + vec.len(), dst + vec.len());
// fill in empty limbs // fill in empty limbs
limb* first = vec.data; limb *first = vec.data;
limb* last = first + n; limb *last = first + n;
::std::fill(first, last, 0); ::std::fill(first, last, 0);
vec.set_len(n + vec.len()); vec.set_len(n + vec.len());
return true; return true;
@ -560,18 +570,12 @@ struct bigint : pow5_tables<> {
return int(limb_bits * vec.len()) - lz; return int(limb_bits * vec.len()) - lz;
} }
FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept { FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept { return small_mul(vec, y); }
return small_mul(vec, y);
}
FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept { FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept { return small_add(vec, y); }
return small_add(vec, y);
}
// multiply as if by 2 raised to a power. // multiply as if by 2 raised to a power.
FASTFLOAT_CONSTEXPR20 bool pow2(uint32_t exp) noexcept { FASTFLOAT_CONSTEXPR20 bool pow2(uint32_t exp) noexcept { return shl(exp); }
return shl(exp);
}
// multiply as if by 5 raised to a power. // multiply as if by 5 raised to a power.
FASTFLOAT_CONSTEXPR20 bool pow5(uint32_t exp) noexcept { FASTFLOAT_CONSTEXPR20 bool pow5(uint32_t exp) noexcept {
@ -597,9 +601,8 @@ struct bigint : pow5_tables<> {
// Work around clang bug https://godbolt.org/z/zedh7rrhc // Work around clang bug https://godbolt.org/z/zedh7rrhc
// This is similar to https://github.com/llvm/llvm-project/issues/47746, // This is similar to https://github.com/llvm/llvm-project/issues/47746,
// except the workaround described there don't work here // except the workaround described there don't work here
FASTFLOAT_TRY( FASTFLOAT_TRY(small_mul(
small_mul(vec, limb(((void)small_power_of_5[0], small_power_of_5[exp]))) vec, limb(((void)small_power_of_5[0], small_power_of_5[exp]))));
);
} }
return true; return true;

View File

@ -20,16 +20,16 @@
#define FASTFLOAT_HAS_BIT_CAST 0 #define FASTFLOAT_HAS_BIT_CAST 0
#endif #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 #define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1
#else #else
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0 #define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0
#endif #endif
// Testing for relevant C++20 constexpr library features // Testing for relevant C++20 constexpr library features
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED \ #if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED && FASTFLOAT_HAS_BIT_CAST && \
&& FASTFLOAT_HAS_BIT_CAST \ __cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/
&& __cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/
#define FASTFLOAT_CONSTEXPR20 constexpr #define FASTFLOAT_CONSTEXPR20 constexpr
#define FASTFLOAT_IS_CONSTEXPR 1 #define FASTFLOAT_IS_CONSTEXPR 1
#else #else

View File

@ -12,27 +12,34 @@
namespace fast_float { namespace fast_float {
// This will compute or rather approximate w * 5**q and return a pair of 64-bit words approximating // This will compute or rather approximate w * 5**q and return a pair of 64-bit
// the result, with the "high" part corresponding to the most significant bits and the // words approximating the result, with the "high" part corresponding to the
// low part corresponding to the least significant bits. // most significant bits and the low part corresponding to the least significant
// bits.
// //
template <int bit_precision> template <int bit_precision>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128
value128 compute_product_approximation(int64_t q, uint64_t w) { compute_product_approximation(int64_t q, uint64_t w) {
const int index = 2 * int(q - powers::smallest_power_of_five); 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 // For small values of q, e.g., q in [0,27], the answer is always exact
// The line value128 firstproduct = full_multiplication(w, power_of_five_128[index]); // because The line value128 firstproduct = full_multiplication(w,
// gives the exact answer. // power_of_five_128[index]); gives the exact answer.
value128 firstproduct = full_multiplication(w, powers::power_of_five_128[index]); value128 firstproduct =
static_assert((bit_precision >= 0) && (bit_precision <= 64), " precision should be in (0,64]"); full_multiplication(w, powers::power_of_five_128[index]);
constexpr uint64_t precision_mask = (bit_precision < 64) ? static_assert((bit_precision >= 0) && (bit_precision <= 64),
(uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision) " precision should be in (0,64]");
constexpr uint64_t precision_mask =
(bit_precision < 64) ? (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision)
: uint64_t(0xFFFFFFFFFFFFFFFF); : uint64_t(0xFFFFFFFFFFFFFFFF);
if((firstproduct.high & precision_mask) == precision_mask) { // could further guard with (lower + w < lower) if ((firstproduct.high & precision_mask) ==
// regarding the second product, we only need secondproduct.high, but our expectation is that the compiler will optimize this extra work away if needed. precision_mask) { // could further guard with (lower + w < lower)
value128 secondproduct = full_multiplication(w, powers::power_of_five_128[index + 1]); // 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; firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { if (secondproduct.high > firstproduct.low) {
firstproduct.high++; firstproduct.high++;
} }
} }
@ -55,43 +62,45 @@ namespace detail {
* where * where
* p = log(5**-q)/log(2) = -q * log(5)/log(2) * p = log(5**-q)/log(2) = -q * log(5)/log(2)
*/ */
constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept { constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept {
return (((152170 + 65536) * q) >> 16) + 63; return (((152170 + 65536) * q) >> 16) + 63;
} }
} // namespace detail } // namespace detail
// create an adjusted mantissa, biased by the invalid power2 // create an adjusted mantissa, biased by the invalid power2
// for significant digits already multiplied by 10 ** q. // for significant digits already multiplied by 10 ** q.
template <typename binary> template <typename binary>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 fastfloat_really_inline FASTFLOAT_CONSTEXPR14 adjusted_mantissa
adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept { compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
int hilz = int(w >> 63) ^ 1; int hilz = int(w >> 63) ^ 1;
adjusted_mantissa answer; adjusted_mantissa answer;
answer.mantissa = w << hilz; answer.mantissa = w << hilz;
int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent(); 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; return answer;
} }
// w * 10 ** q, without rounding the representation up. // w * 10 ** q, without rounding the representation up.
// the power2 in the exponent will be adjusted by invalid_am_bias. // the power2 in the exponent will be adjusted by invalid_am_bias.
template <typename binary> template <typename binary>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
adjusted_mantissa compute_error(int64_t q, uint64_t w) noexcept { compute_error(int64_t q, uint64_t w) noexcept {
int lz = leading_zeroes(w); int lz = leading_zeroes(w);
w <<= lz; 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); return compute_error_scaled<binary>(q, product.high, lz);
} }
// w * 10 ** q // w * 10 ** q
// The returned value should be a valid ieee64 number that simply need to be packed. // The returned value should be a valid ieee64 number that simply need to be
// However, in some very rare cases, the computation will fail. In such cases, we // packed. However, in some very rare cases, the computation will fail. In such
// return an adjusted_mantissa with a negative power of 2: the caller should recompute // cases, we return an adjusted_mantissa with a negative power of 2: the caller
// in such cases. // should recompute in such cases.
template <typename binary> template <typename binary>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept { compute_float(int64_t q, uint64_t w) noexcept {
adjusted_mantissa answer; adjusted_mantissa answer;
if ((w == 0) || (q < binary::smallest_power_of_ten())) { if ((w == 0) || (q < binary::smallest_power_of_ten())) {
answer.power2 = 0; answer.power2 = 0;
@ -105,7 +114,8 @@ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
answer.mantissa = 0; answer.mantissa = 0;
return answer; 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. // We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(w); 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 // The required precision is binary::mantissa_explicit_bits() + 3 because
// 1. We need the implicit bit // 1. We need the implicit bit
// 2. We need an extra bit for rounding purposes // 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. // The computed 'product' is always sufficient.
// Mathematical proof: // Mathematical proof:
// Noble Mushtak and Daniel Lemire, Fast Number Parsing Without Fallback (to appear) // Noble Mushtak and Daniel Lemire, Fast Number Parsing Without Fallback (to
// See script/mushtak_lemire.py // appear) See script/mushtak_lemire.py
// The "compute_product_approximation" function can be slightly slower than a branchless approach: // The "compute_product_approximation" function can be slightly slower than a
// value128 product = compute_product(q, w); // branchless approach: value128 product = compute_product(q, w); but in
// but in practice, we can win big with the compute_product_approximation if its additional branch // practice, we can win big with the compute_product_approximation if its
// is easily predicted. Which is best is data specific. // additional branch is easily predicted. Which is best is data specific.
int upperbit = int(product.high >> 63); int upperbit = int(product.high >> 63);
int shift = upperbit + 64 - binary::mantissa_explicit_bits() - 3; int shift = upperbit + 64 - binary::mantissa_explicit_bits() - 3;
answer.mantissa = product.high >> shift; answer.mantissa = product.high >> shift;
answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz - 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? if (answer.power2 <= 0) { // we have a subnormal?
// Here have that answer.power2 <= 0 so -answer.power2 >= 0 // 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.power2 = 0;
answer.mantissa = 0; answer.mantissa = 0;
// result should be zero // result should be zero
@ -153,19 +168,25 @@ 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 // up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer
// subnormal, but we can only know this after rounding. // subnormal, but we can only know this after rounding.
// So we only declare a subnormal if we are smaller than the threshold. // So we only declare a subnormal if we are smaller than the threshold.
answer.power2 = (answer.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; return answer;
} }
// usually, we round *up*, but if we fall right in between and and we have an // usually, we round *up*, but if we fall right in between and and we have an
// even basis, we need to round down // even basis, we need to round down
// We are only concerned with the cases where 5**q fits in single 64-bit word. // 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()) && if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) &&
((answer.mantissa & 3) == 1) ) { // we may fall between two floats! (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 // To be in-between two floats we need that in doing
// answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3); // answer.mantissa = product.high >> (upperbit + 64 -
// ... we dropped out only zeroes. But if this happened, then we can go back!!! // binary::mantissa_explicit_bits() - 3);
if((answer.mantissa << shift) == product.high) { // ... 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 &= ~uint64_t(1); // flip it so that we do not round up
} }
} }

View File

@ -13,19 +13,34 @@
namespace fast_float { namespace fast_float {
// 1e0 to 1e19 // 1e0 to 1e19
constexpr static uint64_t powers_of_ten_uint64[] = { constexpr static uint64_t powers_of_ten_uint64[] = {1UL,
1UL, 10UL, 100UL, 1000UL, 10000UL, 100000UL, 1000000UL, 10000000UL, 100000000UL, 10UL,
1000000000UL, 10000000000UL, 100000000000UL, 1000000000000UL, 10000000000000UL, 100UL,
100000000000000UL, 1000000000000000UL, 10000000000000000UL, 100000000000000000UL, 1000UL,
1000000000000000000UL, 10000000000000000000UL}; 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. // calculate the exponent, in scientific notation, of the number.
// this algorithm is not even close to optimized, but it has no practical // this algorithm is not even close to optimized, but it has no practical
// effect on performance: in order to have a faster algorithm, we'd need // effect on performance: in order to have a faster algorithm, we'd need
// to slow down performance for faster algorithms, and this is still fast. // to slow down performance for faster algorithms, and this is still fast.
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int32_t
int32_t scientific_exponent(parsed_number_string_t<UC> & num) noexcept { scientific_exponent(parsed_number_string_t<UC> &num) noexcept {
uint64_t mantissa = num.mantissa; uint64_t mantissa = num.mantissa;
int32_t exponent = int32_t(num.exponent); int32_t exponent = int32_t(num.exponent);
while (mantissa >= 10000) { while (mantissa >= 10000) {
@ -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. // this converts a native floating-point number to an extended-precision float.
template <typename T> template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
adjusted_mantissa to_extended(T value) noexcept { to_extended(T value) noexcept {
using equiv_uint = typename binary_format<T>::equiv_uint; using equiv_uint = typename binary_format<T>::equiv_uint;
constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask(); constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask(); constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask(); constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
adjusted_mantissa am; adjusted_mantissa am;
int32_t bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent(); int32_t bias = binary_format<T>::mantissa_explicit_bits() -
binary_format<T>::minimum_exponent();
equiv_uint bits; equiv_uint bits;
#if FASTFLOAT_HAS_BIT_CAST #if FASTFLOAT_HAS_BIT_CAST
bits = std::bit_cast<equiv_uint>(value); bits = std::bit_cast<equiv_uint>(value);
@ -66,7 +82,8 @@ adjusted_mantissa to_extended(T value) noexcept {
am.mantissa = bits & mantissa_mask; am.mantissa = bits & mantissa_mask;
} else { } else {
// normal // normal
am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits()); am.power2 = int32_t((bits & exponent_mask) >>
binary_format<T>::mantissa_explicit_bits());
am.power2 -= bias; am.power2 -= bias;
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
} }
@ -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 // we are given a native float that represents b, so we need to adjust it
// halfway between b and b+u. // halfway between b and b+u.
template <typename T> template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
adjusted_mantissa to_extended_halfway(T value) noexcept { to_extended_halfway(T value) noexcept {
adjusted_mantissa am = to_extended(value); adjusted_mantissa am = to_extended(value);
am.mantissa <<= 1; am.mantissa <<= 1;
am.mantissa += 1; am.mantissa += 1;
@ -89,15 +106,18 @@ adjusted_mantissa to_extended_halfway(T value) noexcept {
// round an extended-precision float to the nearest machine float. // round an extended-precision float to the nearest machine float.
template <typename T, typename callback> template <typename T, typename callback>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
void round(adjusted_mantissa& am, callback cb) noexcept { callback cb) noexcept {
int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1; int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1;
if (-am.power2 >= mantissa_shift) { if (-am.power2 >= mantissa_shift) {
// have a denormal float // have a denormal float
int32_t shift = -am.power2 + 1; int32_t shift = -am.power2 + 1;
cb(am, std::min<int32_t>(shift, 64)); cb(am, std::min<int32_t>(shift, 64));
// check for round-up: if rounding-nearest carried us to the hidden bit. // check for round-up: if rounding-nearest carried us to the hidden bit.
am.power2 = (am.mantissa < (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; return;
} }
@ -105,7 +125,8 @@ void round(adjusted_mantissa& am, callback cb) noexcept {
cb(am, mantissa_shift); cb(am, mantissa_shift);
// check for carry // 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.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
am.power2++; am.power2++;
} }
@ -119,16 +140,11 @@ void round(adjusted_mantissa& am, callback cb) noexcept {
} }
template <typename callback> template <typename callback>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) noexcept { round_nearest_tie_even(adjusted_mantissa &am, int32_t shift,
const uint64_t mask callback cb) noexcept {
= (shift == 64) const uint64_t mask = (shift == 64) ? UINT64_MAX : (uint64_t(1) << shift) - 1;
? UINT64_MAX const uint64_t halfway = (shift == 0) ? 0 : uint64_t(1) << (shift - 1);
: (uint64_t(1) << shift) - 1;
const uint64_t halfway
= (shift == 0)
? 0
: uint64_t(1) << (shift - 1);
uint64_t truncated_bits = am.mantissa & mask; uint64_t truncated_bits = am.mantissa & mask;
bool is_above = truncated_bits > halfway; bool is_above = truncated_bits > halfway;
bool is_halfway = truncated_bits == halfway; bool is_halfway = truncated_bits == halfway;
@ -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)); am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above));
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
void round_down(adjusted_mantissa& am, int32_t shift) noexcept { round_down(adjusted_mantissa &am, int32_t shift) noexcept {
if (shift == 64) { if (shift == 64) {
am.mantissa = 0; am.mantissa = 0;
} else { } else {
@ -155,10 +171,11 @@ void round_down(adjusted_mantissa& am, int32_t shift) noexcept {
am.power2 += shift; am.power2 += shift;
} }
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
void skip_zeros(UC const * & first, UC const * last) noexcept { skip_zeros(UC const *&first, UC const *last) noexcept {
uint64_t val; uint64_t val;
while (!cpp20_and_in_constexpr() && std::distance(first, last) >= int_cmp_len<UC>()) { while (!cpp20_and_in_constexpr() &&
std::distance(first, last) >= int_cmp_len<UC>()) {
::memcpy(&val, first, sizeof(uint64_t)); ::memcpy(&val, first, sizeof(uint64_t));
if (val != int_cmp_zeros<UC>()) { if (val != int_cmp_zeros<UC>()) {
break; break;
@ -176,11 +193,12 @@ void skip_zeros(UC const * & first, UC const * last) noexcept {
// determine if any non-zero digits were truncated. // determine if any non-zero digits were truncated.
// all characters must be valid digits. // all characters must be valid digits.
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
bool is_truncated(UC const * first, UC const * last) noexcept { is_truncated(UC const *first, UC const *last) noexcept {
// do 8-bit optimizations, can just compare to 8 literal 0s. // do 8-bit optimizations, can just compare to 8 literal 0s.
uint64_t val; 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)); ::memcpy(&val, first, sizeof(uint64_t));
if (val != int_cmp_zeros<UC>()) { if (val != int_cmp_zeros<UC>()) {
return true; return true;
@ -196,15 +214,15 @@ bool is_truncated(UC const * first, UC const * last) noexcept {
return false; return false;
} }
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
bool is_truncated(span<const UC> s) noexcept { is_truncated(span<const UC> s) noexcept {
return is_truncated(s.ptr, s.ptr + s.len()); return is_truncated(s.ptr, s.ptr + s.len());
} }
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
void parse_eight_digits(const UC*& p, limb& value, size_t& counter, size_t& count) noexcept { parse_eight_digits(const UC *&p, limb &value, size_t &counter,
size_t &count) noexcept {
value = value * 100000000 + parse_eight_digits_unrolled(p); value = value * 100000000 + parse_eight_digits_unrolled(p);
p += 8; p += 8;
counter += 8; counter += 8;
@ -212,22 +230,23 @@ void parse_eight_digits(const UC*& p, limb& value, size_t& counter, size_t& coun
} }
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
void parse_one_digit(UC const *& p, limb& value, size_t& counter, size_t& count) noexcept { parse_one_digit(UC const *&p, limb &value, size_t &counter,
size_t &count) noexcept {
value = value * 10 + limb(*p - UC('0')); value = value * 10 + limb(*p - UC('0'));
p++; p++;
counter++; counter++;
count++; count++;
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
void add_native(bigint& big, limb power, limb value) noexcept { add_native(bigint &big, limb power, limb value) noexcept {
big.mul(power); big.mul(power);
big.add(value); big.add(value);
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
void round_up_bigint(bigint& big, size_t& count) noexcept { round_up_bigint(bigint &big, size_t &count) noexcept {
// need to round-up the digits, but need to avoid rounding // need to round-up the digits, but need to avoid rounding
// ....9999 to ...10000, which could cause a false halfway point. // ....9999 to ...10000, which could cause a false halfway point.
add_native(big, 10, 1); add_native(big, 10, 1);
@ -236,8 +255,9 @@ void round_up_bigint(bigint& big, size_t& count) noexcept {
// parse the significant digits into a big integer // parse the significant digits into a big integer
template <typename UC> template <typename UC>
inline FASTFLOAT_CONSTEXPR20 inline FASTFLOAT_CONSTEXPR20 void
void parse_mantissa(bigint& result, parsed_number_string_t<UC>& num, size_t max_digits, size_t& digits) noexcept { parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
size_t max_digits, size_t &digits) noexcept {
// try to minimize the number of big integer and scalar multiplication. // try to minimize the number of big integer and scalar multiplication.
// therefore, try to parse 8 digits at a time, and multiply by the largest // therefore, try to parse 8 digits at a time, and multiply by the largest
// scalar value (9 or 19 digits) for each step. // scalar value (9 or 19 digits) for each step.
@ -251,12 +271,13 @@ void parse_mantissa(bigint& result, parsed_number_string_t<UC>& num, size_t max_
#endif #endif
// process all integer digits. // process all integer digits.
UC const * p = num.integer.ptr; UC const *p = num.integer.ptr;
UC const * pend = p + num.integer.len(); UC const *pend = p + num.integer.len();
skip_zeros(p, pend); skip_zeros(p, pend);
// process all digits, in increments of step per loop // process all digits, in increments of step per loop
while (p != pend) { while (p != pend) {
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); parse_eight_digits(p, value, counter, digits);
} }
while (counter < step && p != pend && digits < max_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 // process all digits, in increments of step per loop
while (p != pend) { 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); parse_eight_digits(p, value, counter, digits);
} }
while (counter < step && p != pend && digits < max_digits) { while (counter < step && p != pend && digits < max_digits) {
@ -317,18 +339,22 @@ void parse_mantissa(bigint& result, parsed_number_string_t<UC>& num, size_t max_
} }
template <typename T> template <typename T>
inline FASTFLOAT_CONSTEXPR20 inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
adjusted_mantissa positive_digit_comp(bigint& bigmant, int32_t exponent) noexcept { positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept {
FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent))); FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent)));
adjusted_mantissa answer; adjusted_mantissa answer;
bool truncated; bool truncated;
answer.mantissa = bigmant.hi64(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; answer.power2 = bigmant.bit_length() - 64 + bias;
round<T>(answer, [truncated](adjusted_mantissa& a, int32_t shift) { 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 { round_nearest_tie_even(
return is_above || (is_halfway && truncated) || (is_odd && is_halfway); a, shift,
[truncated](bool is_odd, bool is_halfway, bool is_above) -> bool {
return is_above || (is_halfway && truncated) ||
(is_odd && is_halfway);
}); });
}); });
@ -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 // we then need to scale by `2^(f- e)`, and then the two significant digits
// are of the same magnitude. // are of the same magnitude.
template <typename T> template <typename T>
inline FASTFLOAT_CONSTEXPR20 inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int32_t exponent) noexcept { bigint &bigmant, adjusted_mantissa am, int32_t exponent) noexcept {
bigint& real_digits = bigmant; bigint &real_digits = bigmant;
int32_t real_exp = exponent; int32_t real_exp = exponent;
// get the value of `b`, rounded down, and get a bigint representation of b+h // get the value of `b`, rounded down, and get a bigint representation of b+h
adjusted_mantissa am_b = am; adjusted_mantissa am_b = am;
// gcc7 buf: use a lambda to remove the noexcept qualifier bug with -Wnoexcept-type. // gcc7 buf: use a lambda to remove the noexcept qualifier bug with
round<T>(am_b, [](adjusted_mantissa&a, int32_t shift) { round_down(a, shift); }); // -Wnoexcept-type.
round<T>(am_b,
[](adjusted_mantissa &a, int32_t shift) { round_down(a, shift); });
T b; T b;
to_float(false, am_b, b); to_float(false, am_b, b);
adjusted_mantissa theor = to_extended_halfway(b); adjusted_mantissa theor = to_extended_halfway(b);
@ -371,8 +399,9 @@ adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int
// compare digits, and use it to director rounding // compare digits, and use it to director rounding
int ord = real_digits.compare(theor_digits); int ord = real_digits.compare(theor_digits);
adjusted_mantissa answer = am; adjusted_mantissa answer = am;
round<T>(answer, [ord](adjusted_mantissa& a, int32_t shift) { round<T>(answer, [ord](adjusted_mantissa &a, int32_t shift) {
round_nearest_tie_even(a, shift, [ord](bool is_odd, bool _, bool __) -> bool { 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
(void)__; // not needed, since we've done our comparison (void)__; // not needed, since we've done our comparison
if (ord > 0) { if (ord > 0) {
@ -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 // the actual digits. we then compare the big integer representations
// of both, and use that to direct rounding. // of both, and use that to direct rounding.
template <typename T, typename UC> template <typename T, typename UC>
inline FASTFLOAT_CONSTEXPR20 inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
adjusted_mantissa digit_comp(parsed_number_string_t<UC>& num, adjusted_mantissa am) noexcept { digit_comp(parsed_number_string_t<UC> &num, adjusted_mantissa am) noexcept {
// remove the invalid exponent bias // remove the invalid exponent bias
am.power2 -= invalid_am_bias; am.power2 -= invalid_am_bias;

View File

@ -6,42 +6,50 @@
namespace fast_float { namespace fast_float {
/** /**
* This function parses the character sequence [first,last) for a number. It parses floating-point numbers expecting * This function parses the character sequence [first,last) for a number. It
* a locale-indepent format equivalent to what is used by std::strtod in the default ("C") locale. * parses floating-point numbers expecting a locale-indepent format equivalent
* The resulting floating-point value is the closest floating-point values (using either float or double), * to what is used by std::strtod in the default ("C") locale. The resulting
* using the "round to even" convention for values that would otherwise fall right in-between two values. * floating-point value is the closest floating-point values (using either float
* That is, we provide exact parsing according to the IEEE standard. * 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 * Given a successful parse, the pointer (`ptr`) in the returned value is set to
* parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned * point right after the parsed number, and the `value` referenced is set to the
* `ec` contains a representative error, otherwise the default (`std::errc()`) value is stored. * 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 * Like the C++17 standard, the `fast_float::from_chars` functions take an
* the type `fast_float::chars_format`. It is a bitset value: we check whether * optional last argument of the type `fast_float::chars_format`. It is a bitset
* `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set * value: we check whether `fmt & fast_float::chars_format::fixed` and `fmt &
* to determine whether we allow the fixed point and scientific notation respectively. * fast_float::chars_format::scientific` are set to determine whether we allow
* The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`. * 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>())> template <typename T, typename UC = char,
FASTFLOAT_CONSTEXPR20 typename = FASTFLOAT_ENABLE_IF(is_supported_float_type<T>())>
from_chars_result_t<UC> from_chars(UC const * first, UC const * last, FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
T &value, chars_format fmt = chars_format::general) noexcept; 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. * Like from_chars, but accepts an `options` argument to govern number parsing.
*/ */
template<typename T, typename UC = char> template <typename T, typename UC = char>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last, from_chars_advanced(UC const *first, UC const *last, T &value,
T &value, parse_options_t<UC> options) noexcept; parse_options_t<UC> options) noexcept;
/** /**
* from_chars for integer types. * from_chars for integer types.
*/ */
template <typename T, typename UC = char, typename = FASTFLOAT_ENABLE_IF(!is_supported_float_type<T>())> template <typename T, typename UC = char,
FASTFLOAT_CONSTEXPR20 typename = FASTFLOAT_ENABLE_IF(!is_supported_float_type<T>())>
from_chars_result_t<UC> from_chars(UC const * first, UC const * last, T& value, int base = 10) noexcept; FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars(UC const *first, UC const *last, T &value, int base = 10) noexcept;
} // namespace fast_float } // namespace fast_float
#include "parse_number.h" #include "parse_number.h"

File diff suppressed because it is too large Load Diff

View File

@ -8,9 +8,9 @@
#include <type_traits> #include <type_traits>
#include <system_error> #include <system_error>
#ifdef __has_include #ifdef __has_include
#if __has_include(<stdfloat>) && (__cplusplus > 202002L || _MSVC_LANG > 202002L) #if __has_include(<stdfloat>) && (__cplusplus > 202002L || _MSVC_LANG > 202002L)
#include <stdfloat> #include <stdfloat>
#endif #endif
#endif #endif
#include "constexpr_feature_detect.h" #include "constexpr_feature_detect.h"
@ -32,15 +32,13 @@ enum chars_format {
general = fixed | scientific general = fixed | scientific
}; };
template <typename UC> template <typename UC> struct from_chars_result_t {
struct from_chars_result_t { UC const *ptr;
UC const* ptr;
std::errc ec; std::errc ec;
}; };
using from_chars_result = from_chars_result_t<char>; using from_chars_result = from_chars_result_t<char>;
template <typename UC> template <typename UC> struct parse_options_t {
struct parse_options_t {
constexpr explicit parse_options_t(chars_format fmt = chars_format::general, constexpr explicit parse_options_t(chars_format fmt = chars_format::general,
UC dot = UC('.')) UC dot = UC('.'))
: format(fmt), decimal_point(dot) {} : format(fmt), decimal_point(dot) {}
@ -52,36 +50,37 @@ struct parse_options_t {
}; };
using parse_options = parse_options_t<char>; using parse_options = parse_options_t<char>;
} } // namespace fast_float
#if FASTFLOAT_HAS_BIT_CAST #if FASTFLOAT_HAS_BIT_CAST
#include <bit> #include <bit>
#endif #endif
#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \ #if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
|| defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \ defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) || \
|| defined(__MINGW64__) \ defined(__MINGW64__) || defined(__s390x__) || \
|| defined(__s390x__) \ (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || \
|| (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) \ defined(__PPC64LE__)) || \
|| defined(__loongarch64) ) defined(__loongarch64))
#define FASTFLOAT_64BIT 1 #define FASTFLOAT_64BIT 1
#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \ #elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) || \
|| defined(__arm__) || defined(_M_ARM) || defined(__ppc__) \ defined(__arm__) || defined(_M_ARM) || defined(__ppc__) || \
|| defined(__MINGW32__) || defined(__EMSCRIPTEN__)) defined(__MINGW32__) || defined(__EMSCRIPTEN__))
#define FASTFLOAT_32BIT 1 #define FASTFLOAT_32BIT 1
#else #else
// Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow. // 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. // We can never tell the register width, but the SIZE_MAX is a good
// UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max portability. // approximation. UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max
#if SIZE_MAX == 0xffff // portability.
#error Unknown platform (16-bit, unsupported) #if SIZE_MAX == 0xffff
#elif SIZE_MAX == 0xffffffff #error Unknown platform (16-bit, unsupported)
#define FASTFLOAT_32BIT 1 #elif SIZE_MAX == 0xffffffff
#elif SIZE_MAX == 0xffffffffffffffff #define FASTFLOAT_32BIT 1
#define FASTFLOAT_64BIT 1 #elif SIZE_MAX == 0xffffffffffffffff
#else #define FASTFLOAT_64BIT 1
#error Unknown platform (not 32-bit, not 64-bit?) #else
#endif #error Unknown platform (not 32-bit, not 64-bit?)
#endif
#endif #endif
#if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__)) || \ #if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__)) || \
@ -129,9 +128,9 @@ using parse_options = parse_options_t<char>;
#endif #endif
#endif #endif
#if defined(__SSE2__) || \ #if defined(__SSE2__) || (defined(FASTFLOAT_VISUAL_STUDIO) && \
(defined(FASTFLOAT_VISUAL_STUDIO) && \ (defined(_M_AMD64) || defined(_M_X64) || \
(defined(_M_AMD64) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP == 2))) (defined(_M_IX86_FP) && _M_IX86_FP == 2)))
#define FASTFLOAT_SSE2 1 #define FASTFLOAT_SSE2 1
#endif #endif
@ -153,14 +152,11 @@ using parse_options = parse_options_t<char>;
#endif #endif
#if defined(__GNUC__) #if defined(__GNUC__)
#define FASTFLOAT_SIMD_RESTORE_WARNINGS \ #define FASTFLOAT_SIMD_RESTORE_WARNINGS _Pragma("GCC diagnostic pop")
_Pragma("GCC diagnostic pop")
#else #else
#define FASTFLOAT_SIMD_RESTORE_WARNINGS #define FASTFLOAT_SIMD_RESTORE_WARNINGS
#endif #endif
#ifdef FASTFLOAT_VISUAL_STUDIO #ifdef FASTFLOAT_VISUAL_STUDIO
#define fastfloat_really_inline __forceinline #define fastfloat_really_inline __forceinline
#else #else
@ -168,18 +164,24 @@ using parse_options = parse_options_t<char>;
#endif #endif
#ifndef FASTFLOAT_ASSERT #ifndef FASTFLOAT_ASSERT
#define FASTFLOAT_ASSERT(x) { ((void)(x)); } #define FASTFLOAT_ASSERT(x) \
{ ((void)(x)); }
#endif #endif
#ifndef FASTFLOAT_DEBUG_ASSERT #ifndef FASTFLOAT_DEBUG_ASSERT
#define FASTFLOAT_DEBUG_ASSERT(x) { ((void)(x)); } #define FASTFLOAT_DEBUG_ASSERT(x) \
{ ((void)(x)); }
#endif #endif
// rust style `try!()` macro, or `?` operator // rust style `try!()` macro, or `?` operator
#define FASTFLOAT_TRY(x) { if (!(x)) return false; } #define FASTFLOAT_TRY(x) \
{ \
#define FASTFLOAT_ENABLE_IF(...) typename std::enable_if<(__VA_ARGS__), int>::type if (!(x)) \
return false; \
}
#define FASTFLOAT_ENABLE_IF(...) \
typename std::enable_if<(__VA_ARGS__), int>::type
namespace fast_float { namespace fast_float {
@ -205,17 +207,14 @@ fastfloat_really_inline constexpr bool is_supported_float_type() {
template <typename UC> template <typename UC>
fastfloat_really_inline constexpr bool is_supported_char_type() { fastfloat_really_inline constexpr bool is_supported_char_type() {
return return std::is_same<UC, char>::value || std::is_same<UC, wchar_t>::value ||
std::is_same<UC, char>::value || std::is_same<UC, char16_t>::value || std::is_same<UC, char32_t>::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. // Compares two ASCII strings in a case insensitive manner.
template <typename UC> template <typename UC>
inline FASTFLOAT_CONSTEXPR14 bool inline FASTFLOAT_CONSTEXPR14 bool
fastfloat_strncasecmp(UC const * input1, UC const * input2, size_t length) { fastfloat_strncasecmp(UC const *input1, UC const *input2, size_t length) {
char running_diff{0}; char running_diff{0};
for (size_t i = 0; i < length; ++i) { for (size_t i = 0; i < length; ++i) {
running_diff |= (char(input1[i]) ^ char(input2[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 #endif
// a pointer and a length to a contiguous block of memory // a pointer and a length to a contiguous block of memory
template <typename T> template <typename T> struct span {
struct span { const T *ptr;
const T* ptr;
size_t length; 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 span() : ptr(nullptr), length(0) {}
constexpr size_t len() const noexcept { constexpr size_t len() const noexcept { return length; }
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); FASTFLOAT_DEBUG_ASSERT(index < length);
return ptr[index]; return ptr[index];
} }
@ -253,34 +249,51 @@ struct value128 {
}; };
/* Helper C++14 constexpr generic implementation of leading_zeroes */ /* Helper C++14 constexpr generic implementation of leading_zeroes */
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int
int leading_zeroes_generic(uint64_t input_num, int last_bit = 0) { 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(0xffffffff00000000)) {
if(input_num & uint64_t( 0xffff0000)) { input_num >>= 16; last_bit |= 16; } input_num >>= 32;
if(input_num & uint64_t( 0xff00)) { input_num >>= 8; last_bit |= 8; } last_bit |= 32;
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(0xffff0000)) {
if(input_num & uint64_t( 0x2)) { /* input_num >>= 1; */ last_bit |= 1; } 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; return 63 - last_bit;
} }
/* result might be undefined when input_num is zero */ /* result might be undefined when input_num is zero */
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 int
int leading_zeroes(uint64_t input_num) { leading_zeroes(uint64_t input_num) {
assert(input_num > 0); assert(input_num > 0);
if (cpp20_and_in_constexpr()) { if (cpp20_and_in_constexpr()) {
return leading_zeroes_generic(input_num); return leading_zeroes_generic(input_num);
} }
#ifdef FASTFLOAT_VISUAL_STUDIO #ifdef FASTFLOAT_VISUAL_STUDIO
#if defined(_M_X64) || defined(_M_ARM64) #if defined(_M_X64) || defined(_M_ARM64)
unsigned long leading_zero = 0; unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB) // Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1). // to least significant bit (LSB) for a set bit (1).
_BitScanReverse64(&leading_zero, input_num); _BitScanReverse64(&leading_zero, input_num);
return (int)(63 - leading_zero); return (int)(63 - leading_zero);
#else #else
return leading_zeroes_generic(input_num); return leading_zeroes_generic(input_num);
#endif #endif
#else #else
return __builtin_clzll(input_num); return __builtin_clzll(input_num);
#endif #endif
@ -291,8 +304,8 @@ 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 fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t
uint64_t umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) { umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) {
uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd); uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd); uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32)); uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
@ -307,18 +320,18 @@ uint64_t umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) {
// slow emulation routine for 32-bit // slow emulation routine for 32-bit
#if !defined(__MINGW64__) #if !defined(__MINGW64__)
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t _umul128(uint64_t ab,
uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { uint64_t cd,
uint64_t *hi) {
return umul128_generic(ab, cd, hi); return umul128_generic(ab, cd, hi);
} }
#endif // !__MINGW64__ #endif // !__MINGW64__
#endif // FASTFLOAT_32BIT #endif // FASTFLOAT_32BIT
// compute 64-bit a*b // compute 64-bit a*b
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128
value128 full_multiplication(uint64_t a, uint64_t b) { full_multiplication(uint64_t a, uint64_t b) {
if (cpp20_and_in_constexpr()) { if (cpp20_and_in_constexpr()) {
value128 answer; value128 answer;
answer.low = umul128_generic(a, b, &answer.high); answer.low = umul128_generic(a, b, &answer.high);
@ -360,22 +373,24 @@ constexpr static int32_t invalid_am_bias = -0x8000;
// used for binary_format_lookup_tables<T>::max_mantissa // used for binary_format_lookup_tables<T>::max_mantissa
constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5; constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5;
template <typename T, typename U = void> template <typename T, typename U = void> struct binary_format_lookup_tables;
struct binary_format_lookup_tables;
template <typename T> struct binary_format : binary_format_lookup_tables<T> { template <typename T> struct binary_format : binary_format_lookup_tables<T> {
using equiv_uint = 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 mantissa_explicit_bits();
static inline constexpr int minimum_exponent(); static inline constexpr int minimum_exponent();
static inline constexpr int infinite_power(); static inline constexpr int infinite_power();
static inline constexpr int sign_index(); 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_fast_path();
static inline constexpr int max_exponent_round_to_even(); static inline constexpr int max_exponent_round_to_even();
static inline constexpr int min_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(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 largest_power_of_ten();
static inline constexpr int smallest_power_of_ten(); static inline constexpr int smallest_power_of_ten();
static inline constexpr T exact_power_of_ten(int64_t power); 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(); static inline constexpr equiv_uint hidden_bit_mask();
}; };
template <typename U> template <typename U> struct binary_format_lookup_tables<double, U> {
struct binary_format_lookup_tables<double, U> {
static constexpr double powers_of_ten[] = { static constexpr double powers_of_ten[] = {
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,
1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22}; 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 * 5 * 5 * 5),
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555), 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555),
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5), 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5),
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5), 0x20000000000000 /
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5), (constant_55555 * constant_55555 * constant_55555 * 5 * 5),
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5), 0x20000000000000 /
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555), (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5),
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5), 0x20000000000000 /
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5), (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5),
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5), 0x20000000000000 /
0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5)}; (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> template <typename U>
@ -426,8 +448,7 @@ constexpr double binary_format_lookup_tables<double, U>::powers_of_ten[];
template <typename U> template <typename U>
constexpr uint64_t binary_format_lookup_tables<double, U>::max_mantissa[]; constexpr uint64_t binary_format_lookup_tables<double, U>::max_mantissa[];
template <typename U> template <typename U> struct binary_format_lookup_tables<float, U> {
struct binary_format_lookup_tables<float, U> {
static constexpr float powers_of_ten[] = {1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f, static constexpr float powers_of_ten[] = {1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f,
1e6f, 1e7f, 1e8f, 1e9f, 1e10f}; 1e6f, 1e7f, 1e8f, 1e9f, 1e10f};
@ -454,7 +475,8 @@ constexpr float binary_format_lookup_tables<float, U>::powers_of_ten[];
template <typename U> template <typename U>
constexpr uint64_t binary_format_lookup_tables<float, U>::max_mantissa[]; constexpr 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) #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return 0; return 0;
#else #else
@ -462,7 +484,8 @@ template <> inline constexpr int binary_format<double>::min_exponent_fast_path()
#endif #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) #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return 0; return 0;
#else #else
@ -470,26 +493,32 @@ template <> inline constexpr int binary_format<float>::min_exponent_fast_path()
#endif #endif
} }
template <> inline constexpr int binary_format<double>::mantissa_explicit_bits() { template <>
inline constexpr int binary_format<double>::mantissa_explicit_bits() {
return 52; return 52;
} }
template <> inline constexpr int binary_format<float>::mantissa_explicit_bits() { template <>
inline constexpr int binary_format<float>::mantissa_explicit_bits() {
return 23; 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; 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; 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; 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; return -17;
} }
@ -507,30 +536,42 @@ template <> inline constexpr int binary_format<float>::infinite_power() {
return 0xFF; return 0xFF;
} }
template <> inline constexpr int binary_format<double>::sign_index() { return 63; } template <> inline constexpr int binary_format<double>::sign_index() {
template <> inline constexpr int binary_format<float>::sign_index() { return 31; } 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; 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; 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(); 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 // caller is responsible to ensure that
// power >= 0 && power <= 22 // power >= 0 && power <= 22
// //
// Work around clang bug https://godbolt.org/z/zedh7rrhc // Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)max_mantissa[0], max_mantissa[power]; return (void)max_mantissa[0], max_mantissa[power];
} }
template <> 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(); 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 // caller is responsible to ensure that
// power >= 0 && power <= 10 // power >= 0 && power <= 10
// //
@ -539,7 +580,8 @@ template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_pa
} }
template <> 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 // Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)powers_of_ten[0], powers_of_ten[power]; return (void)powers_of_ten[0], powers_of_ten[power];
} }
@ -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]; 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; return 308;
} }
template <> template <> inline constexpr int binary_format<float>::largest_power_of_ten() {
inline constexpr int binary_format<float>::largest_power_of_ten() {
return 38; return 38;
} }
@ -563,8 +602,7 @@ template <>
inline constexpr int binary_format<double>::smallest_power_of_ten() { inline constexpr int binary_format<double>::smallest_power_of_ten() {
return -342; return -342;
} }
template <> template <> inline constexpr int binary_format<float>::smallest_power_of_ten() {
inline constexpr int binary_format<float>::smallest_power_of_ten() {
return -64; return -64;
} }
@ -575,39 +613,46 @@ template <> inline constexpr size_t binary_format<float>::max_digits() {
return 114; return 114;
} }
template <> inline constexpr binary_format<float>::equiv_uint template <>
binary_format<float>::exponent_mask() { inline constexpr binary_format<float>::equiv_uint
binary_format<float>::exponent_mask() {
return 0x7F800000; return 0x7F800000;
} }
template <> inline constexpr binary_format<double>::equiv_uint template <>
binary_format<double>::exponent_mask() { inline constexpr binary_format<double>::equiv_uint
binary_format<double>::exponent_mask() {
return 0x7FF0000000000000; return 0x7FF0000000000000;
} }
template <> inline constexpr binary_format<float>::equiv_uint template <>
binary_format<float>::mantissa_mask() { inline constexpr binary_format<float>::equiv_uint
binary_format<float>::mantissa_mask() {
return 0x007FFFFF; return 0x007FFFFF;
} }
template <> inline constexpr binary_format<double>::equiv_uint template <>
binary_format<double>::mantissa_mask() { inline constexpr binary_format<double>::equiv_uint
binary_format<double>::mantissa_mask() {
return 0x000FFFFFFFFFFFFF; return 0x000FFFFFFFFFFFFF;
} }
template <> inline constexpr binary_format<float>::equiv_uint template <>
binary_format<float>::hidden_bit_mask() { inline constexpr binary_format<float>::equiv_uint
binary_format<float>::hidden_bit_mask() {
return 0x00800000; return 0x00800000;
} }
template <> inline constexpr binary_format<double>::equiv_uint template <>
binary_format<double>::hidden_bit_mask() { inline constexpr binary_format<double>::equiv_uint
binary_format<double>::hidden_bit_mask() {
return 0x0010000000000000; return 0x0010000000000000;
} }
template<typename T> template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
void to_float(bool negative, adjusted_mantissa am, T &value) { to_float(bool negative, adjusted_mantissa am, T &value) {
using fastfloat_uint = typename binary_format<T>::equiv_uint; using fastfloat_uint = typename binary_format<T>::equiv_uint;
fastfloat_uint word = (fastfloat_uint)am.mantissa; 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(); word |= fastfloat_uint(negative) << binary_format<T>::sign_index();
#if FASTFLOAT_HAS_BIT_CAST #if FASTFLOAT_HAS_BIT_CAST
value = std::bit_cast<T>(word); value = std::bit_cast<T>(word);
@ -617,8 +662,7 @@ void to_float(bool negative, adjusted_mantissa am, T &value) {
} }
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default #ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
template <typename = void> template <typename = void> struct space_lut {
struct space_lut {
static constexpr bool value[] = { 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, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@ -633,135 +677,108 @@ struct space_lut {
0, 0, 0, 0, 0, 0, 0, 0, 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> template <typename T> constexpr bool space_lut<T>::value[];
constexpr bool space_lut<T>::value[];
inline constexpr bool is_space(uint8_t c) { return space_lut<>::value[c]; } inline constexpr bool is_space(uint8_t c) { return space_lut<>::value[c]; }
#endif #endif
template<typename UC> template <typename UC> static constexpr uint64_t int_cmp_zeros() {
static constexpr uint64_t int_cmp_zeros() static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4),
{ "Unsupported character size");
static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4), "Unsupported character size"); return (sizeof(UC) == 1) ? 0x3030303030303030
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')); : (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> template <typename UC> static constexpr int int_cmp_len() {
static constexpr int int_cmp_len()
{
return sizeof(uint64_t) / sizeof(UC); return sizeof(uint64_t) / sizeof(UC);
} }
template<typename UC> template <typename UC> static constexpr UC const *str_const_nan() {
static constexpr UC const * str_const_nan()
{
return nullptr; return nullptr;
} }
template<> template <> constexpr char const *str_const_nan<char>() { return "nan"; }
constexpr char const * str_const_nan<char>() template <> constexpr wchar_t const *str_const_nan<wchar_t>() { return L"nan"; }
{ template <> constexpr char16_t const *str_const_nan<char16_t>() {
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"; return u"nan";
} }
template<> template <> constexpr char32_t const *str_const_nan<char32_t>() {
constexpr char32_t const * str_const_nan<char32_t>()
{
return U"nan"; return U"nan";
} }
template<typename UC> template <typename UC> static constexpr UC const *str_const_inf() {
static constexpr UC const * str_const_inf()
{
return nullptr; return nullptr;
} }
template<> template <> constexpr char const *str_const_inf<char>() { return "infinity"; }
constexpr char const * str_const_inf<char>() template <> constexpr wchar_t const *str_const_inf<wchar_t>() {
{
return "infinity";
}
template<>
constexpr wchar_t const * str_const_inf<wchar_t>()
{
return L"infinity"; return L"infinity";
} }
template<> template <> constexpr char16_t const *str_const_inf<char16_t>() {
constexpr char16_t const * str_const_inf<char16_t>()
{
return u"infinity"; return u"infinity";
} }
template<> template <> constexpr char32_t const *str_const_inf<char32_t>() {
constexpr char32_t const * str_const_inf<char32_t>()
{
return U"infinity"; return U"infinity";
} }
template <typename = void> struct int_luts {
template <typename = void>
struct int_luts {
static constexpr uint8_t chdigit[] = { static constexpr uint8_t chdigit[] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 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, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255,
255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 35, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 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, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
}; 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255};
static constexpr size_t maxdigits_u64[] = { static constexpr size_t maxdigits_u64[] = {
64, 41, 32, 28, 25, 23, 22, 21, 64, 41, 32, 28, 25, 23, 22, 21, 20, 19, 18, 18, 17, 17, 16, 16, 16, 16,
20, 19, 18, 18, 17, 17, 16, 16, 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13};
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[] = { static constexpr uint64_t min_safe_u64[] = {
9223372036854775808ull, 12157665459056928801ull, 4611686018427387904, 7450580596923828125, 4738381338321616896, 9223372036854775808ull, 12157665459056928801ull, 4611686018427387904,
3909821048582988049, 9223372036854775808ull, 12157665459056928801ull, 10000000000000000000ull, 5559917313492231481, 7450580596923828125, 4738381338321616896, 3909821048582988049,
2218611106740436992, 8650415919381337933, 2177953337809371136, 6568408355712890625, 1152921504606846976, 9223372036854775808ull, 12157665459056928801ull, 10000000000000000000ull,
2862423051509815793, 6746640616477458432, 15181127029874798299ull, 1638400000000000000, 3243919932521508681, 5559917313492231481, 2218611106740436992, 8650415919381337933,
6221821273427820544, 11592836324538749809ull, 876488338465357824, 1490116119384765625, 2481152873203736576, 2177953337809371136, 6568408355712890625, 1152921504606846976,
4052555153018976267, 6502111422497947648, 10260628712958602189ull, 15943230000000000000ull, 787662783788549761, 2862423051509815793, 6746640616477458432, 15181127029874798299ull,
1152921504606846976, 1667889514952984961, 2386420683693101056, 3379220508056640625, 4738381338321616896 1638400000000000000, 3243919932521508681, 6221821273427820544,
}; 11592836324538749809ull, 876488338465357824, 1490116119384765625,
2481152873203736576, 4052555153018976267, 6502111422497947648,
10260628712958602189ull, 15943230000000000000ull, 787662783788549761,
1152921504606846976, 1667889514952984961, 2386420683693101056,
3379220508056640625, 4738381338321616896};
}; };
template <typename T> template <typename T> constexpr uint8_t int_luts<T>::chdigit[];
constexpr uint8_t int_luts<T>::chdigit[];
template <typename T> template <typename T> constexpr size_t int_luts<T>::maxdigits_u64[];
constexpr size_t int_luts<T>::maxdigits_u64[];
template <typename T> template <typename T> constexpr uint64_t int_luts<T>::min_safe_u64[];
constexpr uint64_t int_luts<T>::min_safe_u64[];
template <typename UC> template <typename UC>
fastfloat_really_inline fastfloat_really_inline constexpr uint8_t ch_to_digit(UC c) {
constexpr uint8_t ch_to_digit(UC c) { return int_luts<>::chdigit[static_cast<unsigned char>(c)]; } return int_luts<>::chdigit[static_cast<unsigned char>(c)];
}
fastfloat_really_inline fastfloat_really_inline constexpr size_t max_digits_u64(int base) {
constexpr size_t max_digits_u64(int base) { return int_luts<>::maxdigits_u64[base - 2]; } return int_luts<>::maxdigits_u64[base - 2];
}
// If a u64 is exactly max_digits_u64() in length, this is // If a u64 is exactly max_digits_u64() in length, this is
// the value below which it has definitely overflowed. // the value below which it has definitely overflowed.
fastfloat_really_inline fastfloat_really_inline constexpr uint64_t min_safe_u64(int base) {
constexpr uint64_t min_safe_u64(int base) { return int_luts<>::min_safe_u64[base - 2]; } return int_luts<>::min_safe_u64[base - 2];
}
} // namespace fast_float } // namespace fast_float

View File

@ -12,7 +12,6 @@
#include <system_error> #include <system_error>
namespace fast_float { namespace fast_float {
namespace detail { namespace detail {
/** /**
* Special case +inf, -inf, nan, infinity, -infinity. * Special case +inf, -inf, nan, infinity, -infinity.
@ -20,13 +19,16 @@ namespace detail {
* strings a null-free and fixed. * strings a null-free and fixed.
**/ **/
template <typename T, typename UC> template <typename T, typename UC>
from_chars_result_t<UC> FASTFLOAT_CONSTEXPR14 from_chars_result_t<UC> FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first,
parse_infnan(UC const * first, UC const * last, T &value) noexcept { UC const *last,
T &value) noexcept {
from_chars_result_t<UC> answer{}; from_chars_result_t<UC> answer{};
answer.ptr = first; answer.ptr = first;
answer.ec = std::errc(); // be optimistic answer.ec = std::errc(); // be optimistic
bool minusSign = false; bool minusSign = false;
if (*first == UC('-')) { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here if (*first ==
UC('-')) { // assume first < last, so dereference without checks;
// C++17 20.19.3.(7.1) explicitly forbids '+' here
minusSign = true; minusSign = true;
++first; ++first;
} }
@ -38,27 +40,32 @@ parse_infnan(UC const * first, UC const * last, T &value) noexcept {
if (last - first >= 3) { if (last - first >= 3) {
if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) { if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
answer.ptr = (first += 3); answer.ptr = (first += 3);
value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN(); value = minusSign ? -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). : std::numeric_limits<T>::quiet_NaN();
if(first != last && *first == UC('(')) { // Check for possible nan(n-char-seq-opt), C++17 20.19.3.7,
for(UC const * ptr = first + 1; ptr != last; ++ptr) { // 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(')')) { if (*ptr == UC(')')) {
answer.ptr = ptr + 1; // valid nan(n-char-seq-opt) answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
break; break;
} } else if (!((UC('a') <= *ptr && *ptr <= UC('z')) ||
else if(!((UC('a') <= *ptr && *ptr <= UC('z')) || (UC('A') <= *ptr && *ptr <= UC('Z')) || (UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_'))) (UC('A') <= *ptr && *ptr <= UC('Z')) ||
(UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_')))
break; // forbidden char, not nan(n-char-seq-opt) break; // forbidden char, not nan(n-char-seq-opt)
} }
} }
return answer; return answer;
} }
if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) { 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; answer.ptr = first + 8;
} else { } else {
answer.ptr = first + 3; 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; return answer;
} }
} }
@ -88,71 +95,69 @@ fastfloat_really_inline bool rounds_to_nearest() noexcept {
// //
// The volatile keywoard prevents the compiler from computing the function // The volatile keywoard prevents the compiler from computing the function
// at compile-time. // at compile-time.
// There might be other ways to prevent compile-time optimizations (e.g., asm). // There might be other ways to prevent compile-time optimizations (e.g.,
// The value does not need to be std::numeric_limits<float>::min(), any small // asm). The value does not need to be std::numeric_limits<float>::min(), any
// value so that 1 + x should round to 1 would do (after accounting for excess // small value so that 1 + x should round to 1 would do (after accounting for
// precision, as in 387 instructions). // excess precision, as in 387 instructions).
static volatile float fmin = std::numeric_limits<float>::min(); static volatile float fmin = std::numeric_limits<float>::min();
float fmini = fmin; // we copy it so that it gets loaded at most once. float fmini = fmin; // we copy it so that it gets loaded at most once.
// //
// Explanation: // Explanation:
// Only when fegetround() == FE_TONEAREST do we have that // Only when fegetround() == FE_TONEAREST do we have that
// fmin + 1.0f == 1.0f - fmin. // fmin + 1.0f == 1.0f - fmin.
// //
// FE_UPWARD: // FE_UPWARD:
// fmin + 1.0f > 1 // fmin + 1.0f > 1
// 1.0f - fmin == 1 // 1.0f - fmin == 1
// //
// FE_DOWNWARD or FE_TOWARDZERO: // FE_DOWNWARD or FE_TOWARDZERO:
// fmin + 1.0f == 1 // fmin + 1.0f == 1
// 1.0f - fmin < 1 // 1.0f - fmin < 1
// //
// Note: This may fail to be accurate if fast-math has been // Note: This may fail to be accurate if fast-math has been
// enabled, as rounding conventions may not apply. // enabled, as rounding conventions may not apply.
#ifdef FASTFLOAT_VISUAL_STUDIO #ifdef FASTFLOAT_VISUAL_STUDIO
# pragma warning(push) #pragma warning(push)
// todo: is there a VS warning? // 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 // see
#elif defined(__clang__) // https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013
# pragma clang diagnostic push #elif defined(__clang__)
# pragma clang diagnostic ignored "-Wfloat-equal" #pragma clang diagnostic push
#elif defined(__GNUC__) #pragma clang diagnostic ignored "-Wfloat-equal"
# pragma GCC diagnostic push #elif defined(__GNUC__)
# pragma GCC diagnostic ignored "-Wfloat-equal" #pragma GCC diagnostic push
#endif #pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
return (fmini + 1.0f == 1.0f - fmini); return (fmini + 1.0f == 1.0f - fmini);
#ifdef FASTFLOAT_VISUAL_STUDIO #ifdef FASTFLOAT_VISUAL_STUDIO
# pragma warning(pop) #pragma warning(pop)
#elif defined(__clang__) #elif defined(__clang__)
# pragma clang diagnostic pop #pragma clang diagnostic pop
#elif defined(__GNUC__) #elif defined(__GNUC__)
# pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
} }
} // namespace detail } // namespace detail
template <typename T> template <typename T> struct from_chars_caller {
struct from_chars_caller
{
template <typename UC> template <typename UC>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
static from_chars_result_t<UC> call(UC const * first, UC const * last, call(UC const *first, UC const *last, T &value,
T &value, parse_options_t<UC> options) noexcept { parse_options_t<UC> options) noexcept {
return from_chars_advanced(first, last, value, options); return from_chars_advanced(first, last, value, options);
} }
}; };
#if __STDCPP_FLOAT32_T__ == 1 #if __STDCPP_FLOAT32_T__ == 1
template <> template <> struct from_chars_caller<std::float32_t> {
struct from_chars_caller<std::float32_t>
{
template <typename UC> template <typename UC>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
static from_chars_result_t<UC> call(UC const * first, UC const * last, call(UC const *first, UC const *last, std::float32_t &value,
std::float32_t &value, parse_options_t<UC> options) noexcept{ parse_options_t<UC> options) noexcept {
// if std::float32_t is defined, and we are in C++23 mode; macro set for float32; // if std::float32_t is defined, and we are in C++23 mode; macro set for
// set value to float due to equivalence between float and float32_t // float32; set value to float due to equivalence between float and
// float32_t
float val; float val;
auto ret = from_chars_advanced(first, last, val, options); auto ret = from_chars_advanced(first, last, val, options);
value = val; value = val;
@ -162,15 +167,14 @@ struct from_chars_caller<std::float32_t>
#endif #endif
#if __STDCPP_FLOAT64_T__ == 1 #if __STDCPP_FLOAT64_T__ == 1
template <> template <> struct from_chars_caller<std::float64_t> {
struct from_chars_caller<std::float64_t>
{
template <typename UC> template <typename UC>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
static from_chars_result_t<UC> call(UC const * first, UC const * last, call(UC const *first, UC const *last, std::float64_t &value,
std::float64_t &value, parse_options_t<UC> options) noexcept{ parse_options_t<UC> options) noexcept {
// if std::float64_t is defined, and we are in C++23 mode; macro set for float64; // if std::float64_t is defined, and we are in C++23 mode; macro set for
// set value as double due to equivalence between double and float64_t // float64; set value as double due to equivalence between double and
// float64_t
double val; double val;
auto ret = from_chars_advanced(first, last, val, options); auto ret = from_chars_advanced(first, last, val, options);
value = val; value = val;
@ -179,26 +183,27 @@ struct from_chars_caller<std::float64_t>
}; };
#endif #endif
template <typename T, typename UC, typename>
template<typename T, typename UC, typename> FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
FASTFLOAT_CONSTEXPR20 from_chars(UC const *first, UC const *last, T &value,
from_chars_result_t<UC> from_chars(UC const * first, UC const * last, chars_format fmt /*= chars_format::general*/) noexcept {
T &value, chars_format fmt /*= chars_format::general*/) noexcept { return from_chars_caller<T>::call(first, last, value,
return from_chars_caller<T>::call(first, last, value, parse_options_t<UC>(fmt)); parse_options_t<UC>(fmt));
} }
/** /**
* This function overload takes parsed_number_string_t structure that is created and populated * This function overload takes parsed_number_string_t structure that is created
* either by from_chars_advanced function taking chars range and parsing options * and populated either by from_chars_advanced function taking chars range and
* or other parsing custom function implemented by user. * parsing options or other parsing custom function implemented by user.
*/ */
template<typename T, typename UC> template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_result_t<UC> from_chars_advanced(parsed_number_string_t<UC>& pns, from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
T &value) noexcept {
static_assert (is_supported_float_type<T>(), "only some floating-point types are supported"); static_assert(is_supported_float_type<T>(),
static_assert (is_supported_char_type<UC>(), "only char, wchar_t, char16_t and char32_t are supported"); "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; 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 // The implementation of the Clinger's fast path is convoluted because
// we want round-to-nearest in all cases, irrespective of the rounding mode // we want round-to-nearest in all cases, irrespective of the rounding mode
// selected on the thread. // selected on the thread.
// We proceed optimistically, assuming that detail::rounds_to_nearest() returns // We proceed optimistically, assuming that detail::rounds_to_nearest()
// true. // 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) { 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 // Unfortunately, the conventional Clinger's fast path is only possible
// when the system rounds to the nearest float. // when the system rounds to the nearest float.
// //
@ -217,57 +224,75 @@ from_chars_result_t<UC> from_chars_advanced(parsed_number_string_t<UC>& pns,
// We could check it first (before the previous branch), but // We could check it first (before the previous branch), but
// there might be performance advantages at having the check // there might be performance advantages at having the check
// be last. // be last.
if(!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) { if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) {
// We have that fegetround() == FE_TONEAREST. // We have that fegetround() == FE_TONEAREST.
// Next is Clinger's fast path. // Next is Clinger's fast path.
if (pns.mantissa <=binary_format<T>::max_mantissa_fast_path()) { if (pns.mantissa <= binary_format<T>::max_mantissa_fast_path()) {
value = T(pns.mantissa); value = T(pns.mantissa);
if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); } if (pns.exponent < 0) {
else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); } value = value / binary_format<T>::exact_power_of_ten(-pns.exponent);
if (pns.negative) { value = -value; } } else {
value = value * binary_format<T>::exact_power_of_ten(pns.exponent);
}
if (pns.negative) {
value = -value;
}
return answer; return answer;
} }
} else { } else {
// We do not have that fegetround() == FE_TONEAREST. // We do not have that fegetround() == FE_TONEAREST.
// Next is a modified Clinger's fast path, inspired by Jakub Jelínek's proposal // Next is a modified Clinger's fast path, inspired by Jakub Jelínek's
if (pns.exponent >= 0 && pns.mantissa <=binary_format<T>::max_mantissa_fast_path(pns.exponent)) { // proposal
if (pns.exponent >= 0 &&
pns.mantissa <=
binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
#if defined(__clang__) || defined(FASTFLOAT_32BIT) #if defined(__clang__) || defined(FASTFLOAT_32BIT)
// Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD // Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
if(pns.mantissa == 0) { if (pns.mantissa == 0) {
value = pns.negative ? T(-0.) : T(0.); value = pns.negative ? T(-0.) : T(0.);
return answer; return answer;
} }
#endif #endif
value = T(pns.mantissa) * binary_format<T>::exact_power_of_ten(pns.exponent); value = T(pns.mantissa) *
if (pns.negative) { value = -value; } binary_format<T>::exact_power_of_ten(pns.exponent);
if (pns.negative) {
value = -value;
}
return answer; return answer;
} }
} }
} }
adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa); adjusted_mantissa am =
if(pns.too_many_digits && am.power2 >= 0) { compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
if(am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) { 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); 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), // If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa)
// then we need to go the long way around again. This is very uncommon. // and we have an invalid power (am.power2 < 0), then we need to go the long
if(am.power2 < 0) { am = digit_comp<T>(pns, am); } // way around again. This is very uncommon.
if (am.power2 < 0) {
am = digit_comp<T>(pns, am);
}
to_float(pns.negative, am, value); to_float(pns.negative, am, value);
// Test for over/underflow. // 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; answer.ec = std::errc::result_out_of_range;
} }
return answer; return answer;
} }
template<typename T, typename UC> template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last, from_chars_advanced(UC const *first, UC const *last, T &value,
T &value, parse_options_t<UC> options) noexcept { parse_options_t<UC> options) noexcept {
static_assert (is_supported_float_type<T>(), "only some floating-point types are supported"); static_assert(is_supported_float_type<T>(),
static_assert (is_supported_char_type<UC>(), "only char, wchar_t, char16_t and char32_t are supported"); "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; from_chars_result_t<UC> answer;
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default #ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
@ -280,7 +305,8 @@ from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
answer.ptr = first; answer.ptr = first;
return answer; 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 (!pns.valid) {
if (options.format & chars_format::no_infnan) { if (options.format & chars_format::no_infnan) {
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
@ -295,11 +321,11 @@ from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
return from_chars_advanced(pns, value); return from_chars_advanced(pns, value);
} }
template <typename T, typename UC, typename> template <typename T, typename UC, typename>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_result_t<UC> from_chars(UC const* first, UC const* last, T& value, int base) noexcept { from_chars(UC const *first, UC const *last, T &value, int base) noexcept {
static_assert (is_supported_char_type<UC>(), "only char, wchar_t, char16_t and char32_t 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; from_chars_result_t<UC> answer;
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default #ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default

0
script/run-clangcldocker.sh Normal file → Executable file
View File

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,11 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get1(const char *input) {
double get1(const char* input) {
double result_value; double result_value;
auto result = fast_float::from_chars(input, input + strlen(input), result_value); auto result =
if (result.ec != std::errc()) { return 1; } fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 1;
}
return result_value; return result_value;
} }

View File

@ -1,9 +1,11 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get10(const char *input) {
double get10(const char* input) {
double result_value; double result_value;
auto result = fast_float::from_chars(input, input + strlen(input), result_value); auto result =
if (result.ec != std::errc()) { return 10; } fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 10;
}
return result_value; return result_value;
} }

View File

@ -1,9 +1,11 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get2(const char *input) {
double get2(const char* input) {
double result_value; double result_value;
auto result = fast_float::from_chars(input, input + strlen(input), result_value); auto result =
if (result.ec != std::errc()) { return 2; } fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 2;
}
return result_value; return result_value;
} }

View File

@ -1,9 +1,11 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get3(const char *input) {
double get3(const char* input) {
double result_value; double result_value;
auto result = fast_float::from_chars(input, input + strlen(input), result_value); auto result =
if (result.ec != std::errc()) { return 3; } fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 3;
}
return result_value; return result_value;
} }

View File

@ -1,9 +1,11 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get4(const char *input) {
double get4(const char* input) {
double result_value; double result_value;
auto result = fast_float::from_chars(input, input + strlen(input), result_value); auto result =
if (result.ec != std::errc()) { return 4; } fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 4;
}
return result_value; return result_value;
} }

View File

@ -1,9 +1,11 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get5(const char *input) {
double get5(const char* input) {
double result_value; double result_value;
auto result = fast_float::from_chars(input, input + strlen(input), result_value); auto result =
if (result.ec != std::errc()) { return 5; } fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 5;
}
return result_value; return result_value;
} }

View File

@ -1,9 +1,11 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get6(const char *input) {
double get6(const char* input) {
double result_value; double result_value;
auto result = fast_float::from_chars(input, input + strlen(input), result_value); auto result =
if (result.ec != std::errc()) { return 6; } fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 6;
}
return result_value; return result_value;
} }

View File

@ -1,9 +1,11 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get7(const char *input) {
double get7(const char* input) {
double result_value; double result_value;
auto result = fast_float::from_chars(input, input + strlen(input), result_value); auto result =
if (result.ec != std::errc()) { return 7; } fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 7;
}
return result_value; return result_value;
} }

View File

@ -1,9 +1,11 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get8(const char *input) {
double get8(const char* input) {
double result_value; double result_value;
auto result = fast_float::from_chars(input, input + strlen(input), result_value); auto result =
if (result.ec != std::errc()) { return 8; } fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 8;
}
return result_value; return result_value;
} }

View File

@ -1,9 +1,11 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get9(const char *input) {
double get9(const char* input) {
double result_value; double result_value;
auto result = fast_float::from_chars(input, input + strlen(input), result_value); auto result =
if (result.ec != std::errc()) { return 9; } fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 9;
}
return result_value; return result_value;
} }

View File

@ -1,19 +1,20 @@
double get1(const char* input); double get1(const char *input);
double get2(const char* input); double get2(const char *input);
double get3(const char* input); double get3(const char *input);
double get4(const char* input); double get4(const char *input);
double get5(const char* input); double get5(const char *input);
double get6(const char* input); double get6(const char *input);
double get7(const char* input); double get7(const char *input);
double get8(const char* input); double get8(const char *input);
double get9(const char* input); double get9(const char *input);
double get10(const char* input); double get10(const char *input);
int main(int arg, char** argv) { int main(int arg, char **argv) {
double x = get1(argv[0]) + get2(argv[0]) + get3(argv[0]) + get4(argv[0]) + get5(argv[0]) double x = get1(argv[0]) + get2(argv[0]) + get3(argv[0]) + get4(argv[0]) +
+ get6(argv[0]) + get7(argv[0]) + get8(argv[0]) + get9(argv[0]) + get10(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);
} }

View File

@ -1,13 +1,16 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get(const char * input) { double get(const char *input) {
double result_value; double result_value;
auto result = fast_float::from_chars(input, input + strlen(input), result_value); auto result =
if (result.ec != std::errc()) { return 10; } fast_float::from_chars(input, input + strlen(input), result_value);
if (result.ec != std::errc()) {
return 10;
}
return result_value; return result_value;
} }
int main(int arg, char** argv) { int main(int arg, char **argv) {
double x = get(argv[0]); double x = get(argv[0]);
return int(x); return int(x);
} }

View File

@ -1,2 +1,2 @@
#include "test.h" #include "test.h"
void foo() { } void foo() {}

View File

@ -8,8 +8,12 @@ int main() {
const std::string input = "3,1416 xyz "; const std::string input = "3,1416 xyz ";
double result; double result;
fast_float::parse_options options{fast_float::chars_format::general, ','}; 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); auto answer = fast_float::from_chars_advanced(
if((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } 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; std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -4,34 +4,51 @@
#include <string> #include <string>
#include <system_error> #include <system_error>
bool many() { bool many() {
const std::string input = "234532.3426362,7869234.9823,324562.645"; const std::string input = "234532.3426362,7869234.9823,324562.645";
double result; double result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result); auto answer =
if(answer.ec != std::errc()) { return false; } fast_float::from_chars(input.data(), input.data() + input.size(), result);
if(result != 234532.3426362) { return false; } if (answer.ec != std::errc()) {
if(answer.ptr[0] != ',') { return false; } return false;
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result); }
if(answer.ec != std::errc()) { return false; } if (result != 234532.3426362) {
if(result != 7869234.9823) { return false; } return false;
if(answer.ptr[0] != ',') { return false; } }
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result); if (answer.ptr[0] != ',') {
if(answer.ec != std::errc()) { return false; } return false;
if(result != 324562.645) { 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; return true;
} }
void many_loop() { 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; double result;
const char* pointer = input.data(); const char *pointer = input.data();
const char* end_pointer = input.data() + input.size(); 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); auto answer = fast_float::from_chars(pointer, end_pointer, result);
if(answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "error while parsing" << std::endl; std::cerr << "error while parsing" << std::endl;
break; break;
} }
@ -47,32 +64,37 @@ void many_loop() {
// consteval forces compile-time evaluation of the function in C++20. // consteval forces compile-time evaluation of the function in C++20.
consteval double parse(std::string_view input) { consteval double parse(std::string_view input) {
double result; double result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result); auto answer =
if(answer.ec != std::errc()) { return -1.0; } fast_float::from_chars(input.data(), input.data() + input.size(), result);
if (answer.ec != std::errc()) {
return -1.0;
}
return result; return result;
} }
// This function should compile to a function which // This function should compile to a function which
// merely returns 3.1415. // merely returns 3.1415.
constexpr double constexptest() { constexpr double constexptest() { return parse("3.1415 input"); }
return parse("3.1415 input");
}
#endif #endif
int main() { int main() {
const std::string input = "3.1416 xyz "; const std::string input = "3.1416 xyz ";
double result; double result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result); auto answer =
if((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } 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; std::cout << "parsed the number " << result << std::endl;
if(!many()) { if (!many()) {
printf("Bug\n"); printf("Bug\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
many_loop(); many_loop();
#if FASTFLOAT_IS_CONSTEXPR #if FASTFLOAT_IS_CONSTEXPR
if constexpr(constexptest() != 3.1415) { if constexpr (constexptest() != 3.1415) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
#endif #endif

View File

@ -30,10 +30,12 @@ void allvalues() {
const char *string_end = to_string(v, buffer); const char *string_end = to_string(v, buffer);
float result_value; float result_value;
auto result = fast_float::from_chars(buffer, string_end, 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 // Starting with version 4.0 for fast_float, we return result_out_of_range
// value is either too small (too close to zero) or too large (effectively infinity). // if the value is either too small (too close to zero) or too large
// So std::errc::result_out_of_range is normal for well-formed input strings. // (effectively infinity). So std::errc::result_out_of_range is normal for
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { // well-formed input strings.
if (result.ec != std::errc() &&
result.ec != std::errc::result_out_of_range) {
std::cerr << "parsing error ? " << buffer << std::endl; std::cerr << "parsing error ? " << buffer << std::endl;
abort(); abort();
} }
@ -42,9 +44,9 @@ void allvalues() {
std::cerr << "not nan" << buffer << std::endl; std::cerr << "not nan" << buffer << std::endl;
abort(); abort();
} }
} else if(copysign(1,result_value) != copysign(1,v)) { } else if (copysign(1, result_value) != copysign(1, v)) {
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v std::cerr << "I got " << std::hexfloat << result_value
<< std::endl; << " but I was expecting " << v << std::endl;
abort(); abort();
} else if (result_value != v) { } else if (result_value != v) {
std::cerr << "no match ? " << buffer << std::endl; std::cerr << "no match ? " << buffer << std::endl;

View File

@ -16,7 +16,6 @@ template <typename T> char *to_string(T d, char *buffer) {
return buffer + written; return buffer + written;
} }
bool basic_test_64bit(std::string vals, double val) { bool basic_test_64bit(std::string vals, double val) {
double result_value; double result_value;
auto result = fast_float::from_chars(vals.data(), vals.data() + vals.size(), auto result = fast_float::from_chars(vals.data(), vals.data() + vals.size(),
@ -31,14 +30,14 @@ bool basic_test_64bit(std::string vals, double val) {
std::cerr << "not nan" << result_value << std::endl; std::cerr << "not nan" << result_value << std::endl;
return false; return false;
} }
} else if(copysign(1,result_value) != copysign(1,val)) { } else if (copysign(1, result_value) != copysign(1, val)) {
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << val std::cerr << "I got " << std::hexfloat << result_value
<< std::endl; << " but I was expecting " << val << std::endl;
return false; return false;
} else if (result_value != val) { } else if (result_value != val) {
std::cerr << vals << std::endl; std::cerr << vals << std::endl;
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << val std::cerr << "I got " << std::hexfloat << result_value
<< std::endl; << " but I was expecting " << val << std::endl;
std::cerr << std::dec; std::cerr << std::dec;
std::cerr << "string: " << vals << std::endl; std::cerr << "string: " << vals << std::endl;
return false; return false;
@ -46,7 +45,6 @@ bool basic_test_64bit(std::string vals, double val) {
return true; return true;
} }
void all_32bit_values() { void all_32bit_values() {
char buffer[64]; char buffer[64];
for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) { for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) {
@ -61,8 +59,8 @@ void all_32bit_values() {
{ {
const char *string_end = to_string(v, buffer); const char *string_end = to_string(v, buffer);
std::string s(buffer, size_t(string_end-buffer)); std::string s(buffer, size_t(string_end - buffer));
if(!basic_test_64bit(s,v)) { if (!basic_test_64bit(s, v)) {
return; return;
} }
} }

View File

@ -15,28 +15,36 @@
#include <locale> #include <locale>
#include <sstream> #include <sstream>
// workaround for CYGWIN // workaround for CYGWIN
double cygwin_strtod_l(const char* start, char** end) { double cygwin_strtod_l(const char *start, char **end) {
double d; double d;
std::stringstream ss; std::stringstream ss;
ss.imbue(std::locale::classic()); ss.imbue(std::locale::classic());
ss << start; ss << start;
ss >> d; ss >> d;
if(ss.fail()) { *end = nullptr; } if (ss.fail()) {
if(ss.eof()) { ss.clear(); } *end = nullptr;
}
if (ss.eof()) {
ss.clear();
}
auto nread = ss.tellg(); auto nread = ss.tellg();
*end = const_cast<char*>(start) + nread; *end = const_cast<char *>(start) + nread;
return d; return d;
} }
float cygwin_strtof_l(const char* start, char** end) { float cygwin_strtof_l(const char *start, char **end) {
float d; float d;
std::stringstream ss; std::stringstream ss;
ss.imbue(std::locale::classic()); ss.imbue(std::locale::classic());
ss << start; ss << start;
ss >> d; ss >> d;
if(ss.fail()) { *end = nullptr; } if (ss.fail()) {
if(ss.eof()) { ss.clear(); } *end = nullptr;
}
if (ss.eof()) {
ss.clear();
}
auto nread = ss.tellg(); auto nread = ss.tellg();
*end = const_cast<char*>(start) + nread; *end = const_cast<char *>(start) + nread;
return d; return d;
} }
#endif #endif
@ -47,9 +55,10 @@ template <typename T> char *to_string(T d, char *buffer) {
return buffer + written; return buffer + written;
} }
void strtof_from_string(const char * st, float& d) { void strtof_from_string(const char *st, float &d) {
char *pr = (char *)st; char *pr = (char *)st;
#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); d = cygwin_strtof_l(st, &pr);
#elif defined(_WIN32) #elif defined(_WIN32)
static _locale_t c_locale = _create_locale(LC_ALL, "C"); static _locale_t c_locale = _create_locale(LC_ALL, "C");
@ -73,10 +82,14 @@ bool allvalues() {
} }
uint32_t word = uint32_t(w); uint32_t word = uint32_t(w);
memcpy(&v, &word, sizeof(v)); memcpy(&v, &word, sizeof(v));
if(std::isfinite(v)) { if (std::isfinite(v)) {
float nextf = std::nextafterf(v, INFINITY); float nextf = std::nextafterf(v, INFINITY);
if(copysign(1,v) != copysign(1,nextf)) { continue; } if (copysign(1, v) != copysign(1, nextf)) {
if(!std::isfinite(nextf)) { continue; } continue;
}
if (!std::isfinite(nextf)) {
continue;
}
double v1{v}; double v1{v};
assert(float(v1) == v); assert(float(v1) == v);
double v2{nextf}; double v2{nextf};
@ -90,10 +103,12 @@ bool allvalues() {
float result_value; float result_value;
auto result = fast_float::from_chars(buffer, string_end, 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 // Starting with version 4.0 for fast_float, we return result_out_of_range
// value is either too small (too close to zero) or too large (effectively infinity). // if the value is either too small (too close to zero) or too large
// So std::errc::result_out_of_range is normal for well-formed input strings. // (effectively infinity). So std::errc::result_out_of_range is normal for
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { // well-formed input strings.
if (result.ec != std::errc() &&
result.ec != std::errc::result_out_of_range) {
std::cerr << "parsing error ? " << buffer << std::endl; std::cerr << "parsing error ? " << buffer << std::endl;
return false; return false;
} }
@ -103,26 +118,30 @@ bool allvalues() {
std::cerr << "v " << std::hexfloat << v << std::endl; std::cerr << "v " << std::hexfloat << v << std::endl;
std::cerr << "v2 " << std::hexfloat << v2 << std::endl; std::cerr << "v2 " << std::hexfloat << v2 << std::endl;
std::cerr << "midv " << std::hexfloat << midv << 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; 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 << buffer << std::endl;
std::cerr << "v " << std::hexfloat << v << std::endl; std::cerr << "v " << std::hexfloat << v << std::endl;
std::cerr << "v2 " << std::hexfloat << v2 << std::endl; std::cerr << "v2 " << std::hexfloat << v2 << std::endl;
std::cerr << "midv " << std::hexfloat << midv << 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::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v
<< std::endl; << std::endl;
std::cerr << "I got " << std::hexfloat << result_value
<< " but I was expecting " << v << std::endl;
return false; return false;
} else if (result_value != str_answer) { } else if (result_value != str_answer) {
std::cerr << "no match ? " << buffer << std::endl; std::cerr << "no match ? " << buffer << std::endl;
std::cerr << "v " << std::hexfloat << v << std::endl; std::cerr << "v " << std::hexfloat << v << std::endl;
std::cerr << "v2 " << std::hexfloat << v2 << std::endl; std::cerr << "v2 " << std::hexfloat << v2 << std::endl;
std::cerr << "midv " << std::hexfloat << midv << 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 << "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 << "got back " << std::hexfloat << result_value << std::endl;
std::cout << std::dec; std::cout << std::dec;
return false; return false;
@ -134,15 +153,24 @@ bool allvalues() {
} }
inline void Assert(bool Assertion) { inline void Assert(bool Assertion) {
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
if (!Assertion) { std::cerr << "Omitting hard failure on msys/cygwin/sun systems."; } defined(sun) || defined(__sun)
if (!Assertion) {
std::cerr << "Omitting hard failure on msys/cygwin/sun systems.";
}
#else #else
if (!Assertion) { throw std::runtime_error("bug"); } if (!Assertion) {
throw std::runtime_error("bug");
}
#endif #endif
} }
int main() { int main() {
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
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; 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 #endif
Assert(allvalues()); Assert(allvalues());
std::cout << std::endl; std::cout << std::endl;

View File

@ -11,10 +11,8 @@
#endif #endif
#endif #endif
#if FASTFLOAT_CPLUSPLUS >= 201703L #if FASTFLOAT_CPLUSPLUS >= 201703L
#include <cstdlib> #include <cstdlib>
#include <iostream> #include <iostream>
#include <vector> #include <vector>
@ -24,29 +22,33 @@
#include <cstdint> #include <cstdint>
/* /*
all tests conducted are to check fast_float::from_chars functionality with int and unsigned all tests conducted are to check fast_float::from_chars functionality with int
test cases include: and unsigned test cases include: int basic tests - numbers only, numbers with
int basic tests - numbers only, numbers with strings behind, decimals, negative numbers strings behind, decimals, negative numbers unsigned basic tests - numbers only,
unsigned basic tests - numbers only, numbers with strings behind, decimals numbers with strings behind, decimals int invalid tests - strings only, strings
int invalid tests - strings only, strings with numbers behind, space in front of number, plus sign in front of number with numbers behind, space in front of number, plus sign in front of number
unsigned invalid tests - strings only, strings with numbers behind, space in front of number, plus/minus sign in front of number unsigned invalid tests - strings only, strings with numbers behind, space in
int out of range tests - numbers exceeding int bit size for 8, 16, 32, and 64 bits front of number, plus/minus sign in front of number int out of range tests -
unsigned out of range tests - numbers exceeding unsigned bit size 8, 16, 32, and 64 bits numbers exceeding int bit size for 8, 16, 32, and 64 bits unsigned out of range
int pointer tests - points to first character that is not recognized as int tests - numbers exceeding unsigned bit size 8, 16, 32, and 64 bits int pointer
unsigned pointer tests - points to first character that is not recognized as unsigned tests - points to first character that is not recognized as int unsigned pointer
tests - points to first character that is not recognized as unsigned
int/unsigned base 2 tests - numbers are converted from binary to decimal int/unsigned base 2 tests - numbers are converted from binary to decimal
octal tests - numbers are converted from octal to decimal octal tests - numbers are converted from octal to decimal
hex tests - numbers are converted from hex to decimal (Note: 0x and 0X are considered invalid) hex tests - numbers are converted from hex to decimal (Note: 0x and 0X are
invalid base tests - any base not within 2-36 is invalid considered invalid) invalid base tests - any base not within 2-36 is invalid out
out of range base tests - numbers exceeding int/unsigned bit size after converted from base (Note: only 64 bit int and unsigned are tested) of range base tests - numbers exceeding int/unsigned bit size after converted
within range base tests - max/min numbers are still within int/unsigned bit size after converted from base (Note: only 64 bit int and unsigned are tested) from base (Note: only 64 bit int and unsigned are tested) within range base
leading zeros tests - ignores all zeroes in front of valid number after converted from base tests - max/min numbers are still within int/unsigned bit size after converted
from base (Note: only 64 bit int and unsigned are tested) leading zeros tests -
ignores all zeroes in front of valid number after converted from base
*/ */
int main() { int main() {
// int basic test // int basic test
const std::vector<int> int_basic_test_expected { 0, 10, -40, 1001, 9 }; const std::vector<int> int_basic_test_expected{0, 10, -40, 1001, 9};
const std::vector<std::string_view> int_basic_test { "0", "10 ", "-40", "1001 with text", "9.999" }; const std::vector<std::string_view> int_basic_test{"0", "10 ", "-40",
"1001 with text", "9.999"};
for (std::size_t i = 0; i < int_basic_test.size(); ++i) { for (std::size_t i = 0; i < int_basic_test.size(); ++i) {
const auto f = int_basic_test[i]; const auto f = int_basic_test[i];
@ -55,183 +57,208 @@ int main() {
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
if (answer.ec == std::errc::invalid_argument) { if (answer.ec == std::errc::invalid_argument) {
std::cerr << "could not convert to int for input: \"" << f << "\" because of invalid argument" << std::endl; std::cerr << "could not convert to int for input: \"" << f
} << "\" because of invalid argument" << std::endl;
else if (answer.ec == std::errc::result_out_of_range) { } else if (answer.ec == std::errc::result_out_of_range) {
std::cerr << "could not convert to int for input: \"" << f << "\" because it's out of range" << std::endl; std::cerr << "could not convert to int for input: \"" << f
} << "\" because it's out of range" << std::endl;
else { } else {
std::cerr << "could not convert to int for input: \"" << f << "\" because of an unknown error" << std::endl; std::cerr << "could not convert to int for input: \"" << f
<< "\" because of an unknown error" << std::endl;
} }
return EXIT_FAILURE; return EXIT_FAILURE;
} } else if (result != int_basic_test_expected[i]) {
else if (result != int_basic_test_expected[i]) { std::cerr << "result \"" << f << "\" did not match with expected int: "
std::cerr << "result \"" << f << "\" did not match with expected int: " << int_basic_test_expected[i] << std::endl; << int_basic_test_expected[i] << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// unsigned basic test // unsigned basic test
const std::vector<unsigned> unsigned_basic_test_expected { 0, 10, 1001, 9 }; const std::vector<unsigned> unsigned_basic_test_expected{0, 10, 1001, 9};
const std::vector<std::string_view> unsigned_basic_test { "0", "10 ", "1001 with text", "9.999" }; const std::vector<std::string_view> unsigned_basic_test{
"0", "10 ", "1001 with text", "9.999"};
for (std::size_t i = 0; i < unsigned_basic_test.size(); ++i) { for (std::size_t i = 0; i < unsigned_basic_test.size(); ++i) {
const auto& f = unsigned_basic_test[i]; const auto &f = unsigned_basic_test[i];
unsigned result; unsigned result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "could not convert to unsigned for input: \"" << f << "\"" << std::endl; std::cerr << "could not convert to unsigned for input: \"" << f << "\""
<< std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} } else if (result != unsigned_basic_test_expected[i]) {
else if (result != unsigned_basic_test_expected[i]) { std::cerr << "result \"" << f
std::cerr << "result \"" << f << "\" did not match with expected unsigned: " << unsigned_basic_test_expected[i] << std::endl; << "\" did not match with expected unsigned: "
<< unsigned_basic_test_expected[i] << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// int invalid error test // int invalid error test
const std::vector<std::string_view> int_invalid_argument_test{ "text", "text with 1002", "+50", " 50" }; const std::vector<std::string_view> int_invalid_argument_test{
"text", "text with 1002", "+50", " 50"};
for (std::size_t i = 0; i < int_invalid_argument_test.size(); ++i) { for (std::size_t i = 0; i < int_invalid_argument_test.size(); ++i) {
const auto& f = int_invalid_argument_test[i]; const auto &f = int_invalid_argument_test[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::invalid_argument) { if (answer.ec != std::errc::invalid_argument) {
std::cerr << "expected error should be 'invalid_argument' for: \"" << f << "\"" << std::endl; std::cerr << "expected error should be 'invalid_argument' for: \"" << f
<< "\"" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// unsigned invalid error test // unsigned invalid error test
const std::vector<std::string_view> unsigned_invalid_argument_test{ "text", "text with 1002", "+50", " 50", "-50" }; const std::vector<std::string_view> unsigned_invalid_argument_test{
"text", "text with 1002", "+50", " 50", "-50"};
for (std::size_t i = 0; i < unsigned_invalid_argument_test.size(); ++i) { for (std::size_t i = 0; i < unsigned_invalid_argument_test.size(); ++i) {
const auto& f = unsigned_invalid_argument_test[i]; const auto &f = unsigned_invalid_argument_test[i];
unsigned result; unsigned result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::invalid_argument) { if (answer.ec != std::errc::invalid_argument) {
std::cerr << "expected error should be 'invalid_argument' for: \"" << f << "\"" << std::endl; std::cerr << "expected error should be 'invalid_argument' for: \"" << f
<< "\"" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// int out of range error test #1 (8 bit) // int out of range error test #1 (8 bit)
const std::vector<std::string_view> int_out_of_range_test_1{ "2000000000000000000000", "128", "-129"}; const std::vector<std::string_view> int_out_of_range_test_1{
"2000000000000000000000", "128", "-129"};
for (std::size_t i = 0; i < int_out_of_range_test_1.size(); ++i) { for (std::size_t i = 0; i < int_out_of_range_test_1.size(); ++i) {
const auto& f = int_out_of_range_test_1[i]; const auto &f = int_out_of_range_test_1[i];
int8_t result; int8_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
std::cerr << "expected error for should be 'result_out_of_range': \"" << f << "\"" << std::endl; std::cerr << "expected error for should be 'result_out_of_range': \"" << f
<< "\"" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// int out of range error test #2 (16 bit) // int out of range error test #2 (16 bit)
const std::vector<std::string_view> int_out_of_range_test_2{ "2000000000000000000000", "32768", "-32769"}; const std::vector<std::string_view> int_out_of_range_test_2{
"2000000000000000000000", "32768", "-32769"};
for (std::size_t i = 0; i < int_out_of_range_test_2.size(); ++i) { for (std::size_t i = 0; i < int_out_of_range_test_2.size(); ++i) {
const auto& f = int_out_of_range_test_2[i]; const auto &f = int_out_of_range_test_2[i];
int16_t result; int16_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
std::cerr << "expected error for should be 'result_out_of_range': \"" << f << "\"" << std::endl; std::cerr << "expected error for should be 'result_out_of_range': \"" << f
<< "\"" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// int out of range error test #3 (32 bit) // int out of range error test #3 (32 bit)
const std::vector<std::string_view> int_out_of_range_test_3{ "2000000000000000000000", "2147483648", "-2147483649"}; const std::vector<std::string_view> int_out_of_range_test_3{
"2000000000000000000000", "2147483648", "-2147483649"};
for (std::size_t i = 0; i < int_out_of_range_test_3.size(); ++i) { for (std::size_t i = 0; i < int_out_of_range_test_3.size(); ++i) {
const auto& f = int_out_of_range_test_3[i]; const auto &f = int_out_of_range_test_3[i];
int32_t result; int32_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
std::cerr << "expected error for should be 'result_out_of_range': \"" << f << "\"" << std::endl; std::cerr << "expected error for should be 'result_out_of_range': \"" << f
<< "\"" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// int out of range error test #4 (64 bit) // int out of range error test #4 (64 bit)
const std::vector<std::string_view> int_out_of_range_test_4{ "2000000000000000000000", "9223372036854775808", "-9223372036854775809"}; const std::vector<std::string_view> int_out_of_range_test_4{
"2000000000000000000000", "9223372036854775808", "-9223372036854775809"};
for (std::size_t i = 0; i < int_out_of_range_test_4.size(); ++i) { for (std::size_t i = 0; i < int_out_of_range_test_4.size(); ++i) {
const auto& f = int_out_of_range_test_4[i]; const auto &f = int_out_of_range_test_4[i];
int64_t result; int64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
std::cerr << "expected error for should be 'result_out_of_range': \"" << f << "\"" << std::endl; std::cerr << "expected error for should be 'result_out_of_range': \"" << f
<< "\"" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// unsigned out of range error test #1 (8 bit) // unsigned out of range error test #1 (8 bit)
const std::vector<std::string_view> unsigned_out_of_range_test_1{ "2000000000000000000000", "256" }; const std::vector<std::string_view> unsigned_out_of_range_test_1{
"2000000000000000000000", "256"};
for (std::size_t i = 0; i < unsigned_out_of_range_test_1.size(); ++i) { for (std::size_t i = 0; i < unsigned_out_of_range_test_1.size(); ++i) {
const auto& f = unsigned_out_of_range_test_1[i]; const auto &f = unsigned_out_of_range_test_1[i];
uint8_t result; uint8_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
std::cerr << "expected error for should be 'result_out_of_range': \"" << f << "\"" << std::endl; std::cerr << "expected error for should be 'result_out_of_range': \"" << f
<< "\"" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// unsigned out of range error test #2 (16 bit) // unsigned out of range error test #2 (16 bit)
const std::vector<std::string_view> unsigned_out_of_range_test_2{ "2000000000000000000000", "65536" }; const std::vector<std::string_view> unsigned_out_of_range_test_2{
"2000000000000000000000", "65536"};
for (std::size_t i = 0; i < unsigned_out_of_range_test_2.size(); ++i) { for (std::size_t i = 0; i < unsigned_out_of_range_test_2.size(); ++i) {
const auto& f = unsigned_out_of_range_test_2[i]; const auto &f = unsigned_out_of_range_test_2[i];
uint16_t result; uint16_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
std::cerr << "expected error for should be 'result_out_of_range': \"" << f << "\"" << std::endl; std::cerr << "expected error for should be 'result_out_of_range': \"" << f
<< "\"" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// unsigned out of range error test #3 (32 bit) // unsigned out of range error test #3 (32 bit)
const std::vector<std::string_view> unsigned_out_of_range_test_3{ "2000000000000000000000", "4294967296" }; const std::vector<std::string_view> unsigned_out_of_range_test_3{
"2000000000000000000000", "4294967296"};
for (std::size_t i = 0; i < unsigned_out_of_range_test_3.size(); ++i) { for (std::size_t i = 0; i < unsigned_out_of_range_test_3.size(); ++i) {
const auto& f = unsigned_out_of_range_test_3[i]; const auto &f = unsigned_out_of_range_test_3[i];
uint32_t result; uint32_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
std::cerr << "expected error for should be 'result_out_of_range': \"" << f << "\"" << std::endl; std::cerr << "expected error for should be 'result_out_of_range': \"" << f
<< "\"" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// unsigned out of range error test #4 (64 bit) // unsigned out of range error test #4 (64 bit)
const std::vector<std::string_view> unsigned_out_of_range_test_4{ "2000000000000000000000", "18446744073709551616" }; const std::vector<std::string_view> unsigned_out_of_range_test_4{
"2000000000000000000000", "18446744073709551616"};
for (std::size_t i = 0; i < unsigned_out_of_range_test_4.size(); ++i) { for (std::size_t i = 0; i < unsigned_out_of_range_test_4.size(); ++i) {
const auto& f = unsigned_out_of_range_test_4[i]; const auto &f = unsigned_out_of_range_test_4[i];
uint64_t result; uint64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
std::cerr << "expected error for should be 'result_out_of_range': \"" << f << "\"" << std::endl; std::cerr << "expected error for should be 'result_out_of_range': \"" << f
<< "\"" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// int pointer test #1 (only numbers) // int pointer test #1 (only numbers)
const std::vector<std::string_view> int_pointer_test_1 { "0", "010", "-40" }; const std::vector<std::string_view> int_pointer_test_1{"0", "010", "-40"};
for (std::size_t i = 0; i < int_pointer_test_1.size(); ++i) { for (std::size_t i = 0; i < int_pointer_test_1.size(); ++i) {
const auto& f = int_pointer_test_1[i]; const auto &f = int_pointer_test_1[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "could not convert to int for input: \"" << f << "\"" << std::endl; std::cerr << "could not convert to int for input: \"" << f << "\""
<< std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} } else if (strcmp(answer.ptr, "") != 0) {
else if (strcmp(answer.ptr, "") != 0) { std::cerr << "ptr of result " << f
std::cerr << "ptr of result " << f << " did not match with expected ptr: \"\"" << std::endl; << " did not match with expected ptr: \"\"" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
@ -239,195 +266,233 @@ int main() {
// int pointer test #2 (string behind numbers) // int pointer test #2 (string behind numbers)
const std::string_view int_pointer_test_2 = "1001 with text"; const std::string_view int_pointer_test_2 = "1001 with text";
const auto& f2 = int_pointer_test_2; const auto &f2 = int_pointer_test_2;
int result2; int result2;
auto answer2 = fast_float::from_chars(f2.data(), f2.data() + f2.size(), result2); auto answer2 =
fast_float::from_chars(f2.data(), f2.data() + f2.size(), result2);
if (strcmp(answer2.ptr, " with text") != 0) { if (strcmp(answer2.ptr, " with text") != 0) {
std::cerr << "ptr of result " << f2 << " did not match with expected ptr: \"with text\"" << std::endl; std::cerr << "ptr of result " << f2
<< " did not match with expected ptr: \"with text\"" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// int pointer test #3 (string with newline behind numbers) // int pointer test #3 (string with newline behind numbers)
const std::string_view int_pointer_test_3 = "1001 with text\n"; const std::string_view int_pointer_test_3 = "1001 with text\n";
const auto& f3 = int_pointer_test_3; const auto &f3 = int_pointer_test_3;
int result3; int result3;
auto answer3 = fast_float::from_chars(f3.data(), f3.data() + f3.size(), result3); auto answer3 =
fast_float::from_chars(f3.data(), f3.data() + f3.size(), result3);
if (strcmp(answer3.ptr, " with text\n") != 0) { if (strcmp(answer3.ptr, " with text\n") != 0) {
std::cerr << "ptr of result " << f3 << " did not match with expected ptr: with text" << std::endl; std::cerr << "ptr of result " << f3
<< " did not match with expected ptr: with text" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// int pointer test #4 (float) // int pointer test #4 (float)
const std::string_view int_pointer_test_4 = "9.999"; const std::string_view int_pointer_test_4 = "9.999";
const auto& f4 = int_pointer_test_4; const auto &f4 = int_pointer_test_4;
int result4; int result4;
auto answer4 = fast_float::from_chars(f4.data(), f4.data() + f4.size(), result4); auto answer4 =
fast_float::from_chars(f4.data(), f4.data() + f4.size(), result4);
if (strcmp(answer4.ptr, ".999") != 0) { if (strcmp(answer4.ptr, ".999") != 0) {
std::cerr << "ptr of result " << f4 << " did not match with expected ptr: .999" << std::endl; std::cerr << "ptr of result " << f4
<< " did not match with expected ptr: .999" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// int pointer test #5 (invalid int) // int pointer test #5 (invalid int)
const std::string_view int_pointer_test_5 = "+50"; const std::string_view int_pointer_test_5 = "+50";
const auto& f5 = int_pointer_test_5; const auto &f5 = int_pointer_test_5;
int result5; int result5;
auto answer5 = fast_float::from_chars(f5.data(), f5.data() + f5.size(), result5); auto answer5 =
fast_float::from_chars(f5.data(), f5.data() + f5.size(), result5);
if (strcmp(answer5.ptr, "+50") != 0) { if (strcmp(answer5.ptr, "+50") != 0) {
std::cerr << "ptr of result " << f5 << " did not match with expected ptr: +50" << std::endl; std::cerr << "ptr of result " << f5
<< " did not match with expected ptr: +50" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// unsigned pointer test #2 (string behind numbers) // unsigned pointer test #2 (string behind numbers)
const std::string_view unsigned_pointer_test_1 = "1001 with text"; const std::string_view unsigned_pointer_test_1 = "1001 with text";
const auto& f6 = unsigned_pointer_test_1; const auto &f6 = unsigned_pointer_test_1;
unsigned result6; unsigned result6;
auto answer6 = fast_float::from_chars(f6.data(), f6.data() + f6.size(), result6); auto answer6 =
fast_float::from_chars(f6.data(), f6.data() + f6.size(), result6);
if (strcmp(answer6.ptr, " with text") != 0) { if (strcmp(answer6.ptr, " with text") != 0) {
std::cerr << "ptr of result " << f6 << " did not match with expected ptr: with text" << std::endl; std::cerr << "ptr of result " << f6
<< " did not match with expected ptr: with text" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// unsigned pointer test #2 (invalid unsigned) // unsigned pointer test #2 (invalid unsigned)
const std::string_view unsigned_pointer_test_2 = "-50"; const std::string_view unsigned_pointer_test_2 = "-50";
const auto& f7 = unsigned_pointer_test_2; const auto &f7 = unsigned_pointer_test_2;
unsigned result7; unsigned result7;
auto answer7 = fast_float::from_chars(f7.data(), f7.data() + f7.size(), result7); auto answer7 =
fast_float::from_chars(f7.data(), f7.data() + f7.size(), result7);
if (strcmp(answer7.ptr, "-50") != 0) { if (strcmp(answer7.ptr, "-50") != 0) {
std::cerr << "ptr of result " << f7 << " did not match with expected ptr: -50" << std::endl; std::cerr << "ptr of result " << f7
<< " did not match with expected ptr: -50" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// int base 2 test // int base 2 test
const std::vector<int> int_base_2_test_expected { 0, 1, 4, 2, -1 }; const std::vector<int> int_base_2_test_expected{0, 1, 4, 2, -1};
const std::vector<std::string_view> int_base_2_test { "0", "1", "100", "010", "-1" }; const std::vector<std::string_view> int_base_2_test{"0", "1", "100", "010",
"-1"};
for (std::size_t i = 0; i < int_base_2_test.size(); ++i) { for (std::size_t i = 0; i < int_base_2_test.size(); ++i) {
const auto f = int_base_2_test[i]; const auto f = int_base_2_test[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); auto answer =
fast_float::from_chars(f.data(), f.data() + f.size(), result, 2);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "could not convert to int for input: \"" << f << "\"" << std::endl; std::cerr << "could not convert to int for input: \"" << f << "\""
<< std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} } else if (result != int_base_2_test_expected[i]) {
else if (result != int_base_2_test_expected[i]) { std::cerr << "result " << f << " did not match with expected int: "
std::cerr << "result " << f << " did not match with expected int: " << int_base_2_test_expected[i] << std::endl; << int_base_2_test_expected[i] << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// unsigned base 2 test // unsigned base 2 test
const std::vector<unsigned> unsigned_base_2_test_expected { 0, 1, 4, 2 }; const std::vector<unsigned> unsigned_base_2_test_expected{0, 1, 4, 2};
const std::vector<std::string_view> unsigned_base_2_test { "0", "1", "100", "010" }; const std::vector<std::string_view> unsigned_base_2_test{"0", "1", "100",
"010"};
for (std::size_t i = 0; i < unsigned_base_2_test.size(); ++i) { for (std::size_t i = 0; i < unsigned_base_2_test.size(); ++i) {
const auto& f = unsigned_base_2_test[i]; const auto &f = unsigned_base_2_test[i];
unsigned result; unsigned result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); auto answer =
fast_float::from_chars(f.data(), f.data() + f.size(), result, 2);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "could not convert to unsigned for input: \"" << f << "\"" << std::endl; std::cerr << "could not convert to unsigned for input: \"" << f << "\""
<< std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} } else if (result != unsigned_base_2_test_expected[i]) {
else if (result != unsigned_base_2_test_expected[i]) { std::cerr << "result " << f << " did not match with expected unsigned: "
std::cerr << "result " << f << " did not match with expected unsigned: " << unsigned_base_2_test_expected[i] << std::endl; << unsigned_base_2_test_expected[i] << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// int invalid error base 2 test // int invalid error base 2 test
const std::vector<std::string_view> int_invalid_argument_base_2_test{ "2", "A", "-2" }; const std::vector<std::string_view> int_invalid_argument_base_2_test{"2", "A",
"-2"};
for (std::size_t i = 0; i < int_invalid_argument_base_2_test.size(); ++i) { for (std::size_t i = 0; i < int_invalid_argument_base_2_test.size(); ++i) {
const auto& f = int_invalid_argument_base_2_test[i]; const auto &f = int_invalid_argument_base_2_test[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); auto answer =
fast_float::from_chars(f.data(), f.data() + f.size(), result, 2);
if (answer.ec != std::errc::invalid_argument) { if (answer.ec != std::errc::invalid_argument) {
std::cerr << "expected error should be 'invalid_argument' for: \"" << f << "\"" << std::endl; std::cerr << "expected error should be 'invalid_argument' for: \"" << f
<< "\"" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// unsigned invalid error base 2 test // unsigned invalid error base 2 test
const std::vector<std::string_view> unsigned_invalid_argument_base_2_test{ "2", "A", "-1", "-2" }; const std::vector<std::string_view> unsigned_invalid_argument_base_2_test{
"2", "A", "-1", "-2"};
for (std::size_t i = 0; i < unsigned_invalid_argument_base_2_test.size(); ++i) { for (std::size_t i = 0; i < unsigned_invalid_argument_base_2_test.size();
const auto& f = unsigned_invalid_argument_base_2_test[i]; ++i) {
const auto &f = unsigned_invalid_argument_base_2_test[i];
unsigned result; unsigned result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); auto answer =
fast_float::from_chars(f.data(), f.data() + f.size(), result, 2);
if (answer.ec != std::errc::invalid_argument) { if (answer.ec != std::errc::invalid_argument) {
std::cerr << "expected error should be 'invalid_argument' for: \"" << f << "\"" << std::endl; std::cerr << "expected error should be 'invalid_argument' for: \"" << f
<< "\"" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// octal test // octal test
const std::vector<int> base_octal_test_expected {0, 1, 7, 8, 9}; const std::vector<int> base_octal_test_expected{0, 1, 7, 8, 9};
const std::vector<std::string_view> base_octal_test { "0", "1", "07", "010", "0011" }; const std::vector<std::string_view> base_octal_test{"0", "1", "07", "010",
"0011"};
for (std::size_t i = 0; i < base_octal_test.size(); ++i) { for (std::size_t i = 0; i < base_octal_test.size(); ++i) {
const auto& f = base_octal_test[i]; const auto &f = base_octal_test[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 8); auto answer =
fast_float::from_chars(f.data(), f.data() + f.size(), result, 8);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "could not convert to int for input: \"" << f << "\"" << std::endl; std::cerr << "could not convert to int for input: \"" << f << "\""
<< std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} } else if (result != base_octal_test_expected[i]) {
else if (result != base_octal_test_expected[i]) { std::cerr << "result " << f << " did not match with expected int: "
std::cerr << "result " << f << " did not match with expected int: " << base_octal_test_expected[i] << std::endl; << base_octal_test_expected[i] << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// hex test // hex test
const std::vector<int> base_hex_test_expected { 0, 1, 15, 31, 0, 16}; const std::vector<int> base_hex_test_expected{0, 1, 15, 31, 0, 16};
const std::vector<std::string_view> base_hex_test { "0", "1", "F", "01f", "0x11", "10X11" }; const std::vector<std::string_view> base_hex_test{"0", "1", "F",
"01f", "0x11", "10X11"};
for (std::size_t i = 0; i < base_hex_test.size(); ++i) { for (std::size_t i = 0; i < base_hex_test.size(); ++i) {
const auto& f = base_hex_test[i]; const auto &f = base_hex_test[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 16); auto answer =
fast_float::from_chars(f.data(), f.data() + f.size(), result, 16);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "could not convert to int for input: \"" << f << "\"" << std::endl; std::cerr << "could not convert to int for input: \"" << f << "\""
<< std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} } else if (result != base_hex_test_expected[i]) {
else if (result != base_hex_test_expected[i]) { std::cerr << "result " << f << " did not match with expected int: "
std::cerr << "result " << f << " did not match with expected int: " << base_hex_test_expected[i] << std::endl; << base_hex_test_expected[i] << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// invalid base test #1 (-1) // invalid base test #1 (-1)
const std::vector<std::string_view> invalid_base_test_1 { "0", "1", "-1", "F", "10Z" }; const std::vector<std::string_view> invalid_base_test_1{"0", "1", "-1", "F",
"10Z"};
for (std::size_t i = 0; i < invalid_base_test_1.size(); ++i) { for (std::size_t i = 0; i < invalid_base_test_1.size(); ++i) {
const auto& f = invalid_base_test_1[i]; const auto &f = invalid_base_test_1[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, -1); auto answer =
fast_float::from_chars(f.data(), f.data() + f.size(), result, -1);
if (answer.ec != std::errc::invalid_argument) { if (answer.ec != std::errc::invalid_argument) {
std::cerr << "expected error should be 'invalid_argument' for: \"" << f << "\"" << std::endl; std::cerr << "expected error should be 'invalid_argument' for: \"" << f
<< "\"" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// invalid base test #2 (37) // invalid base test #2 (37)
const std::vector<std::string_view> invalid_base_test_2 { "0", "1", "F", "Z", "10Z" }; const std::vector<std::string_view> invalid_base_test_2{"0", "1", "F", "Z",
"10Z"};
for (std::size_t i = 0; i < invalid_base_test_2.size(); ++i) { for (std::size_t i = 0; i < invalid_base_test_2.size(); ++i) {
const auto& f = invalid_base_test_2[i]; const auto &f = invalid_base_test_2[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 37); auto answer =
fast_float::from_chars(f.data(), f.data() + f.size(), result, 37);
if (answer.ec != std::errc::invalid_argument) { if (answer.ec != std::errc::invalid_argument) {
std::cerr << "expected error should be 'invalid_argument' for: \"" << f << "\"" << std::endl; std::cerr << "expected error should be 'invalid_argument' for: \"" << f
<< "\"" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// int out of range error base test (64 bit) // int out of range error base test (64 bit)
const std::vector<std::string_view> int_out_of_range_base_test { "1000000000000000000000000000000000000000000000000000000000000000", const std::vector<std::string_view> int_out_of_range_base_test{
"1000000000000000000000000000000000000000000000000000000000000000",
"-1000000000000000000000000000000000000000000000000000000000000001", "-1000000000000000000000000000000000000000000000000000000000000001",
"2021110011022210012102010021220101220222", "2021110011022210012102010021220101220222",
"-2021110011022210012102010021220101221000", "-2021110011022210012102010021220101221000",
@ -496,20 +561,23 @@ int main() {
"2PIJMIKEXRXP8", "2PIJMIKEXRXP8",
"-2PIJMIKEXRXP9", "-2PIJMIKEXRXP9",
"1Y2P0IJ32E8E8", "1Y2P0IJ32E8E8",
"-1Y2P0IJ32E8E9" }; "-1Y2P0IJ32E8E9"};
for (std::size_t i = 0; i < int_out_of_range_base_test.size(); ++i) { for (std::size_t i = 0; i < int_out_of_range_base_test.size(); ++i) {
const auto& f = int_out_of_range_base_test[i]; const auto &f = int_out_of_range_base_test[i];
int64_t result; int64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, int(2 + (i / 2))); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result,
int(2 + (i / 2)));
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
std::cerr << "expected error for should be 'result_out_of_range': \"" << f << "\"" << std::endl; std::cerr << "expected error for should be 'result_out_of_range': \"" << f
<< "\"" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// unsigned out of range error base test (64 bit) // unsigned out of range error base test (64 bit)
const std::vector<std::string_view> unsigned_out_of_range_base_test { "10000000000000000000000000000000000000000000000000000000000000000", const std::vector<std::string_view> unsigned_out_of_range_base_test{
"10000000000000000000000000000000000000000000000000000000000000000",
"11112220022122120101211020120210210211221", "11112220022122120101211020120210210211221",
"100000000000000000000000000000000", "100000000000000000000000000000000",
"2214220303114400424121122431", "2214220303114400424121122431",
@ -543,21 +611,24 @@ int main() {
"B1W8P7J5Q9R6G", "B1W8P7J5Q9R6G",
"7ORP63SH4DPHI", "7ORP63SH4DPHI",
"5G24A25TWKWFG", "5G24A25TWKWFG",
"3W5E11264SGSG" }; "3W5E11264SGSG"};
int base_unsigned = 2; int base_unsigned = 2;
for (std::size_t i = 0; i < unsigned_out_of_range_base_test.size(); ++i) { for (std::size_t i = 0; i < unsigned_out_of_range_base_test.size(); ++i) {
const auto& f = unsigned_out_of_range_base_test[i]; const auto &f = unsigned_out_of_range_base_test[i];
uint64_t result; uint64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, base_unsigned); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result,
base_unsigned);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
std::cerr << "expected error for should be 'result_out_of_range': \"" << f << "\"" << std::endl; std::cerr << "expected error for should be 'result_out_of_range': \"" << f
<< "\"" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
++base_unsigned; ++base_unsigned;
} }
// just within range base test (64 bit) // just within range base test (64 bit)
const std::vector<std::string_view> int_within_range_base_test { "111111111111111111111111111111111111111111111111111111111111111", const std::vector<std::string_view> int_within_range_base_test{
"111111111111111111111111111111111111111111111111111111111111111",
"-1000000000000000000000000000000000000000000000000000000000000000", "-1000000000000000000000000000000000000000000000000000000000000000",
"2021110011022210012102010021220101220221", "2021110011022210012102010021220101220221",
"-2021110011022210012102010021220101220222", "-2021110011022210012102010021220101220222",
@ -626,20 +697,23 @@ int main() {
"2PIJMIKEXRXP7", "2PIJMIKEXRXP7",
"-2PIJMIKEXRXP8", "-2PIJMIKEXRXP8",
"1Y2P0IJ32E8E7", "1Y2P0IJ32E8E7",
"-1Y2P0IJ32E8E8" }; "-1Y2P0IJ32E8E8"};
for (std::size_t i = 0; i < int_within_range_base_test.size(); ++i) { for (std::size_t i = 0; i < int_within_range_base_test.size(); ++i) {
const auto& f = int_within_range_base_test[i]; const auto &f = int_within_range_base_test[i];
int64_t result; int64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, int(2 + (i / 2))); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result,
int(2 + (i / 2)));
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "converting " << f << " to int failed (most likely out of range)" << std::endl; std::cerr << "converting " << f
<< " to int failed (most likely out of range)" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// unsigned within range base test (64 bit) // unsigned within range base test (64 bit)
const std::vector<std::string_view> unsigned_within_range_base_test { "1111111111111111111111111111111111111111111111111111111111111111", const std::vector<std::string_view> unsigned_within_range_base_test{
"1111111111111111111111111111111111111111111111111111111111111111",
"11112220022122120101211020120210210211220", "11112220022122120101211020120210210211220",
"33333333333333333333333333333333", "33333333333333333333333333333333",
"2214220303114400424121122430", "2214220303114400424121122430",
@ -673,21 +747,26 @@ int main() {
"B1W8P7J5Q9R6F", "B1W8P7J5Q9R6F",
"7ORP63SH4DPHH", "7ORP63SH4DPHH",
"5G24A25TWKWFF", "5G24A25TWKWFF",
"3W5E11264SGSF" }; "3W5E11264SGSF"};
int base_unsigned2 = 2; int base_unsigned2 = 2;
for (std::size_t i = 0; i < unsigned_within_range_base_test.size(); ++i) { for (std::size_t i = 0; i < unsigned_within_range_base_test.size(); ++i) {
const auto& f = unsigned_within_range_base_test[i]; const auto &f = unsigned_within_range_base_test[i];
uint64_t result; uint64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, base_unsigned2); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result,
base_unsigned2);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "converting " << f << " to unsigned failed (most likely out of range)" << std::endl; std::cerr << "converting " << f
<< " to unsigned failed (most likely out of range)"
<< std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
++base_unsigned2; ++base_unsigned2;
} }
// int leading zeros test // int leading zeros test
const std::vector<std::string_view> int_leading_zeros_test { "00000000000000000000000000000000000000000000000000000000000000000000001111110111", const std::vector<std::string_view> int_leading_zeros_test{
"000000000000000000000000000000000000000000000000000000000000000000000011"
"11110111",
"000000000000000000000000000000000000000000000000001101121", "000000000000000000000000000000000000000000000000001101121",
"000000000000000000000000000000000000000033313", "000000000000000000000000000000000000000033313",
"00000000000000000000000000000013030", "00000000000000000000000000000013030",
@ -721,18 +800,20 @@ int main() {
"00000000000000000000UP", "00000000000000000000UP",
"00000000000000000000TT", "00000000000000000000TT",
"00000000000000000000T0", "00000000000000000000T0",
"00000000000000000000S7" }; "00000000000000000000S7"};
for (std::size_t i = 0; i < int_leading_zeros_test.size(); ++i) { for (std::size_t i = 0; i < int_leading_zeros_test.size(); ++i) {
const auto& f = int_leading_zeros_test[i]; const auto &f = int_leading_zeros_test[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, int(i + 2)); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result,
int(i + 2));
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "could not convert to int for input: \"" << f << "\"" << std::endl; std::cerr << "could not convert to int for input: \"" << f << "\""
<< std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} } else if (result != 1015) {
else if (result != 1015) { std::cerr << "result " << f
std::cerr << "result " << f << " did not match with expected int: " << 1015 << std::endl; << " did not match with expected int: " << 1015 << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
@ -745,8 +826,7 @@ int main() {
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "could not convert to int for input: '0'" << std::endl; std::cerr << "could not convert to int for input: '0'" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} } else if (foo != 0) {
else if (foo != 0) {
std::cerr << "expected zero: " << foo << std::endl; std::cerr << "expected zero: " << foo << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }

View File

@ -7,14 +7,16 @@
#include <stdfloat> #include <stdfloat>
int main() { int main() {
// Write some testcases for the parsing of floating point numbers in the float32_t type. // Write some testcases for the parsing of floating point numbers in the
// We use the from_chars function defined in this library. // float32_t type. We use the from_chars function defined in this library.
#if __STDCPP_FLOAT32_T__ #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::float32_t> float32_test_expected{
const std::vector<std::string_view> float32_test{"123.456", "-78.9", "0.0001", "3.40282e+038"}; 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; std::cout << "runing float32 test" << std::endl;
for (std::size_t i = 0; i < float32_test.size(); ++i) { 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; std::float32_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), 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; std::cerr << "Failed to parse: \"" << f << "\"" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if(result != float32_test_expected[i]) { if (result != float32_test_expected[i]) {
std::cerr << "Test failed for input: \"" << f << "\" expected " << float32_test_expected[i] << " got " << result << std::endl; std::cerr << "Test failed for input: \"" << f << "\" expected "
<< float32_test_expected[i] << " got " << result << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
@ -33,11 +36,14 @@ int main() {
#if __STDCPP_FLOAT64_T__ #if __STDCPP_FLOAT64_T__
// Test cases for std::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::float64_t> float64_test_expected{
const std::vector<std::string_view> float64_test{"1.23e4", "-5.67e-8", "1.7976931348623157e+308", "-1.7976931348623157e+308"}; 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; std::cout << "runing float64 test" << std::endl;
for (std::size_t i = 0; i < float64_test.size(); ++i) { 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; std::float64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), 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; std::cerr << "Failed to parse: \"" << f << "\"" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if(result != float64_test_expected[i]) { if (result != float64_test_expected[i]) {
std::cerr << "Test failed for input: \"" << f << "\" expected " << float64_test_expected[i] << " got " << result << std::endl; std::cerr << "Test failed for input: \"" << f << "\" expected "
<< float64_test_expected[i] << " got " << result << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }

View File

@ -9,61 +9,66 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
int main_readme() { int main_readme() {
const std::string input = "1d+4"; const std::string input = "1d+4";
double result; double result;
fast_float::parse_options options{ fast_float::chars_format::fortran }; 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); auto answer = fast_float::from_chars_advanced(
if((answer.ec != std::errc()) || ((result != 10000))) { std::cerr << "parsing failure\n" << result <<"\n"; return EXIT_FAILURE; } 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; std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
int main () int main() {
{ const std::vector<double> expected{10000, 1000, 100, 10, 1,
const std::vector<double> expected{ 10000, 1000, 100, 10, 1, .1, .01, .001, .0001 }; .1, .01, .001, .0001};
const std::vector<std::string> fmt1{ "1+4", "1+3", "1+2", "1+1", "1+0", "1-1", "1-2", const std::vector<std::string> fmt1{"1+4", "1+3", "1+2", "1+1", "1+0",
"1-3", "1-4" }; "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", const std::vector<std::string> fmt2{"1d+4", "1d+3", "1d+2", "1d+1", "1d+0",
"1d-2", "1d-3", "1d-4" }; "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", const std::vector<std::string> fmt3{"+1+4", "+1+3", "+1+2", "+1+1", "+1+0",
"+1-2", "+1-3", "+1-4" }; "+1-1", "+1-2", "+1-3", "+1-4"};
const fast_float::parse_options options{ fast_float::chars_format::fortran }; const fast_float::parse_options options{fast_float::chars_format::fortran};
for ( auto const& f : fmt1 ) { for (auto const &f : fmt1) {
auto d{ std::distance( &fmt1[0], &f ) }; auto d{std::distance(&fmt1[0], &f)};
double result; double result;
auto answer{ fast_float::from_chars_advanced( f.data(), f.data()+f.size(), result, auto answer{fast_float::from_chars_advanced(f.data(), f.data() + f.size(),
options ) }; result, options)};
if ( answer.ec != std::errc() || result != expected[std::size_t(d)] ) { if (answer.ec != std::errc() || result != expected[std::size_t(d)]) {
std::cerr << "parsing failure on " << f << std::endl; std::cerr << "parsing failure on " << f << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
for ( auto const& f : fmt2 ) { for (auto const &f : fmt2) {
auto d{ std::distance( &fmt2[0], &f ) }; auto d{std::distance(&fmt2[0], &f)};
double result; double result;
auto answer{ fast_float::from_chars_advanced( f.data(), f.data()+f.size(), result, auto answer{fast_float::from_chars_advanced(f.data(), f.data() + f.size(),
options ) }; result, options)};
if ( answer.ec != std::errc() || result != expected[std::size_t(d)] ) { if (answer.ec != std::errc() || result != expected[std::size_t(d)]) {
std::cerr << "parsing failure on " << f << std::endl; std::cerr << "parsing failure on " << f << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
for ( auto const& f : fmt3 ) { for (auto const &f : fmt3) {
auto d{ std::distance( &fmt3[0], &f ) }; auto d{std::distance(&fmt3[0], &f)};
double result; double result;
auto answer{ fast_float::from_chars_advanced( f.data(), f.data()+f.size(), result, auto answer{fast_float::from_chars_advanced(f.data(), f.data() + f.size(),
options ) }; result, options)};
if ( answer.ec != std::errc() || result != expected[std::size_t(d)] ) { if (answer.ec != std::errc() || result != expected[std::size_t(d)]) {
std::cerr << "parsing failure on " << f << std::endl; std::cerr << "parsing failure on " << f << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
if(main_readme() != EXIT_SUCCESS) { return EXIT_FAILURE; } if (main_readme() != EXIT_SUCCESS) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -11,28 +11,40 @@
int main_readme() { int main_readme() {
const std::string input = "+.1"; // not valid const std::string input = "+.1"; // not valid
double result; double result;
fast_float::parse_options options{ fast_float::chars_format::json }; 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); auto answer = fast_float::from_chars_advanced(
if(answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; } 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; return EXIT_SUCCESS;
} }
int main_readme2() { int main_readme2() {
const std::string input = "inf"; // not valid in JSON const std::string input = "inf"; // not valid in JSON
double result; double result;
fast_float::parse_options options{ fast_float::chars_format::json }; 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); auto answer = fast_float::from_chars_advanced(
if(answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; } 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; return EXIT_SUCCESS;
} }
int main_readme3() { int main_readme3() {
const std::string input = "inf"; // not valid in JSON but we allow it with json_or_infnan const std::string input =
"inf"; // not valid in JSON but we allow it with json_or_infnan
double result; double result;
fast_float::parse_options options{ fast_float::chars_format::json_or_infnan }; 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); auto answer = fast_float::from_chars_advanced(
if(answer.ec != std::errc() || (!std::isinf(result))) { std::cerr << "should have parsed infinity\n"; return EXIT_FAILURE; } 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; return EXIT_SUCCESS;
} }
@ -77,41 +89,44 @@ int main() {
{"inf", {fast_float::parse_error::no_digits_in_integer_part, 0}}, {"inf", {fast_float::parse_error::no_digits_in_integer_part, 0}},
{"nan(snan)", {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) for (std::size_t i = 0; i < accept.size(); ++i) {
{ const auto &s = accept[i].input;
const auto& s = accept[i].input; const auto &expected = accept[i].expected;
const auto& expected = accept[i].expected;
double result; 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()) { if (answer.ec != std::errc()) {
std::cerr << "json fmt rejected valid json " << s << std::endl; std::cerr << "json fmt rejected valid json " << s << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (result != expected.value) { 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; return EXIT_FAILURE;
} }
if (std::string(answer.ptr) != expected.junk_chars) { 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; return EXIT_FAILURE;
} }
} }
for (std::size_t i = 0; i < reject.size(); ++i) for (std::size_t i = 0; i < reject.size(); ++i) {
{ const auto &s = reject[i].input;
const auto& s = reject[i].input;
double result; 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()) { if (answer.ec == std::errc()) {
std::cerr << "json fmt accepted invalid json " << s << std::endl; std::cerr << "json fmt accepted invalid json " << s << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
for (std::size_t i = 0; i < reject.size(); ++i) for (std::size_t i = 0; i < reject.size(); ++i) {
{ const auto &f = reject[i].input;
const auto& f = reject[i].input; const auto &expected_reason = reject[i].reason;
const auto& expected_reason = reject[i].reason;
auto answer = fast_float::parse_number_string( auto answer = fast_float::parse_number_string(
f.data(), f.data() + f.size(), f.data(), f.data() + f.size(),
fast_float::parse_options(fast_float::chars_format::json)); 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_readme() != EXIT_SUCCESS) {
if(main_readme2() != EXIT_SUCCESS) { return EXIT_FAILURE; } return EXIT_FAILURE;
}
if (main_readme2() != EXIT_SUCCESS) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -9,8 +9,7 @@
#include <system_error> #include <system_error>
template <typename T> char *to_string(T d, char *buffer) { template <typename T> char *to_string(T d, char *buffer) {
auto written = std::snprintf(buffer, 128, "%.*e", auto written = std::snprintf(buffer, 128, "%.*e", 64, d);
64, d);
return buffer + written; return buffer + written;
} }
@ -29,10 +28,12 @@ void allvalues() {
const char *string_end = to_string(v, buffer); const char *string_end = to_string(v, buffer);
float result_value; float result_value;
auto result = fast_float::from_chars(buffer, string_end, 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 // Starting with version 4.0 for fast_float, we return result_out_of_range
// value is either too small (too close to zero) or too large (effectively infinity). // if the value is either too small (too close to zero) or too large
// So std::errc::result_out_of_range is normal for well-formed input strings. // (effectively infinity). So std::errc::result_out_of_range is normal for
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { // well-formed input strings.
if (result.ec != std::errc() &&
result.ec != std::errc::result_out_of_range) {
std::cerr << "parsing error ? " << buffer << std::endl; std::cerr << "parsing error ? " << buffer << std::endl;
abort(); abort();
} }
@ -41,13 +42,14 @@ void allvalues() {
std::cerr << "not nan" << buffer << std::endl; std::cerr << "not nan" << buffer << std::endl;
abort(); 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 << buffer << std::endl;
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v std::cerr << "I got " << std::hexfloat << result_value
<< std::endl; << " but I was expecting " << v << std::endl;
abort(); abort();
} else if (result_value != v) { } 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 << "started with " << std::hexfloat << v << std::endl;
std::cout << "got back " << std::hexfloat << result_value << std::endl; std::cout << "got back " << std::hexfloat << result_value << std::endl;
std::cout << std::dec; std::cout << std::dec;

View File

@ -7,8 +7,7 @@
#include <iostream> #include <iostream>
template <typename T> char *to_string(T d, char *buffer) { template <typename T> char *to_string(T d, char *buffer) {
auto written = std::snprintf(buffer, 128, "%.*e", auto written = std::snprintf(buffer, 128, "%.*e", 64, d);
64, d);
return buffer + written; return buffer + written;
} }
@ -28,10 +27,12 @@ void all_32bit_values() {
const char *string_end = to_string(v, buffer); const char *string_end = to_string(v, buffer);
double result_value; double result_value;
auto result = fast_float::from_chars(buffer, string_end, 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 // Starting with version 4.0 for fast_float, we return result_out_of_range
// value is either too small (too close to zero) or too large (effectively infinity). // if the value is either too small (too close to zero) or too large
// So std::errc::result_out_of_range is normal for well-formed input strings. // (effectively infinity). So std::errc::result_out_of_range is normal for
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { // well-formed input strings.
if (result.ec != std::errc() &&
result.ec != std::errc::result_out_of_range) {
std::cerr << "parsing error ? " << buffer << std::endl; std::cerr << "parsing error ? " << buffer << std::endl;
abort(); abort();
} }
@ -40,9 +41,9 @@ void all_32bit_values() {
std::cerr << "not nan" << buffer << std::endl; std::cerr << "not nan" << buffer << std::endl;
abort(); abort();
} }
} else if(copysign(1,result_value) != copysign(1,v)) { } else if (copysign(1, result_value) != copysign(1, v)) {
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v std::cerr << "I got " << std::hexfloat << result_value
<< std::endl; << " but I was expecting " << v << std::endl;
abort(); abort();
} else if (std::isnan(v)) { } else if (std::isnan(v)) {
if (!std::isnan(result_value)) { if (!std::isnan(result_value)) {

View File

@ -8,8 +8,7 @@
#include <system_error> #include <system_error>
template <typename T> char *to_string(T d, char *buffer) { template <typename T> char *to_string(T d, char *buffer) {
auto written = std::snprintf(buffer, 128, "%.*e", auto written = std::snprintf(buffer, 128, "%.*e", 64, d);
64, d);
return buffer + written; return buffer + written;
} }
@ -33,7 +32,8 @@ static inline void lehmer64_seed(uint64_t seed) {
} }
static inline uint64_t lehmer64() { 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); v.high += g_lehmer64_state.high * UINT64_C(0xda942042e4dd58b5);
g_lehmer64_state = v; g_lehmer64_state = v;
return v.high; return v.high;
@ -56,10 +56,12 @@ void random_values(size_t N) {
const char *string_end = to_string(v, buffer); const char *string_end = to_string(v, buffer);
double result_value; double result_value;
auto result = fast_float::from_chars(buffer, string_end, 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 // Starting with version 4.0 for fast_float, we return result_out_of_range
// value is either too small (too close to zero) or too large (effectively infinity). // if the value is either too small (too close to zero) or too large
// So std::errc::result_out_of_range is normal for well-formed input strings. // (effectively infinity). So std::errc::result_out_of_range is normal for
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { // well-formed input strings.
if (result.ec != std::errc() &&
result.ec != std::errc::result_out_of_range) {
std::cerr << "parsing error ? " << buffer << std::endl; std::cerr << "parsing error ? " << buffer << std::endl;
errors++; errors++;
if (errors > 10) { if (errors > 10) {
@ -75,10 +77,10 @@ void random_values(size_t N) {
abort(); 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 << buffer << std::endl;
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v std::cerr << "I got " << std::hexfloat << result_value
<< std::endl; << " but I was expecting " << v << std::endl;
abort(); abort();
} else if (result_value != v) { } else if (result_value != v) {
std::cerr << "no match ? '" << buffer << "'" << std::endl; std::cerr << "no match ? '" << buffer << "'" << std::endl;
@ -97,7 +99,8 @@ void random_values(size_t N) {
int main() { int main() {
errors = 0; 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); random_values(N);
if (errors == 0) { if (errors == 0) {
std::cout << std::endl; std::cout << std::endl;

View File

@ -8,37 +8,42 @@
#include <vector> #include <vector>
inline void Assert(bool Assertion) { inline void Assert(bool Assertion) {
if (!Assertion) { throw std::runtime_error("bug"); } if (!Assertion) {
throw std::runtime_error("bug");
}
} }
template <typename T> template <typename T> bool test() {
bool test() { std::string input = "0.156250000000000000000000000000000000000000 "
std::string input = "0.156250000000000000000000000000000000000000 3.14159265358979323846264338327950288419716939937510 2.71828182845904523536028747135266249775724709369995"; "3.14159265358979323846264338327950288419716939937510 "
std::vector<T> answers = {T(0.15625), T(3.141592653589793), T(2.718281828459045)}; "2.71828182845904523536028747135266249775724709369995";
const char * begin = input.data(); std::vector<T> answers = {T(0.15625), T(3.141592653589793),
const char * end = input.data() + input.size(); T(2.718281828459045)};
for(size_t i = 0; i < answers.size(); i++) { const char *begin = input.data();
const char *end = input.data() + input.size();
for (size_t i = 0; i < answers.size(); i++) {
T result_value; T result_value;
while((begin < end) && (std::isspace(*begin))) { begin++; } while ((begin < end) && (std::isspace(*begin))) {
auto result = fast_float::from_chars(begin, end, begin++;
result_value); }
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { 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); printf("parsing %.*s\n", int(end - begin), begin);
std::cerr << " I could not parse " << std::endl; std::cerr << " I could not parse " << std::endl;
return false; return false;
} }
if(result_value != answers[i]) { if (result_value != answers[i]) {
printf("parsing %.*s\n", int(end - begin), begin); printf("parsing %.*s\n", int(end - begin), begin);
std::cerr << " Mismatch " << std::endl; std::cerr << " Mismatch " << std::endl;
std::cerr << " Expected " << answers[i] << std::endl; std::cerr << " Expected " << answers[i] << std::endl;
std::cerr << " Got " << result_value << std::endl; std::cerr << " Got " << result_value << std::endl;
return false; return false;
} }
begin = result.ptr; begin = result.ptr;
} }
if(begin != end) { if (begin != end) {
std::cerr << " bad ending " << std::endl; std::cerr << " bad ending " << std::endl;
return false; return false;
} }

View File

@ -9,44 +9,52 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) defined(sun) || defined(__sun)
// Anything at all that is related to cygwin, msys and so forth will // 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 // always use this fallback because we cannot rely on it behaving as normal
// gcc. // gcc.
#include <locale> #include <locale>
// workaround for CYGWIN // workaround for CYGWIN
double cygwin_strtod_l(const char* start, char** end) { double cygwin_strtod_l(const char *start, char **end) {
double d; double d;
std::stringstream ss; std::stringstream ss;
ss.imbue(std::locale::classic()); ss.imbue(std::locale::classic());
ss << start; ss << start;
ss >> d; ss >> d;
if(ss.fail()) { *end = nullptr; } if (ss.fail()) {
if(ss.eof()) { ss.clear(); } *end = nullptr;
}
if (ss.eof()) {
ss.clear();
}
auto nread = ss.tellg(); auto nread = ss.tellg();
*end = const_cast<char*>(start) + nread; *end = const_cast<char *>(start) + nread;
return d; return d;
} }
float cygwin_strtof_l(const char* start, char** end) { float cygwin_strtof_l(const char *start, char **end) {
float d; float d;
std::stringstream ss; std::stringstream ss;
ss.imbue(std::locale::classic()); ss.imbue(std::locale::classic());
ss << start; ss << start;
ss >> d; ss >> d;
if(ss.fail()) { *end = nullptr; } if (ss.fail()) {
if(ss.eof()) { ss.clear(); } *end = nullptr;
}
if (ss.eof()) {
ss.clear();
}
auto nread = ss.tellg(); auto nread = ss.tellg();
*end = const_cast<char*>(start) + nread; *end = const_cast<char *>(start) + nread;
return d; return d;
} }
#endif #endif
std::pair<double, bool> strtod_from_string(const char *st) { std::pair<double, bool> strtod_from_string(const char *st) {
double d; double d;
char *pr; 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_strtod_l(st, &pr); d = cygwin_strtod_l(st, &pr);
#elif defined(_WIN32) #elif defined(_WIN32)
static _locale_t c_locale = _create_locale(LC_ALL, "C"); static _locale_t c_locale = _create_locale(LC_ALL, "C");
@ -65,7 +73,8 @@ std::pair<double, bool> strtod_from_string(const char *st) {
std::pair<float, bool> strtof_from_string(char *st) { std::pair<float, bool> strtof_from_string(char *st) {
float d; float d;
char *pr; 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); d = cygwin_strtof_l(st, &pr);
#elif defined(_WIN32) #elif defined(_WIN32)
static _locale_t c_locale = _create_locale(LC_ALL, "C"); static _locale_t c_locale = _create_locale(LC_ALL, "C");
@ -103,9 +112,11 @@ bool tester() {
std::pair<double, bool> expected_double = std::pair<double, bool> expected_double =
strtod_from_string(to_be_parsed.c_str()); strtod_from_string(to_be_parsed.c_str());
double result_value; double result_value;
auto result = auto result = fast_float::from_chars(
fast_float::from_chars(to_be_parsed.data(), to_be_parsed.data() + to_be_parsed.size(), result_value); to_be_parsed.data(), to_be_parsed.data() + to_be_parsed.size(),
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { result_value);
if (result.ec != std::errc() &&
result.ec != std::errc::result_out_of_range) {
std::cout << to_be_parsed << std::endl; std::cout << to_be_parsed << std::endl;
std::cerr << " I could not parse " << std::endl; std::cerr << " I could not parse " << std::endl;
return false; return false;

View File

@ -14,7 +14,6 @@ template <typename T> char *to_string(T d, char *buffer) {
return buffer + written; return buffer + written;
} }
static fast_float::value128 g_lehmer64_state; static fast_float::value128 g_lehmer64_state;
/** /**
@ -35,7 +34,8 @@ static inline void lehmer64_seed(uint64_t seed) {
} }
static inline uint64_t lehmer64() { 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); v.high += g_lehmer64_state.high * UINT64_C(0xda942042e4dd58b5);
g_lehmer64_state = v; g_lehmer64_state = v;
return v.high; return v.high;
@ -59,10 +59,12 @@ void random_values(size_t N) {
const char *string_end = to_string(v, buffer); const char *string_end = to_string(v, buffer);
double result_value; double result_value;
auto result = fast_float::from_chars(buffer, string_end, 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 // Starting with version 4.0 for fast_float, we return result_out_of_range
// value is either too small (too close to zero) or too large (effectively infinity). // if the value is either too small (too close to zero) or too large
// So std::errc::result_out_of_range is normal for well-formed input strings. // (effectively infinity). So std::errc::result_out_of_range is normal for
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { // well-formed input strings.
if (result.ec != std::errc() &&
result.ec != std::errc::result_out_of_range) {
std::cerr << "parsing error ? " << buffer << std::endl; std::cerr << "parsing error ? " << buffer << std::endl;
errors++; errors++;
if (errors > 10) { if (errors > 10) {
@ -78,10 +80,10 @@ void random_values(size_t N) {
abort(); 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 << buffer << std::endl;
std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v std::cerr << "I got " << std::hexfloat << result_value
<< std::endl; << " but I was expecting " << v << std::endl;
abort(); abort();
} else if (result_value != v) { } else if (result_value != v) {
std::cerr << "no match ? " << buffer << std::endl; std::cerr << "no match ? " << buffer << std::endl;
@ -100,7 +102,8 @@ void random_values(size_t N) {
int main() { int main() {
errors = 0; 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); random_values(N);
if (errors == 0) { if (errors == 0) {
std::cout << std::endl; std::cout << std::endl;

View File

@ -7,35 +7,44 @@
#include <system_error> #include <system_error>
#include <utility> #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 // 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 // always use this fallback because we cannot rely on it behaving as normal
// gcc. // gcc.
#include <locale> #include <locale>
#include <sstream> #include <sstream>
// workaround for CYGWIN // workaround for CYGWIN
double cygwin_strtod_l(const char* start, char** end) { double cygwin_strtod_l(const char *start, char **end) {
double d; double d;
std::stringstream ss; std::stringstream ss;
ss.imbue(std::locale::classic()); ss.imbue(std::locale::classic());
ss << start; ss << start;
ss >> d; ss >> d;
if(ss.fail()) { *end = nullptr; } if (ss.fail()) {
if(ss.eof()) { ss.clear(); } *end = nullptr;
}
if (ss.eof()) {
ss.clear();
}
auto nread = ss.tellg(); auto nread = ss.tellg();
*end = const_cast<char*>(start) + nread; *end = const_cast<char *>(start) + nread;
return d; return d;
} }
float cygwin_strtof_l(const char* start, char** end) { float cygwin_strtof_l(const char *start, char **end) {
float d; float d;
std::stringstream ss; std::stringstream ss;
ss.imbue(std::locale::classic()); ss.imbue(std::locale::classic());
ss << start; ss << start;
ss >> d; ss >> d;
if(ss.fail()) { *end = nullptr; } if (ss.fail()) {
if(ss.eof()) { ss.clear(); } *end = nullptr;
}
if (ss.eof()) {
ss.clear();
}
auto nread = ss.tellg(); auto nread = ss.tellg();
*end = const_cast<char*>(start) + nread; *end = const_cast<char *>(start) + nread;
return d; return d;
} }
#endif #endif
@ -43,13 +52,14 @@ float cygwin_strtof_l(const char* start, char** end) {
class RandomEngine { class RandomEngine {
public: public:
RandomEngine() = delete; RandomEngine() = delete;
RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed) {}; RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed){};
uint64_t next() { uint64_t next() {
// Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h // Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h
// Inspired from // Inspired from
// https://github.com/lemire/testingRNG/blob/master/source/wyhash.h // https://github.com/lemire/testingRNG/blob/master/source/wyhash.h
wyhash64_x_ += UINT64_C(0x60bee2bee120fc15); 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; 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; uint64_t m2 = (tmp.high) ^ tmp.low;
@ -92,8 +102,9 @@ size_t build_random_string(RandomEngine &rand, char *buffer) {
buffer[pos++] = '-'; buffer[pos++] = '-';
} }
int number_of_digits = rand.next_ranged_int(1, 100); int number_of_digits = rand.next_ranged_int(1, 100);
if(number_of_digits == 100) { if (number_of_digits == 100) {
// With low probability, we want to allow very long strings just to stress the system. // With low probability, we want to allow very long strings just to stress
// the system.
number_of_digits = rand.next_ranged_int(1, 2000); number_of_digits = rand.next_ranged_int(1, 2000);
} }
int location_of_decimal_separator = rand.next_ranged_int(1, number_of_digits); 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'); buffer[pos] = char(rand.next_digit() + '0');
// We can have a leading zero only if location_of_decimal_separator = 1. // 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'); buffer[pos] = char(rand.next_digit() + '0');
} }
pos++; pos++;
@ -133,7 +145,8 @@ size_t build_random_string(RandomEngine &rand, char *buffer) {
std::pair<double, bool> strtod_from_string(char *st) { std::pair<double, bool> strtod_from_string(char *st) {
double d; double d;
char *pr; 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_strtod_l(st, &pr); d = cygwin_strtod_l(st, &pr);
#elif defined(_WIN32) #elif defined(_WIN32)
static _locale_t c_locale = _create_locale(LC_ALL, "C"); static _locale_t c_locale = _create_locale(LC_ALL, "C");
@ -152,7 +165,8 @@ std::pair<double, bool> strtod_from_string(char *st) {
std::pair<float, bool> strtof_from_string(char *st) { std::pair<float, bool> strtof_from_string(char *st) {
float d; float d;
char *pr; 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); d = cygwin_strtof_l(st, &pr);
#elif defined(_WIN32) #elif defined(_WIN32)
static _locale_t c_locale = _create_locale(LC_ALL, "C"); static _locale_t c_locale = _create_locale(LC_ALL, "C");
@ -176,14 +190,18 @@ bool tester(uint64_t seed, size_t volume) {
char buffer[4096]; // large buffer (can't overflow) char buffer[4096]; // large buffer (can't overflow)
RandomEngine rand(seed); RandomEngine rand(seed);
for (size_t i = 0; i < volume; i++) { 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); size_t length = build_random_string(rand, buffer);
std::pair<double, bool> expected_double = strtod_from_string(buffer); std::pair<double, bool> expected_double = strtod_from_string(buffer);
if (expected_double.second) { if (expected_double.second) {
double result_value; double result_value;
auto result = auto result =
fast_float::from_chars(buffer, buffer + length, result_value); 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); printf("parsing %.*s\n", int(length), buffer);
std::cerr << " I could not parse " << std::endl; std::cerr << " I could not parse " << std::endl;
return false; return false;
@ -206,7 +224,8 @@ bool tester(uint64_t seed, size_t volume) {
float result_value; float result_value;
auto result = auto result =
fast_float::from_chars(buffer, buffer + length, result_value); 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); printf("parsing %.*s\n", int(length), buffer);
std::cerr << " I could not parse " << std::endl; std::cerr << " I could not parse " << std::endl;
return false; return false;
@ -230,7 +249,8 @@ bool tester(uint64_t seed, size_t volume) {
int main() { 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; std::cout << "Warning: msys/cygwin or solaris detected." << std::endl;
return EXIT_SUCCESS; return EXIT_SUCCESS;
#else #else

View File

@ -121,7 +121,8 @@ bool eddelbuettel() {
if (std::isnan(result) && std::isnan(expected.second)) { if (std::isnan(result) && std::isnan(expected.second)) {
continue; 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; return false;
} }
} }

View File

@ -7,35 +7,44 @@
#include <system_error> #include <system_error>
#include <utility> #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 // 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 // always use this fallback because we cannot rely on it behaving as normal
// gcc. // gcc.
#include <locale> #include <locale>
#include <sstream> #include <sstream>
// workaround for CYGWIN // workaround for CYGWIN
double cygwin_strtod_l(const char* start, char** end) { double cygwin_strtod_l(const char *start, char **end) {
double d; double d;
std::stringstream ss; std::stringstream ss;
ss.imbue(std::locale::classic()); ss.imbue(std::locale::classic());
ss << start; ss << start;
ss >> d; ss >> d;
if(ss.fail()) { *end = nullptr; } if (ss.fail()) {
if(ss.eof()) { ss.clear(); } *end = nullptr;
}
if (ss.eof()) {
ss.clear();
}
auto nread = ss.tellg(); auto nread = ss.tellg();
*end = const_cast<char*>(start) + nread; *end = const_cast<char *>(start) + nread;
return d; return d;
} }
float cygwin_strtof_l(const char* start, char** end) { float cygwin_strtof_l(const char *start, char **end) {
float d; float d;
std::stringstream ss; std::stringstream ss;
ss.imbue(std::locale::classic()); ss.imbue(std::locale::classic());
ss << start; ss << start;
ss >> d; ss >> d;
if(ss.fail()) { *end = nullptr; } if (ss.fail()) {
if(ss.eof()) { ss.clear(); } *end = nullptr;
}
if (ss.eof()) {
ss.clear();
}
auto nread = ss.tellg(); auto nread = ss.tellg();
*end = const_cast<char*>(start) + nread; *end = const_cast<char *>(start) + nread;
return d; return d;
} }
#endif #endif
@ -43,13 +52,14 @@ float cygwin_strtof_l(const char* start, char** end) {
class RandomEngine { class RandomEngine {
public: public:
RandomEngine() = delete; RandomEngine() = delete;
RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed) { }; RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed){};
uint64_t next() { uint64_t next() {
// Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h // Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h
// Inspired from // Inspired from
// https://github.com/lemire/testingRNG/blob/master/source/wyhash.h // https://github.com/lemire/testingRNG/blob/master/source/wyhash.h
wyhash64_x_ += UINT64_C(0x60bee2bee120fc15); 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; 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; uint64_t m2 = (tmp.high) ^ tmp.low;
@ -99,7 +109,8 @@ size_t build_random_string(RandomEngine &rand, char *buffer) {
} }
buffer[pos] = char(rand.next_digit() + '0'); buffer[pos] = char(rand.next_digit() + '0');
// We can have a leading zero only if location_of_decimal_separator = 1. // 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'); buffer[pos] = char(rand.next_digit() + '0');
} }
pos++; pos++;
@ -129,7 +140,8 @@ size_t build_random_string(RandomEngine &rand, char *buffer) {
std::pair<double, bool> strtod_from_string(char *st) { std::pair<double, bool> strtod_from_string(char *st) {
double d; double d;
char *pr; 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_strtod_l(st, &pr); d = cygwin_strtod_l(st, &pr);
#elif defined(_WIN32) #elif defined(_WIN32)
static _locale_t c_locale = _create_locale(LC_ALL, "C"); static _locale_t c_locale = _create_locale(LC_ALL, "C");
@ -148,7 +160,8 @@ std::pair<double, bool> strtod_from_string(char *st) {
std::pair<float, bool> strtof_from_string(char *st) { std::pair<float, bool> strtof_from_string(char *st) {
float d; float d;
char *pr; 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); d = cygwin_strtof_l(st, &pr);
#elif defined(_WIN32) #elif defined(_WIN32)
static _locale_t c_locale = _create_locale(LC_ALL, "C"); static _locale_t c_locale = _create_locale(LC_ALL, "C");
@ -172,14 +185,18 @@ bool tester(uint64_t seed, size_t volume) {
char buffer[4096]; // large buffer (can't overflow) char buffer[4096]; // large buffer (can't overflow)
RandomEngine rand(seed); RandomEngine rand(seed);
for (size_t i = 0; i < volume; i++) { 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); size_t length = build_random_string(rand, buffer);
std::pair<double, bool> expected_double = strtod_from_string(buffer); std::pair<double, bool> expected_double = strtod_from_string(buffer);
if (expected_double.second) { if (expected_double.second) {
double result_value; double result_value;
auto result = auto result =
fast_float::from_chars(buffer, buffer + length, result_value); 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); printf("parsing %.*s\n", int(length), buffer);
std::cerr << " I could not parse " << std::endl; std::cerr << " I could not parse " << std::endl;
return false; return false;
@ -202,7 +219,8 @@ bool tester(uint64_t seed, size_t volume) {
float result_value; float result_value;
auto result = auto result =
fast_float::from_chars(buffer, buffer + length, result_value); 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); printf("parsing %.*s\n", int(length), buffer);
std::cerr << " I could not parse " << std::endl; std::cerr << " I could not parse " << std::endl;
return false; return false;
@ -225,8 +243,12 @@ bool tester(uint64_t seed, size_t volume) {
} }
int main() { int main() {
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
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; 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; return EXIT_SUCCESS;
#else #else
if (tester(1234344, 100000000)) { if (tester(1234344, 100000000)) {

File diff suppressed because it is too large Load Diff