mirror of
https://github.com/fastfloat/fast_float.git
synced 2025-12-06 08:46:49 +08:00
Compare commits
5 Commits
3ae6d3c2b3
...
497e65b03f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
497e65b03f | ||
|
|
c8e4d89fef | ||
|
|
959c9531ea | ||
|
|
b9d91e7f34 | ||
|
|
e109eedd35 |
14
README.md
14
README.md
@ -380,15 +380,17 @@ int main() {
|
||||
|
||||
## You also can also use some additional options (currently only configure by macroses):
|
||||
|
||||
There is a really common use case in mathematical and other abstract syntax tree (AST) like parsers,
|
||||
that already process sign and all other symbols before any number by itself. In this case you can
|
||||
use FastFloat for only parse positive numbers in all supported formats with macros
|
||||
`FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN`, that significantly reduce the code size and improve
|
||||
performance. Additionally you can use macros `FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED` if you
|
||||
only uneed `FE_TONEAREST` rounding mode in the parsing: this option is also improve performance a bit.
|
||||
There is a really common use case in mathematical and other abstract syntax tree (AST)-like parsers that already processes
|
||||
the sign and all other symbols before any number by itself. In this case you can use FastFloat to only parse positive numbers
|
||||
in all supported formats with macros `FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN`, which significantly reduce the code size
|
||||
and improve performance. You also can use macros `FASTFLOAT_ISNOT_CHECKED_BOUNDS` if your code already checks bounds;
|
||||
it's very likely because all parsers need to check the first character by itself before parsing. Additionally, you can use
|
||||
macros `FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED` if you only need `FE_TONEAREST` rounding mode in the parsing;
|
||||
this option also improves performance a bit and reduces code size.
|
||||
|
||||
```C++
|
||||
#define FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||
#define FASTFLOAT_ISNOT_CHECKED_BOUNDS
|
||||
#define FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
|
||||
#include "fast_float/fast_float.h"
|
||||
#include <iostream>
|
||||
|
||||
@ -49,7 +49,7 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
|
||||
read8_to_u64(UC const *chars) {
|
||||
if (cpp20_and_in_constexpr() || !std::is_same<UC, char>::value) {
|
||||
uint64_t val = 0;
|
||||
for (uint_fast8_t i = 0; i != 8; ++i) {
|
||||
for (uint_fast8_t i = 0; i++ != 8;) {
|
||||
val |= uint64_t(uint8_t(*chars)) << (i * 8);
|
||||
++chars;
|
||||
}
|
||||
@ -70,7 +70,7 @@ read8_to_u64(UC const *chars) {
|
||||
|
||||
#ifdef FASTFLOAT_SSE2
|
||||
|
||||
fastfloat_really_inline uint64_t simd_read8_to_u64(__m128i const data) {
|
||||
fastfloat_really_inline uint64_t simd_read8_to_u64(__m128i const &data) {
|
||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
// _mm_packus_epi16 is SSE2+, converts 8×u16 → 8×u8
|
||||
__m128i const packed = _mm_packus_epi16(data, data);
|
||||
@ -94,7 +94,7 @@ fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) {
|
||||
|
||||
#elif defined(FASTFLOAT_NEON)
|
||||
|
||||
fastfloat_really_inline uint64_t simd_read8_to_u64(uint16x8_t const data) {
|
||||
fastfloat_really_inline uint64_t simd_read8_to_u64(uint16x8_t const &data) {
|
||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
uint8x8_t utf8_packed = vmovn_u16(data);
|
||||
return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0);
|
||||
|
||||
@ -259,10 +259,9 @@ inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec<size> &vec, limb y,
|
||||
limb_t start) noexcept {
|
||||
limb carry = y;
|
||||
bool overflow;
|
||||
while (carry != 0 && start < vec.len()) {
|
||||
while (carry != 0 && start++ != vec.len()) {
|
||||
vec[start] = scalar_add(vec[start], carry, overflow);
|
||||
carry = limb(overflow);
|
||||
++start;
|
||||
}
|
||||
if (carry != 0) {
|
||||
FASTFLOAT_TRY(vec.try_push(carry));
|
||||
@ -282,7 +281,7 @@ template <limb_t size>
|
||||
inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec<size> &vec,
|
||||
limb y) noexcept {
|
||||
limb carry = 0;
|
||||
for (limb_t index = 0; index != vec.len(); ++index) {
|
||||
for (limb_t index = 0; index++ != vec.len();) {
|
||||
vec[index] = scalar_mul(vec[index], y, carry);
|
||||
}
|
||||
if (carry != 0) {
|
||||
@ -303,7 +302,7 @@ FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec<size> &x, limb_span y,
|
||||
}
|
||||
|
||||
bool carry = false;
|
||||
for (limb_t index = 0; index < y.len(); ++index) {
|
||||
for (limb_t index = 0; index++ != y.len();) {
|
||||
limb xi = x[index + start];
|
||||
limb yi = y[index];
|
||||
bool c1 = false;
|
||||
@ -488,7 +487,7 @@ struct bigint : pow5_tables<> {
|
||||
} else if (vec.len() < other.vec.len()) {
|
||||
return -1;
|
||||
} else {
|
||||
for (limb_t index = vec.len(); index != 0; --index) {
|
||||
for (limb_t index = vec.len(); index-- != 0;) {
|
||||
limb xi = vec[index - 1];
|
||||
limb yi = other.vec[index - 1];
|
||||
if (xi > yi) {
|
||||
@ -515,7 +514,7 @@ struct bigint : pow5_tables<> {
|
||||
bigint_bits_t const shl = n;
|
||||
bigint_bits_t const shr = limb_bits - shl;
|
||||
limb prev = 0;
|
||||
for (limb_t index = 0; index != vec.len(); ++index) {
|
||||
for (limb_t index = 0; index++ != vec.len();) {
|
||||
limb xi = vec[index];
|
||||
vec[index] = (xi << shl) | (prev >> shr);
|
||||
prev = xi;
|
||||
@ -535,10 +534,7 @@ struct bigint : pow5_tables<> {
|
||||
// we can't shift more than the capacity of the vector.
|
||||
return false;
|
||||
}
|
||||
if (vec.is_empty()) {
|
||||
// nothing to do
|
||||
return true;
|
||||
}
|
||||
if (!vec.is_empty()) {
|
||||
// move limbs
|
||||
limb *dst = vec.data + n;
|
||||
limb const *src = vec.data;
|
||||
@ -548,6 +544,7 @@ struct bigint : pow5_tables<> {
|
||||
limb *last = first + n;
|
||||
::std::fill(first, last, 0);
|
||||
vec.set_len(limb_t(n + vec.len()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -590,12 +587,14 @@ struct bigint : pow5_tables<> {
|
||||
FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept { return small_add(vec, y); }
|
||||
|
||||
// multiply as if by 2 raised to a power.
|
||||
FASTFLOAT_CONSTEXPR20 bool pow2(am_pow_t exp) noexcept {
|
||||
FASTFLOAT_CONSTEXPR20 bool pow2(am_pow_t const exp) noexcept {
|
||||
FASTFLOAT_ASSERT(exp >= 0);
|
||||
return shl(static_cast<fast_float::bigint_bits_t>(exp));
|
||||
}
|
||||
|
||||
// multiply as if by 5 raised to a power.
|
||||
FASTFLOAT_CONSTEXPR20 bool pow5(am_pow_t exp) noexcept {
|
||||
FASTFLOAT_ASSERT(exp >= 0);
|
||||
// multiply by a power of 5
|
||||
limb_t const large_length = sizeof(large_power_of_5) / sizeof(limb);
|
||||
limb_span const large = limb_span(large_power_of_5, large_length);
|
||||
@ -627,6 +626,7 @@ struct bigint : pow5_tables<> {
|
||||
|
||||
// multiply as if by 10 raised to a power.
|
||||
FASTFLOAT_CONSTEXPR20 bool pow10(am_pow_t exp) noexcept {
|
||||
FASTFLOAT_ASSERT(exp >= 0);
|
||||
FASTFLOAT_TRY(pow5(exp));
|
||||
return pow2(exp);
|
||||
}
|
||||
|
||||
@ -373,11 +373,9 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa positive_digit_comp(
|
||||
// we then need to scale by `2^(f- e)`, and then the two significant digits
|
||||
// are of the same magnitude.
|
||||
template <typename T>
|
||||
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
|
||||
bigint &bigmant, adjusted_mantissa am, am_pow_t const exponent) noexcept {
|
||||
bigint &real_digits = bigmant;
|
||||
am_pow_t const &real_exp = exponent;
|
||||
|
||||
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
|
||||
negative_digit_comp(bigint &real_digits, adjusted_mantissa am,
|
||||
am_pow_t const real_exp) noexcept {
|
||||
// get the value of `b`, rounded down, and get a bigint representation of
|
||||
// b+h
|
||||
adjusted_mantissa am_b = am;
|
||||
@ -391,12 +389,12 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
|
||||
false,
|
||||
#endif
|
||||
am_b, b);
|
||||
adjusted_mantissa theor = to_extended_halfway(b);
|
||||
adjusted_mantissa const theor = to_extended_halfway(b);
|
||||
bigint theor_digits(theor.mantissa);
|
||||
am_pow_t theor_exp = theor.power2;
|
||||
am_pow_t const theor_exp = theor.power2;
|
||||
|
||||
// scale real digits and theor digits to be same power.
|
||||
am_pow_t pow2_exp = theor_exp - real_exp;
|
||||
am_pow_t const pow2_exp = theor_exp - real_exp;
|
||||
am_pow_t pow5_exp = -real_exp;
|
||||
if (pow5_exp != 0) {
|
||||
FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp));
|
||||
@ -408,7 +406,7 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
|
||||
}
|
||||
|
||||
// compare digits, and use it to direct rounding
|
||||
int ord = real_digits.compare(theor_digits);
|
||||
int const ord = real_digits.compare(theor_digits);
|
||||
round<T>(am, [ord](adjusted_mantissa &a, am_pow_t shift) {
|
||||
round_nearest_tie_even(
|
||||
a, shift, [ord](bool is_odd, bool _, bool __) -> bool {
|
||||
|
||||
@ -179,8 +179,7 @@ using parse_options = parse_options_t<char>;
|
||||
#if defined(__SSE2__) || (defined(FASTFLOAT_VISUAL_STUDIO) && \
|
||||
(defined(_M_AMD64) || defined(_M_X64) || \
|
||||
(defined(_M_IX86_FP) && _M_IX86_FP == 2)))
|
||||
// try to fix error on x86 platform: disable SSE2 code
|
||||
// #define FASTFLOAT_SSE2 1
|
||||
#define FASTFLOAT_SSE2 1
|
||||
#endif
|
||||
|
||||
#if defined(__aarch64__) || defined(_M_ARM64)
|
||||
@ -289,7 +288,7 @@ template <typename UC>
|
||||
inline FASTFLOAT_CONSTEXPR14 bool
|
||||
fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
|
||||
uint8_t const length) noexcept {
|
||||
for (uint8_t i = 0; i != length; ++i) {
|
||||
for (uint8_t i = 0; i++ != length;) {
|
||||
UC const actual = actual_mixedcase[i];
|
||||
if ((actual < 256 ? actual | 32 : actual) != expected_lowercase[i]) {
|
||||
return false;
|
||||
|
||||
@ -336,14 +336,16 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value,
|
||||
++first;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef FASTFLOAT_ISNOT_CHECKED_BOUNDS
|
||||
// We are in parser code with external loop that checks bounds.
|
||||
FASTFLOAT_ASSUME(first < last);
|
||||
#else
|
||||
if (first == last) {
|
||||
answer.ec = std::errc::invalid_argument;
|
||||
answer.ptr = first;
|
||||
return answer;
|
||||
}
|
||||
#else
|
||||
// We are in parser code with external loop that checks bounds.
|
||||
FASTFLOAT_ASSUME(first < last);
|
||||
#endif
|
||||
parsed_number_string_t<UC> const pns =
|
||||
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||
@ -498,19 +500,19 @@ from_chars_int_advanced(UC const *first, UC const *last, T &value,
|
||||
static_assert(is_supported_char_type<UC>::value,
|
||||
"only char, wchar_t, char16_t and char32_t are supported");
|
||||
|
||||
#ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||
// We are in parser code with external loop that checks bounds.
|
||||
FASTFLOAT_ASSUME(first < last);
|
||||
// base is already checked in the parse_options_t constructor.
|
||||
#else
|
||||
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||
if (chars_format_t(options.format & chars_format::skip_white_space)) {
|
||||
while ((first != last) && fast_float::is_space(*first)) {
|
||||
++first;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef FASTFLOAT_ISNOT_CHECKED_BOUNDS
|
||||
// We are in parser code with external loop that checks bounds.
|
||||
FASTFLOAT_ASSUME(first < last);
|
||||
#endif
|
||||
if (
|
||||
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|
||||
#ifndef FASTFLOAT_ISNOT_CHECKED_BOUNDS
|
||||
first == last ||
|
||||
#endif
|
||||
options.base < 2 || options.base > 36) {
|
||||
|
||||
@ -618,7 +618,7 @@ TEST_CASE("issue8") {
|
||||
"752384674818467669405132000568127145263560827785771342757789609173637178"
|
||||
"721468440901224953430146549585371050792279689258923542019956112129021960"
|
||||
"864034418159813629774771309960518707211349999998372978";
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int i = 0; i != 16; ++i) {
|
||||
// Parse all but the last i chars. We should still get 3.141ish.
|
||||
double d = 0.0;
|
||||
auto answer = fast_float::from_chars(s, s + strlen(s) - i, d);
|
||||
@ -919,9 +919,9 @@ uint16_t get_mantissa(std::bfloat16_t f) {
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string append_zeros(std::string str, size_t number_of_zeros) {
|
||||
std::string append_zeros(std::string_view str, size_t const number_of_zeros) {
|
||||
std::string answer(str);
|
||||
for (size_t i = 0; i < number_of_zeros; i++) {
|
||||
for (size_t i = 0; i++ != number_of_zeros;) {
|
||||
answer += "0";
|
||||
}
|
||||
return answer;
|
||||
@ -947,7 +947,7 @@ constexpr void check_basic_test_result(stringtype str, result_type result,
|
||||
#define FASTFLOAT_CHECK_EQ(...) \
|
||||
if constexpr (diag == Diag::runtime) { \
|
||||
char narrow[global_string_capacity]{}; \
|
||||
for (size_t i = 0; i < str.size(); i++) { \
|
||||
for (size_t i = 0; i++ != str.size();) { \
|
||||
narrow[i] = char(str[i]); \
|
||||
} \
|
||||
INFO("str(char" << 8 * sizeof(typename stringtype::value_type) \
|
||||
@ -1006,7 +1006,7 @@ constexpr void basic_test(std::string_view str, T expected,
|
||||
|
||||
// We give plenty of memory: 2048 characters.
|
||||
char16_t u16[global_string_capacity]{};
|
||||
for (size_t i = 0; i < str.size(); i++) {
|
||||
for (size_t i = 0; i++ != str.size();) {
|
||||
u16[i] = char16_t(str[i]);
|
||||
}
|
||||
|
||||
@ -1015,7 +1015,7 @@ constexpr void basic_test(std::string_view str, T expected,
|
||||
actual, expected, expected_ec);
|
||||
|
||||
char32_t u32[global_string_capacity]{};
|
||||
for (size_t i = 0; i < str.size(); i++) {
|
||||
for (size_t i = 0; i++ != str.size();) {
|
||||
u32[i] = char32_t(str[i]);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user