Added a config option FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN for faster and more compact code parsing numbers with input support only positive C/C++ style numbers without nan or inf. That case is very useful in mathematical applications, game development, CSS parsing, embedded code, etc...

Additional improve in constant initialization.
This commit is contained in:
IRainman 2025-03-06 22:25:05 +03:00
parent 28795646ab
commit 9ebac23081
4 changed files with 113 additions and 47 deletions

View File

@ -234,6 +234,7 @@ loop_parse_if_eight_digits(char const *&p, char const *const pend,
enum class parse_error { enum class parse_error {
no_error, no_error,
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
// [JSON-only] The minus sign must be followed by an integer. // [JSON-only] The minus sign must be followed by an integer.
missing_integer_after_sign, missing_integer_after_sign,
// A sign must be followed by an integer or dot. // A sign must be followed by an integer or dot.
@ -245,6 +246,7 @@ enum class parse_error {
// [JSON-only] If there is a decimal point, there must be digits in the // [JSON-only] If there is a decimal point, there must be digits in the
// fractional part. // fractional part.
no_digits_in_fractional_part, no_digits_in_fractional_part,
#endif
// The mantissa must have at least one digit. // The mantissa must have at least one digit.
no_digits_in_mantissa, no_digits_in_mantissa,
// Scientific notation requires an exponential part. // Scientific notation requires an exponential part.
@ -255,7 +257,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_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
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
@ -288,7 +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)]]; // assume p < pend, so dereference without checks; [[assume(p < pend)]]; // assume p < pend, so dereference without checks;
if (!uint64_t(options.format & chars_format::disallow_leading_sign)) { #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
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('-')) ||
@ -313,9 +317,7 @@ parse_number_string(UC const *p, UC const *pend,
} }
} }
} }
} else { #endif
answer.negative = false;
}
UC const *const start_digits = p; UC const *const start_digits = p;
@ -332,6 +334,7 @@ parse_number_string(UC const *p, UC const *pend,
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<UC const>(start_digits, size_t(digit_count)); answer.integer = span<UC const>(start_digits, size_t(digit_count));
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (uint64_t(options.format & detail::basic_json_fmt)) { if (uint64_t(options.format & detail::basic_json_fmt)) {
// at least 1 digit in integer part, without leading zeros // at least 1 digit in integer part, without leading zeros
if (digit_count == 0) { if (digit_count == 0) {
@ -342,6 +345,7 @@ parse_number_string(UC const *p, UC const *pend,
parse_error::leading_zeros_in_integer_part); parse_error::leading_zeros_in_integer_part);
} }
} }
#endif
int64_t exponent = 0; int64_t exponent = 0;
bool const has_decimal_point = (p != pend) && (*p == options.decimal_point); bool const has_decimal_point = (p != pend) && (*p == options.decimal_point);
@ -361,22 +365,28 @@ parse_number_string(UC const *p, UC const *pend,
answer.fraction = span<UC const>(before, size_t(p - before)); answer.fraction = span<UC const>(before, size_t(p - before));
digit_count -= exponent; digit_count -= exponent;
} }
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (uint64_t(options.format & detail::basic_json_fmt)) { if (uint64_t(options.format & detail::basic_json_fmt)) {
// 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, return report_parse_error<UC>(p,
parse_error::no_digits_in_fractional_part); parse_error::no_digits_in_fractional_part);
} }
} else if (digit_count == } else
#endif
if (digit_count ==
0) { // we must have encountered at least one integer! 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 ((uint64_t(options.format & chars_format::scientific) && (p != pend) && if ((uint64_t(options.format & chars_format::scientific) && (p != pend) &&
((UC('e') == *p) || (UC('E') == *p))) || ((UC('e') == *p) || (UC('E') == *p)))
(uint64_t(options.format & detail::basic_fortran_fmt) && (p != pend) && #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
|| (uint64_t(options.format & detail::basic_fortran_fmt) && (p != pend) &&
((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) || ((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) ||
(UC('D') == *p)))) { (UC('D') == *p)))
#endif
) {
UC const *location_of_e = p; UC const *location_of_e = p;
if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) || if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) ||
(UC('D') == *p)) { (UC('D') == *p)) {
@ -483,9 +493,8 @@ parse_int_string(UC const *p, UC const *pend, T &value,
UC const *const first = p; UC const *const first = p;
bool negative; #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (!uint64_t(options.fmt & chars_format::disallow_leading_sign)) { bool const negative = (*p == UC('-'));
negative = (*p == UC('-'));
#ifdef FASTFLOAT_VISUAL_STUDIO #ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4127) #pragma warning(disable : 4127)
@ -502,9 +511,7 @@ parse_int_string(UC const *p, UC const *pend, T &value,
(uint64_t(options.fmt & chars_format::allow_leading_plus) && (*p == UC('+')))) { (uint64_t(options.fmt & chars_format::allow_leading_plus) && (*p == UC('+')))) {
++p; ++p;
} }
} else { #endif
negative = false;
}
UC const *const start_num = p; UC const *const start_num = p;
@ -560,12 +567,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_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
+ 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_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (negative) { if (negative) {
#ifdef FASTFLOAT_VISUAL_STUDIO #ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(push) #pragma warning(push)
@ -583,8 +595,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_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
} }
#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_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
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

