* Added a config macro FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN and FASTFLOAT_DISALLOW_NAN. This both allow to significantly reduce code size and speedup your code when you use fast_float as a part of your parser.

This commit is contained in:
IRainman 2025-03-05 23:21:24 +03:00
parent 3c5b166f6c
commit b0b1954c87
4 changed files with 89 additions and 12 deletions

View File

@ -255,7 +255,9 @@ template <typename UC> 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};
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
bool negative{false}; bool negative{false};
#endif
bool valid{false}; bool valid{false};
bool too_many_digits{false}; bool too_many_digits{false};
// contains the range of the significant digits // contains the range of the significant digits
@ -290,6 +292,7 @@ parse_number_string(UC const *p, UC const *pend,
answer.valid = false; answer.valid = false;
answer.too_many_digits = false; answer.too_many_digits = false;
// assume p < pend, so dereference without checks; // assume p < pend, so dereference without checks;
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
answer.negative = (*p == UC('-')); answer.negative = (*p == UC('-'));
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
if ((*p == UC('-')) || if ((*p == UC('-')) ||
@ -314,6 +317,7 @@ parse_number_string(UC const *p, UC const *pend,
} }
} }
} }
#endif
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)
@ -481,6 +485,7 @@ parse_int_string(UC const *p, UC const *pend, T &value,
UC const *const first = p; UC const *const first = p;
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
bool const negative = (*p == UC('-')); bool const negative = (*p == UC('-'));
#ifdef FASTFLOAT_VISUAL_STUDIO #ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(push) #pragma warning(push)
@ -498,6 +503,7 @@ parse_int_string(UC const *p, UC const *pend, T &value,
(uint64_t(fmt & chars_format::allow_leading_plus) && (*p == UC('+')))) { (uint64_t(fmt & chars_format::allow_leading_plus) && (*p == UC('+')))) {
++p; ++p;
} }
#endif
UC const *const start_num = p; UC const *const start_num = p;
@ -553,12 +559,17 @@ parse_int_string(UC const *p, UC const *pend, T &value,
// check other types overflow // check other types overflow
if (!std::is_same<T, uint64_t>::value) { if (!std::is_same<T, uint64_t>::value) {
if (i > uint64_t(std::numeric_limits<T>::max()) + uint64_t(negative)) { if (i > uint64_t(std::numeric_limits<T>::max())
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
+ uint64_t(negative)
#endif
) {
answer.ec = std::errc::result_out_of_range; answer.ec = std::errc::result_out_of_range;
return answer; return answer;
} }
} }
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
if (negative) { if (negative) {
#ifdef FASTFLOAT_VISUAL_STUDIO #ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(push) #pragma warning(push)
@ -576,8 +587,11 @@ parse_int_string(UC const *p, UC const *pend, T &value,
#pragma warning(pop) #pragma warning(pop)
#endif #endif
} else { } else {
#endif
value = T(i); value = T(i);
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
} }
#endif
answer.ec = std::errc(); answer.ec = std::errc();
return answer; return answer;

View File

@ -381,7 +381,11 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
round<T>(am_b, round<T>(am_b,
[](adjusted_mantissa &a, int32_t shift) { round_down(a, shift); }); [](adjusted_mantissa &a, int32_t shift) { round_down(a, shift); });
T b; T b;
to_float(false, am_b, b); to_float(
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
false,
#endif
am_b, b);
adjusted_mantissa theor = to_extended_halfway(b); adjusted_mantissa theor = to_extended_halfway(b);
bigint theor_digits(theor.mantissa); bigint theor_digits(theor.mantissa);
int32_t theor_exp = theor.power2; int32_t theor_exp = theor.power2;

View File

@ -51,8 +51,10 @@ enum class chars_format : uint64_t {
json_or_infnan = uint64_t(detail::basic_json_fmt) | fixed | scientific, json_or_infnan = uint64_t(detail::basic_json_fmt) | fixed | scientific,
fortran = uint64_t(detail::basic_fortran_fmt) | fixed | scientific, fortran = uint64_t(detail::basic_fortran_fmt) | fixed | scientific,
general = fixed | scientific, general = fixed | scientific,
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
allow_leading_plus = 1 << 7, allow_leading_plus = 1 << 7,
skip_white_space = 1 << 8, skip_white_space = 1 << 8,
#endif
}; };
template <typename UC> struct from_chars_result_t { template <typename UC> struct from_chars_result_t {
@ -606,6 +608,8 @@ template <> inline constexpr int binary_format<float>::infinite_power() {
return 0xFF; return 0xFF;
} }
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
template <> inline constexpr int binary_format<double>::sign_index() { template <> inline constexpr int binary_format<double>::sign_index() {
return 63; return 63;
} }
@ -614,6 +618,8 @@ template <> inline constexpr int binary_format<float>::sign_index() {
return 31; return 31;
} }
#endif
template <> template <>
inline constexpr int binary_format<double>::max_exponent_fast_path() { inline constexpr int binary_format<double>::max_exponent_fast_path() {
return 22; return 22;
@ -979,13 +985,19 @@ binary_format<double>::hidden_bit_mask() {
template <typename T> template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
to_float(bool negative, adjusted_mantissa am, T &value) { to_float(
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
bool negative,
#endif
adjusted_mantissa am, T &value) {
using equiv_uint = equiv_uint_t<T>; using equiv_uint = equiv_uint_t<T>;
equiv_uint word = equiv_uint(am.mantissa); equiv_uint word = equiv_uint(am.mantissa);
word = equiv_uint(word | equiv_uint(am.power2) word = equiv_uint(word | equiv_uint(am.power2)
<< binary_format<T>::mantissa_explicit_bits()); << binary_format<T>::mantissa_explicit_bits());
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
word = word =
equiv_uint(word | equiv_uint(negative) << binary_format<T>::sign_index()); equiv_uint(word | equiv_uint(negative) << binary_format<T>::sign_index());
#endif
#if FASTFLOAT_HAS_BIT_CAST #if FASTFLOAT_HAS_BIT_CAST
value = std::bit_cast<T>(word); value = std::bit_cast<T>(word);
#else #else
@ -993,6 +1005,8 @@ to_float(bool negative, adjusted_mantissa am, T &value) {
#endif #endif
} }
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
template <typename = void> struct space_lut { template <typename = void> 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,
@ -1018,6 +1032,8 @@ template <typename UC> constexpr bool is_space(UC c) {
return c < 256 && space_lut<>::value[uint8_t(c)]; return c < 256 && space_lut<>::value[uint8_t(c)];
} }
#endif
template <typename UC> static constexpr uint64_t int_cmp_zeros() { template <typename UC> static constexpr uint64_t int_cmp_zeros() {
static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4), static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4),
"Unsupported character size"); "Unsupported character size");
@ -1032,6 +1048,8 @@ template <typename UC> static constexpr int int_cmp_len() {
return sizeof(uint64_t) / sizeof(UC); return sizeof(uint64_t) / sizeof(UC);
} }
#ifndef FASTFLOAT_DISALLOW_NAN
template <typename UC> constexpr UC const *str_const_nan(); template <typename UC> constexpr UC const *str_const_nan();
template <> constexpr char const *str_const_nan<char>() { return "nan"; } template <> constexpr char const *str_const_nan<char>() { return "nan"; }
@ -1052,6 +1070,8 @@ template <> constexpr char8_t const *str_const_nan<char8_t>() {
} }
#endif #endif
#endif
template <typename UC> constexpr UC const *str_const_inf(); template <typename UC> constexpr UC const *str_const_inf();
template <> constexpr char const *str_const_inf<char>() { return "infinity"; } template <> constexpr char const *str_const_inf<char>() { return "infinity"; }
@ -1223,7 +1243,12 @@ operator^=(chars_format &lhs, chars_format rhs) noexcept {
namespace detail { namespace detail {
// adjust for deprecated feature macros // adjust for deprecated feature macros
constexpr chars_format adjust_for_feature_macros(chars_format fmt) { fastfloat_really_inline constexpr chars_format adjust_for_feature_macros(chars_format fmt) {
#if defined(FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN) && \
(defined(FASTFLOAT_ALLOWS_LEADING_PLUS) || \
defined(FASTFLOAT_SKIP_WHITE_SPACE))
#error "FASTFLOAT_ALLOWS_LEADING_PLUS and FASTFLOAT_SKIP_WHITE_SPACE require FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN to be undefined"
#endif
return fmt return fmt
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS #ifdef FASTFLOAT_ALLOWS_LEADING_PLUS
| chars_format::allow_leading_plus | chars_format::allow_leading_plus

View File

@ -22,11 +22,16 @@ namespace detail {
template <typename T, typename UC> template <typename T, typename UC>
from_chars_result_t<UC> from_chars_result_t<UC>
FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, UC const *last, FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, UC const *last,
T &value, chars_format fmt) noexcept { T &value
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
, chars_format fmt
#endif
) 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
// assume first < last, so dereference without checks; // assume first < last, so dereference without checks;
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
bool const minusSign = (*first == UC('-')); bool const minusSign = (*first == UC('-'));
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
if ((*first == UC('-')) || if ((*first == UC('-')) ||
@ -34,11 +39,16 @@ from_chars_result_t<UC>
(*first == UC('+')))) { (*first == UC('+')))) {
++first; ++first;
} }
#endif
if (last - first >= 3) { if (last - first >= 3) {
#ifndef FASTFLOAT_DISALLOW_NAN
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() value =
: std::numeric_limits<T>::quiet_NaN(); #ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
minusSign ? -std::numeric_limits<T>::quiet_NaN() :
#endif
std::numeric_limits<T>::quiet_NaN();
// Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, // 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). // C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan).
if (first != last && *first == UC('(')) { if (first != last && *first == UC('(')) {
@ -54,6 +64,7 @@ from_chars_result_t<UC>
} }
return answer; return answer;
} }
#endif
if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) { if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) {
if ((last - first >= 8) && if ((last - first >= 8) &&
fastfloat_strncasecmp(first + 3, str_const_inf<UC>() + 3, 5)) { fastfloat_strncasecmp(first + 3, str_const_inf<UC>() + 3, 5)) {
@ -61,8 +72,11 @@ from_chars_result_t<UC>
} else { } else {
answer.ptr = first + 3; answer.ptr = first + 3;
} }
value = minusSign ? -std::numeric_limits<T>::infinity() value =
: std::numeric_limits<T>::infinity(); #ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
minusSign ? -std::numeric_limits<T>::infinity() :
#endif
std::numeric_limits<T>::infinity();
return answer; return answer;
} }
} }
@ -231,9 +245,11 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
} else { } else {
value = value * binary_format<T>::exact_power_of_ten(pns.exponent); value = value * binary_format<T>::exact_power_of_ten(pns.exponent);
} }
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
if (pns.negative) { if (pns.negative) {
value = -value; value = -value;
} }
#endif
return answer; return answer;
} }
} else { } else {
@ -246,15 +262,21 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
#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 =
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
pns.negative ? T(-0.) :
#endif
T(0.);
return answer; return answer;
} }
#endif #endif
value = T(pns.mantissa) * value = T(pns.mantissa) *
binary_format<T>::exact_power_of_ten(pns.exponent); binary_format<T>::exact_power_of_ten(pns.exponent);
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
if (pns.negative) { if (pns.negative) {
value = -value; value = -value;
} }
#endif
return answer; return answer;
} }
} }
@ -272,7 +294,11 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
if (am.power2 < 0) { if (am.power2 < 0) {
am = digit_comp<T>(pns, am); am = digit_comp<T>(pns, am);
} }
to_float(pns.negative, am, value); to_float(
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
pns.negative,
#endif
am, value);
// Test for over/underflow. // Test for over/underflow.
if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) || if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) ||
am.power2 == binary_format<T>::infinite_power()) { am.power2 == binary_format<T>::infinite_power()) {
@ -294,11 +320,13 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value,
chars_format const fmt = detail::adjust_for_feature_macros(options.format); chars_format const fmt = detail::adjust_for_feature_macros(options.format);
from_chars_result_t<UC> answer; from_chars_result_t<UC> answer;
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
if (uint64_t(fmt & chars_format::skip_white_space)) { if (uint64_t(fmt & chars_format::skip_white_space)) {
while ((first != last) && fast_float::is_space(*first)) { while ((first != last) && fast_float::is_space(*first)) {
first++; first++;
} }
} }
#endif
if (first == last) { if (first == last) {
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
answer.ptr = first; answer.ptr = first;
@ -312,7 +340,11 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value,
answer.ptr = first; answer.ptr = first;
return answer; return answer;
} else { } else {
return detail::parse_infnan(first, last, value, fmt); return detail::parse_infnan(first, last, value
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
, fmt
#endif
);
} }
} }
@ -348,11 +380,13 @@ from_chars_int_advanced(UC const *first, UC const *last, T &value,
int const base = options.base; int const base = options.base;
from_chars_result_t<UC> answer; from_chars_result_t<UC> answer;
#ifndef FASTFLOAT_DISALLOW_ANY_LEADING_SYMBOLS_INCLUDE_SIGN
if (uint64_t(fmt & chars_format::skip_white_space)) { if (uint64_t(fmt & chars_format::skip_white_space)) {
while ((first != last) && fast_float::is_space(*first)) { while ((first != last) && fast_float::is_space(*first)) {
first++; first++;
} }
} }
#endif
if (first == last || base < 2 || base > 36) { if (first == last || base < 2 || base > 36) {
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
answer.ptr = first; answer.ptr = first;