mirror of
https://github.com/fastfloat/fast_float.git
synced 2025-12-06 08:46:49 +08:00
lint
This commit is contained in:
parent
8f3dae6b9f
commit
5ad6aae0b1
0
clang-format-ignore.txt
Normal file
0
clang-format-ignore.txt
Normal file
@ -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;
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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
0
script/run-clangcldocker.sh
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
@ -1,2 +1,2 @@
|
|||||||
#include "test.h"
|
#include "test.h"
|
||||||
void foo() { }
|
void foo() {}
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
|
|||||||
@ -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)) {
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
Loading…
x
Reference in New Issue
Block a user