@ -35,25 +35,29 @@ namespace fast_float {
enum class chars_format : uint64_t; enum class chars_format : uint64_t;
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
namespace detail { namespace detail {
constexpr chars_format basic_json_fmt = chars_format(1 << 5); constexpr chars_format basic_json_fmt = chars_format(1 << 5);
constexpr chars_format basic_fortran_fmt = chars_format(1 << 6); constexpr chars_format basic_fortran_fmt = chars_format(1 << 6);
} // namespace detail } // namespace detail
#endif
enum class chars_format : uint64_t { enum class chars_format : uint64_t {
scientific = 1 << 0, scientific = 1 << 0,
fixed = 1 << 2, fixed = 1 << 2,
general = fixed | scientific,
hex = 1 << 3, hex = 1 << 3,
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
no_infnan = 1 << 4, no_infnan = 1 << 4,
// RFC 8259: https://datatracker.ietf.org/doc/html/rfc8259#section-6 // RFC 8259: https://datatracker.ietf.org/doc/html/rfc8259#section-6
json = uint64_t(detail::basic_json_fmt) | fixed | scientific | no_infnan, json = uint64_t(detail::basic_json_fmt) | general | no_infnan,
// Extension of RFC 8259 where, e.g., "inf" and "nan" are allowed. // Extension of RFC 8259 where, e.g., "inf" and "nan" are allowed.
json_or_infnan = uint64_t(detail::basic_json_fmt) | fixed | scientific, json_or_infnan = uint64_t(detail::basic_json_fmt) | general,
fortran = uint64_t(detail::basic_fortran_fmt) | fixed | scientific, fortran = uint64_t(detail::basic_fortran_fmt) | general,
general = fixed | scientific,
allow_leading_plus = 1 << 7, allow_leading_plus = 1 << 7,
skip_white_space = 1 << 8, skip_white_space = 1 << 8,
disallow_leading_sign = 1 << 9, disallow_leading_sign = 1 << 9,
#endif
}; };
template <typename UC> struct from_chars_result_t { template <typename UC> struct from_chars_result_t {
@ -616,6 +620,8 @@ template <> inline constexpr int binary_format<float>::infinite_power() {
return 0xFF; return 0xFF;
} }
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
template <> inline constexpr int binary_format<double>::sign_index() { template <> inline constexpr int binary_format<double>::sign_index() {
return 63; return 63;
} }
@ -624,6 +630,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;
@ -750,10 +758,14 @@ inline constexpr int binary_format<std::float16_t>::infinite_power() {
return 0x1F; return 0x1F;
} }
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
template <> inline constexpr int binary_format<std::float16_t>::sign_index() { template <> inline constexpr int binary_format<std::float16_t>::sign_index() {
return 15; return 15;
} }
#endif
template <> template <>
inline constexpr int binary_format<std::float16_t>::largest_power_of_ten() { inline constexpr int binary_format<std::float16_t>::largest_power_of_ten() {
return 4; return 4;
@ -873,10 +885,14 @@ inline constexpr int binary_format<std::bfloat16_t>::infinite_power() {
return 0xFF; return 0xFF;
} }
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
template <> inline constexpr int binary_format<std::bfloat16_t>::sign_index() { template <> inline constexpr int binary_format<std::bfloat16_t>::sign_index() {
return 15; return 15;
} }
#endif
template <> template <>
inline constexpr int binary_format<std::bfloat16_t>::largest_power_of_ten() { inline constexpr int binary_format<std::bfloat16_t>::largest_power_of_ten() {
return 38; return 38;
@ -989,13 +1005,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(const bool negative, const adjusted_mantissa am, T &value) noexcept { to_float(
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
const bool negative,
#endif
const adjusted_mantissa am, T &value) noexcept {
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_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
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
@ -1042,6 +1064,8 @@ template <typename UC> static constexpr int int_cmp_len() {
return sizeof(uint64_t) / sizeof(UC); return sizeof(uint64_t) / sizeof(UC);
} }
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_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"; }
@ -1084,6 +1108,8 @@ template <> constexpr char8_t const *str_const_inf<char8_t>() {
} }
#endif #endif
#endif
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,

View File

@ -14,6 +14,7 @@
namespace fast_float { namespace fast_float {
namespace detail { namespace detail {
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
/** /**
* Special case +inf, -inf, nan, infinity, -infinity. * Special case +inf, -inf, nan, infinity, -infinity.
* The case comparisons could be made much faster given that we know that the * The case comparisons could be made much faster given that we know that the
@ -77,6 +78,7 @@ from_chars_result_t<UC>
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
return answer; return answer;
} }
#endif
/** /**
* Returns true if the floating-pointing rounding mode is to 'nearest'. * Returns true if the floating-pointing rounding mode is to 'nearest'.
@ -239,9 +241,11 @@ from_chars_advanced(const 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_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (pns.negative) { if (pns.negative) {
value = -value; value = -value;
} }
#endif
return answer; return answer;
} }
} else { } else {
@ -254,15 +258,21 @@ from_chars_advanced(const 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_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
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_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (pns.negative) { if (pns.negative) {
value = -value; value = -value;
} }
#endif
return answer; return answer;
} }
} }
@ -280,7 +290,11 @@ from_chars_advanced(const 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_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
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()) {
@ -300,26 +314,32 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value,
"only char, wchar_t, char16_t and char32_t are supported"); "only char, wchar_t, char16_t and char32_t are supported");
from_chars_result_t<UC> answer; from_chars_result_t<UC> answer;
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (uint64_t(options.format & chars_format::skip_white_space)) { if (uint64_t(options.format & 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;
return answer; return answer;
} }
parsed_number_string_t<UC> pns = parsed_number_string_t<UC> const pns =
parse_number_string<UC>(first, last, options); parse_number_string<UC>(first, last, options);
if (!pns.valid) { if (!pns.valid) {
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (uint64_t(options.format & chars_format::no_infnan)) { if (uint64_t(options.format & chars_format::no_infnan)) {
#endif
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
answer.ptr = first; answer.ptr = first;
return answer; return answer;
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
} else { } else {
return detail::parse_infnan(first, last, value, options.format); return detail::parse_infnan(first, last, value, options.format);
} }
#endif
} }
// call overload that takes parsed_number_string_t directly. // call overload that takes parsed_number_string_t directly.
@ -335,28 +355,29 @@ from_chars(UC const *first, UC const *last, T &value, int base) noexcept {
static_assert(is_supported_char_type<UC>::value, static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported"); "only char, wchar_t, char16_t and char32_t are supported");
parse_options_t<UC> options; parse_options_t<UC> const options(chars_format::general, UC('.'), base);
options.base = base;
return from_chars_advanced(first, last, value, options); return from_chars_advanced(first, last, value, options);
} }
template <typename T, typename UC> template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_int_advanced(UC const *first, UC const *last, T &value, from_chars_int_advanced(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept { const parse_options_t<UC> options) noexcept {
static_assert(is_supported_integer_type<T>::value, static_assert(is_supported_integer_type<T>::value,
"only integer types are supported"); "only integer types are supported");
static_assert(is_supported_char_type<UC>::value, static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported"); "only char, wchar_t, char16_t and char32_t are supported");
from_chars_result_t<UC> answer; #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (uint64_t(options.format & chars_format::skip_white_space)) { if (uint64_t(options.format & 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 || options.base < 2 || options.base > 36) { if (first == last || options.base < 2 || options.base > 36) {
from_chars_result_t<UC> answer;
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
answer.ptr = first; answer.ptr = first;
return answer; return answer;