Compare commits

...

5 Commits

Author SHA1 Message Date
IRainman
497e65b03f * Added separate config macros FASTFLOAT_ISNOT_CHECKED_BOUNDS. 2025-11-09 19:08:11 +03:00
IRainman
c8e4d89fef # unfck lint 2025-11-09 18:15:53 +03:00
IRainman
959c9531ea # cycles (for and while) cleanup in low level for the best compiler optimization and the best runtime. 2025-11-09 18:13:17 +03:00
IRainman
b9d91e7f34 * code cleanup. 2025-11-09 17:22:13 +03:00
IRainman
e109eedd35 * try to fix error on x86 platform step3. 2025-11-09 17:21:52 +03:00
7 changed files with 56 additions and 55 deletions

View File

@ -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>

View File

@ -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);

View File

@ -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);
}

View File

@ -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 {

View File

@ -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;

View File

@ -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) {

View File

@ -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]);
}