* Added an option disallow_leading_sign and stronger constexpr / consteval, also significantly reduce register pressure by reducing copy of constant data.

This commit is contained in:
IRainman 2025-03-05 23:21:24 +03:00
parent 3c5b166f6c
commit 63f6abebdf
7 changed files with 128 additions and 120 deletions

View File

@ -202,7 +202,7 @@ 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 &) { FASTFLOAT_CONSTEVAL20 bool simd_parse_if_eight_digits_unrolled(UC const *, uint64_t &) {
return 0; return 0;
} }
@ -269,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) noexcept {
parsed_number_string_t<UC> answer; parsed_number_string_t<UC> answer;
answer.valid = false; answer.valid = false;
answer.lastmatch = p; answer.lastmatch = p;
@ -282,38 +282,41 @@ report_parse_error(UC const *p, parse_error error) {
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>
parse_number_string(UC const *p, UC const *pend, parse_number_string(UC const *p, UC const *pend,
parse_options_t<UC> options) noexcept { const parse_options_t<UC> options) noexcept {
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
UC const decimal_point = options.decimal_point;
parsed_number_string_t<UC> answer; parsed_number_string_t<UC> answer;
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)]]; // assume p < pend, so dereference without checks;
answer.negative = (*p == UC('-')); if (!uint64_t(options.format & chars_format::disallow_leading_sign)) {
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here answer.negative = (*p == UC('-'));
if ((*p == UC('-')) || // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
(uint64_t(fmt & chars_format::allow_leading_plus) && if ((*p == UC('-')) ||
!uint64_t(fmt & detail::basic_json_fmt) && *p == UC('+'))) { (uint64_t(options.format & chars_format::allow_leading_plus) &&
++p; !uint64_t(options.format & detail::basic_json_fmt) && *p == UC('+'))) {
if (p == pend) { ++p;
return report_parse_error<UC>( if (p == pend) {
p, parse_error::missing_integer_or_dot_after_sign);
}
if (uint64_t(fmt & detail::basic_json_fmt)) {
if (!is_integer(*p)) { // a sign must be followed by an integer
return report_parse_error<UC>(p,
parse_error::missing_integer_after_sign);
}
} else {
if (!is_integer(*p) &&
(*p !=
decimal_point)) { // a sign must be followed by an integer or the dot
return report_parse_error<UC>( return report_parse_error<UC>(
p, parse_error::missing_integer_or_dot_after_sign); p, parse_error::missing_integer_or_dot_after_sign);
} }
if (uint64_t(options.format & detail::basic_json_fmt)) {
if (!is_integer(*p)) { // a sign must be followed by an integer
return report_parse_error<UC>(p,
parse_error::missing_integer_after_sign);
}
} else {
if (!is_integer(*p) &&
(*p !=
options.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);
}
}
} }
} else {
answer.negative = false;
} }
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)
@ -329,7 +332,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));
if (uint64_t(fmt & 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) {
return report_parse_error<UC>(p, parse_error::no_digits_in_integer_part); return report_parse_error<UC>(p, parse_error::no_digits_in_integer_part);
@ -341,7 +344,7 @@ parse_number_string(UC const *p, UC const *pend,
} }
int64_t exponent = 0; int64_t exponent = 0;
bool const has_decimal_point = (p != pend) && (*p == decimal_point); bool const has_decimal_point = (p != pend) && (*p == options.decimal_point);
if (has_decimal_point) { if (has_decimal_point) {
++p; ++p;
UC const *before = p; UC const *before = p;
@ -358,7 +361,7 @@ 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;
} }
if (uint64_t(fmt & 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,
@ -369,9 +372,9 @@ parse_number_string(UC const *p, UC const *pend,
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(fmt & 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(fmt & detail::basic_fortran_fmt) && (p != pend) && (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)))) {
UC const *location_of_e = p; UC const *location_of_e = p;
@ -389,7 +392,7 @@ parse_number_string(UC const *p, UC const *pend,
++p; ++p;
} }
if ((p == pend) || !is_integer(*p)) { if ((p == pend) || !is_integer(*p)) {
if (!uint64_t(fmt & chars_format::fixed)) { if (!uint64_t(options.format & 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.
@ -412,8 +415,8 @@ parse_number_string(UC const *p, UC const *pend,
} }
} else { } else {
// If it scientific and not fixed, we have to bail out. // If it scientific and not fixed, we have to bail out.
if (uint64_t(fmt & chars_format::scientific) && if (uint64_t(options.format & chars_format::scientific) &&
!uint64_t(fmt & chars_format::fixed)) { !uint64_t(options.format & chars_format::fixed)) {
return report_parse_error<UC>(p, parse_error::missing_exponential_part); return report_parse_error<UC>(p, parse_error::missing_exponential_part);
} }
} }
@ -431,7 +434,8 @@ parse_number_string(UC const *p, UC const *pend,
// 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 == options.decimal_point)) {
if (*start == UC('0')) { if (*start == UC('0')) {
digit_count--; digit_count--;
} }
@ -474,29 +478,32 @@ template <typename T, typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
parse_int_string(UC const *p, UC const *pend, T &value, parse_int_string(UC const *p, UC const *pend, T &value,
parse_options_t<UC> options) { parse_options_t<UC> options) {
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
int const base = options.base;
from_chars_result_t<UC> answer; from_chars_result_t<UC> answer;
UC const *const first = p; UC const *const first = p;
bool const negative = (*p == UC('-')); bool negative;
if (!uint64_t(options.fmt & chars_format::disallow_leading_sign)) {
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)
#endif #endif
if (!std::is_signed<T>::value && negative) { if (!std::is_signed<T>::value && negative) {
#ifdef FASTFLOAT_VISUAL_STUDIO #ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(pop) #pragma warning(pop)
#endif #endif
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
answer.ptr = first; answer.ptr = first;
return answer; return answer;
} }
if ((*p == UC('-')) || if ((*p == UC('-')) ||
(uint64_t(fmt & chars_format::allow_leading_plus) && (*p == UC('+')))) { (uint64_t(options.fmt & chars_format::allow_leading_plus) && (*p == UC('+')))) {
++p; ++p;
}
} else {
negative = false;
} }
UC const *const start_num = p; UC const *const start_num = p;
@ -510,15 +517,15 @@ parse_int_string(UC const *p, UC const *pend, T &value,
UC const *const start_digits = p; UC const *const start_digits = p;
uint64_t i = 0; uint64_t i = 0;
if (base == 10) { if (options.base == 10) {
loop_parse_if_eight_digits(p, pend, i); // use SIMD if possible loop_parse_if_eight_digits(p, pend, i); // use SIMD if possible
} }
while (p != pend) { while (p != pend) {
uint8_t digit = ch_to_digit(*p); const uint8_t digit = ch_to_digit(*p);
if (digit >= base) { if (digit >= options.base) {
break; break;
} }
i = uint64_t(base) * i + digit; // might overflow, check this later i = static_cast<uint64_t>(options.base) * i + digit; // might overflow, check this later
p++; p++;
} }
@ -539,14 +546,14 @@ parse_int_string(UC const *p, UC const *pend, T &value,
answer.ptr = p; answer.ptr = p;
// check u64 overflow // check u64 overflow
size_t max_digits = max_digits_u64(base); size_t const max_digits = max_digits_u64(options.base);
if (digit_count > max_digits) { if (digit_count > max_digits) {
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 // this check can be eliminated for all other types, but they will all require
// a max_digits(base) equivalent // a max_digits(base) equivalent
if (digit_count == max_digits && i < min_safe_u64(base)) { if (digit_count == max_digits && i < min_safe_u64(options.base)) {
answer.ec = std::errc::result_out_of_range; answer.ec = std::errc::result_out_of_range;
return answer; return answer;
} }

View File

@ -42,14 +42,14 @@ template <uint16_t size> struct stackvec {
// we never need more than 150 limbs // we never need more than 150 limbs
uint16_t length{0}; uint16_t length{0};
stackvec() = default; FASTFLOAT_CONSTEXPR20 stackvec() noexcept = default;
stackvec(stackvec const &) = delete; stackvec(stackvec const &) = delete;
stackvec &operator=(stackvec const &) = delete; stackvec &operator=(stackvec const &) = delete;
stackvec(stackvec &&) = delete; stackvec(stackvec &&) = delete;
stackvec &operator=(stackvec &&other) = delete; stackvec &operator=(stackvec &&other) = delete;
// create stack vector from existing limb span. // create stack vector from existing limb span.
FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) { FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) noexcept {
FASTFLOAT_ASSERT(try_extend(s)); FASTFLOAT_ASSERT(try_extend(s));
} }
@ -435,14 +435,14 @@ 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() noexcept : vec() {}
bigint(bigint const &) = delete; bigint(bigint const &) = delete;
bigint &operator=(bigint const &) = delete; bigint &operator=(bigint const &) = 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) noexcept : vec() {
#ifdef FASTFLOAT_64BIT_LIMB #ifdef FASTFLOAT_64BIT_LIMB
vec.push_unchecked(value); vec.push_unchecked(value);
#else #else
@ -517,8 +517,8 @@ struct bigint : pow5_tables<> {
FASTFLOAT_DEBUG_ASSERT(n != 0); FASTFLOAT_DEBUG_ASSERT(n != 0);
FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8); FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8);
size_t shl = n; size_t const shl = n;
size_t shr = limb_bits - shl; size_t const shr = limb_bits - shl;
limb prev = 0; limb prev = 0;
for (size_t index = 0; index < vec.len(); index++) { for (size_t index = 0; index < vec.len(); index++) {
limb xi = vec[index]; limb xi = vec[index];
@ -556,8 +556,8 @@ struct bigint : pow5_tables<> {
// move the limbs left by `n` bits. // move the limbs left by `n` bits.
FASTFLOAT_CONSTEXPR20 bool shl(size_t n) noexcept { FASTFLOAT_CONSTEXPR20 bool shl(size_t n) noexcept {
size_t rem = n % limb_bits; size_t const rem = n % limb_bits;
size_t div = n / limb_bits; size_t const div = n / limb_bits;
if (rem != 0) { if (rem != 0) {
FASTFLOAT_TRY(shl_bits(rem)); FASTFLOAT_TRY(shl_bits(rem));
} }
@ -598,8 +598,8 @@ struct bigint : pow5_tables<> {
// 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 {
// multiply by a power of 5 // multiply by a power of 5
size_t large_length = sizeof(large_power_of_5) / sizeof(limb); size_t const large_length = sizeof(large_power_of_5) / sizeof(limb);
limb_span large = limb_span(large_power_of_5, large_length); limb_span const large = limb_span(large_power_of_5, large_length);
while (exp >= large_step) { while (exp >= large_step) {
FASTFLOAT_TRY(large_mul(vec, large)); FASTFLOAT_TRY(large_mul(vec, large));
exp -= large_step; exp -= large_step;

View File

@ -32,9 +32,11 @@
defined(__cpp_lib_constexpr_algorithms) && \ defined(__cpp_lib_constexpr_algorithms) && \
__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_CONSTEVAL20 consteval
#define FASTFLOAT_IS_CONSTEXPR 1 #define FASTFLOAT_IS_CONSTEXPR 1
#else #else
#define FASTFLOAT_CONSTEXPR20 #define FASTFLOAT_CONSTEXPR20
#define FASTFLOAT_CONSTEVAL20
#define FASTFLOAT_IS_CONSTEXPR 0 #define FASTFLOAT_IS_CONSTEXPR 0
#endif #endif

View File

@ -19,7 +19,7 @@ namespace fast_float {
// //
template <int bit_precision> template <int bit_precision>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128
compute_product_approximation(int64_t q, uint64_t w) { compute_product_approximation(int64_t q, uint64_t w) noexcept {
int const index = 2 * int(q - powers::smallest_power_of_five); int const 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 // For small values of q, e.g., q in [0,27], the answer is always exact
// because The line value128 firstproduct = full_multiplication(w, // because The line value128 firstproduct = full_multiplication(w,

View File

@ -40,7 +40,7 @@ constexpr static uint64_t powers_of_ten_uint64[] = {1UL,
// 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 int32_t fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int32_t
scientific_exponent(parsed_number_string_t<UC> &num) noexcept { scientific_exponent(const 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) {
@ -258,7 +258,7 @@ 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 void inline FASTFLOAT_CONSTEXPR20 void
parse_mantissa(bigint &result, parsed_number_string_t<UC> &num, parse_mantissa(bigint &result, const parsed_number_string_t<UC> &num,
size_t max_digits, size_t &digits) noexcept { 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
@ -370,9 +370,9 @@ positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept {
// are of the same magnitude. // are of the same magnitude.
template <typename T> template <typename T>
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp( inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
bigint &bigmant, adjusted_mantissa am, int32_t exponent) noexcept { bigint &bigmant, const adjusted_mantissa& am, const int32_t exponent) noexcept {
bigint &real_digits = bigmant; bigint &real_digits = bigmant;
int32_t real_exp = exponent; const 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;
@ -434,7 +434,7 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
// 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 adjusted_mantissa inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
digit_comp(parsed_number_string_t<UC> &num, adjusted_mantissa am) noexcept { digit_comp(const 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;

View File

@ -53,6 +53,7 @@ enum class chars_format : uint64_t {
general = fixed | scientific, 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,
}; };
template <typename UC> struct from_chars_result_t { template <typename UC> struct from_chars_result_t {
@ -63,16 +64,24 @@ template <typename UC> struct from_chars_result_t {
using from_chars_result = from_chars_result_t<char>; using from_chars_result = from_chars_result_t<char>;
template <typename UC> struct parse_options_t { template <typename UC> struct parse_options_t {
constexpr explicit parse_options_t(chars_format fmt = chars_format::general, FASTFLOAT_CONSTEXPR20 explicit parse_options_t(chars_format fmt = chars_format::general,
UC dot = UC('.'), int b = 10) UC dot = UC('.'), unsigned char b = 10) noexcept
: format(fmt), decimal_point(dot), base(b) {} : format(fmt), decimal_point(dot), base(b) {}
/** Which number formats are accepted */ /** Which number formats are accepted */
chars_format format; const chars_format format
// adjust for deprecated feature macros
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS
| chars_format::allow_leading_plus
#endif
#ifdef FASTFLOAT_SKIP_WHITE_SPACE
| chars_format::skip_white_space
#endif
;
/** The character used as decimal point */ /** The character used as decimal point */
UC decimal_point; const UC decimal_point;
/** The base used for integers */ /** The base used for integers */
int base; const unsigned char base;
}; };
using parse_options = parse_options_t<char>; using parse_options = parse_options_t<char>;
@ -212,7 +221,7 @@ using parse_options = parse_options_t<char>;
namespace fast_float { namespace fast_float {
fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() { fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() noexcept {
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED #if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED
return std::is_constant_evaluated(); return std::is_constant_evaluated();
#else #else
@ -265,7 +274,7 @@ struct is_supported_char_type
template <typename UC> template <typename UC>
inline FASTFLOAT_CONSTEXPR14 bool inline FASTFLOAT_CONSTEXPR14 bool
fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase, fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
size_t length) { size_t length) noexcept {
for (size_t i = 0; i < length; ++i) { for (size_t i = 0; i < length; ++i) {
UC const actual = actual_mixedcase[i]; UC const actual = actual_mixedcase[i];
if ((actual < 256 ? actual | 32 : actual) != expected_lowercase[i]) { if ((actual < 256 ? actual | 32 : actual) != expected_lowercase[i]) {
@ -300,9 +309,9 @@ struct value128 {
uint64_t low; uint64_t low;
uint64_t high; uint64_t high;
constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {} constexpr value128(uint64_t _low, uint64_t _high) noexcept : low(_low), high(_high) {}
constexpr value128() : low(0), high(0) {} constexpr value128() noexcept : low(0), high(0) {}
}; };
/* Helper C++14 constexpr generic implementation of leading_zeroes */ /* Helper C++14 constexpr generic implementation of leading_zeroes */
@ -336,8 +345,9 @@ leading_zeroes_generic(uint64_t input_num, int last_bit = 0) {
/* result might be undefined when input_num is zero */ /* result might be undefined when input_num is zero */
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 int fastfloat_really_inline FASTFLOAT_CONSTEXPR20 int
leading_zeroes(uint64_t input_num) { leading_zeroes(uint64_t input_num) noexcept {
assert(input_num > 0); assert(input_num > 0);
[[assume(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);
} }
@ -357,12 +367,12 @@ leading_zeroes(uint64_t input_num) {
} }
// slow emulation routine for 32-bit // slow emulation routine for 32-bit
fastfloat_really_inline constexpr uint64_t emulu(uint32_t x, uint32_t y) { fastfloat_really_inline constexpr uint64_t emulu(uint32_t x, uint32_t y) noexcept {
return x * (uint64_t)y; return x * (uint64_t)y;
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t
umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) { umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) noexcept {
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));
@ -388,7 +398,7 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t _umul128(uint64_t ab,
// compute 64-bit a*b // compute 64-bit a*b
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128
full_multiplication(uint64_t a, uint64_t b) { full_multiplication(uint64_t a, uint64_t b) noexcept {
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);
@ -416,13 +426,13 @@ full_multiplication(uint64_t a, uint64_t b) {
struct adjusted_mantissa { struct adjusted_mantissa {
uint64_t mantissa{0}; uint64_t mantissa{0};
int32_t power2{0}; // a negative value indicates an invalid result int32_t power2{0}; // a negative value indicates an invalid result
adjusted_mantissa() = default; adjusted_mantissa() noexcept = default;
constexpr bool operator==(adjusted_mantissa const &o) const { constexpr bool operator==(adjusted_mantissa const &o) const noexcept {
return mantissa == o.mantissa && power2 == o.power2; return mantissa == o.mantissa && power2 == o.power2;
} }
constexpr bool operator!=(adjusted_mantissa const &o) const { constexpr bool operator!=(adjusted_mantissa const &o) const noexcept {
return mantissa != o.mantissa || power2 != o.power2; return mantissa != o.mantissa || power2 != o.power2;
} }
}; };
@ -979,7 +989,7 @@ 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(const bool negative, 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)
@ -1221,20 +1231,6 @@ operator^=(chars_format &lhs, chars_format rhs) noexcept {
return lhs = (lhs ^ rhs); return lhs = (lhs ^ rhs);
} }
namespace detail {
// adjust for deprecated feature macros
constexpr chars_format adjust_for_feature_macros(chars_format fmt) {
return fmt
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS
| chars_format::allow_leading_plus
#endif
#ifdef FASTFLOAT_SKIP_WHITE_SPACE
| chars_format::skip_white_space
#endif
;
}
} // namespace detail
} // namespace fast_float } // namespace fast_float
#endif #endif

View File

@ -22,18 +22,26 @@ 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, const chars_format fmt) 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
bool const minusSign = (*first == UC('-'));
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here bool minusSign;
if ((*first == UC('-')) || if (!uint64_t(fmt & chars_format::disallow_leading_sign)) {
(uint64_t(fmt & chars_format::allow_leading_plus) && // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
(*first == UC('+')))) { minusSign = (*first == UC('-'));
++first; // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
if ((*first == UC('-')) ||
(uint64_t(fmt & chars_format::allow_leading_plus) &&
(*first == UC('+')))) {
++first;
}
} else {
minusSign = false;
} }
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);
@ -284,17 +292,15 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
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_float_advanced(UC const *first, UC const *last, T &value, from_chars_float_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_float_type<T>::value, static_assert(is_supported_float_type<T>::value,
"only some floating-point types are supported"); "only some floating-point 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");
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
from_chars_result_t<UC> answer; from_chars_result_t<UC> answer;
if (uint64_t(fmt & 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++;
} }
@ -307,12 +313,12 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value,
parsed_number_string_t<UC> pns = parsed_number_string_t<UC> pns =
parse_number_string<UC>(first, last, options); parse_number_string<UC>(first, last, options);
if (!pns.valid) { if (!pns.valid) {
if (uint64_t(fmt & chars_format::no_infnan)) { if (uint64_t(options.format & chars_format::no_infnan)) {
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
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, options.format);
} }
} }
@ -344,16 +350,13 @@ from_chars_int_advanced(UC const *first, UC const *last, T &value,
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");
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
int const base = options.base;
from_chars_result_t<UC> answer; from_chars_result_t<UC> answer;
if (uint64_t(fmt & 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++;
} }
} }
if (first == last || base < 2 || base > 36) { if (first == last || options.base < 2 || options.base > 36) {
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
answer.ptr = first; answer.ptr = first;
return answer; return answer;
@ -370,7 +373,7 @@ template <> struct from_chars_advanced_caller<1> {
template <typename T, typename UC> template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
call(UC const *first, UC const *last, T &value, call(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept { const parse_options_t<UC> options) noexcept {
return from_chars_float_advanced(first, last, value, options); return from_chars_float_advanced(first, last, value, options);
} }
}; };
@ -379,7 +382,7 @@ template <> struct from_chars_advanced_caller<2> {
template <typename T, typename UC> template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
call(UC const *first, UC const *last, T &value, call(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept { const parse_options_t<UC> options) noexcept {
return from_chars_int_advanced(first, last, value, options); return from_chars_int_advanced(first, last, value, options);
} }
}; };
@ -387,7 +390,7 @@ template <> struct from_chars_advanced_caller<2> {
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_advanced(UC const *first, UC const *last, T &value, from_chars_advanced(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept { const parse_options_t<UC> options) noexcept {
return from_chars_advanced_caller< return from_chars_advanced_caller<
size_t(is_supported_float_type<T>::value) + size_t(is_supported_float_type<T>::value) +
2 * size_t(is_supported_integer_type<T>::value)>::call(first, last, value, 2 * size_t(is_supported_integer_type<T>::value)>::call(first, last, value,