Merge pull request #177 from leni536/easy-constexpr

Low-risk C++11 and C++14 constexpr functions
This commit is contained in:
Daniel Lemire 2023-02-28 17:08:37 -05:00 committed by GitHub
commit 52fed52d94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 726 additions and 711 deletions

View File

@ -12,9 +12,11 @@ namespace fast_float {
// Next function can be micro-optimized, but compilers are entirely // Next function can be micro-optimized, but compilers are entirely
// able to optimize it well. // able to optimize it well.
fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; } fastfloat_really_inline constexpr bool is_integer(char c) noexcept {
return c >= '0' && c <= '9';
}
fastfloat_really_inline uint64_t byteswap(uint64_t val) { fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
return (val & 0xFF00000000000000) >> 56 return (val & 0xFF00000000000000) >> 56
| (val & 0x00FF000000000000) >> 40 | (val & 0x00FF000000000000) >> 40
| (val & 0x0000FF0000000000) >> 24 | (val & 0x0000FF0000000000) >> 24
@ -44,7 +46,8 @@ fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) {
} }
// credit @aqrit // credit @aqrit
fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) { fastfloat_really_inline FASTFLOAT_CONSTEXPR14
uint32_t parse_eight_digits_unrolled(uint64_t val) {
const uint64_t mask = 0x000000FF000000FF; const uint64_t mask = 0x000000FF000000FF;
const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
@ -59,7 +62,7 @@ fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars)
} }
// credit @aqrit // credit @aqrit
fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept { fastfloat_really_inline constexpr bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
0x8080808080808080)); 0x8080808080808080));
} }

View File

@ -54,23 +54,23 @@ struct stackvec {
FASTFLOAT_ASSERT(try_extend(s)); FASTFLOAT_ASSERT(try_extend(s));
} }
limb& operator[](size_t index) noexcept { FASTFLOAT_CONSTEXPR14 limb& operator[](size_t index) noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index]; return data[index];
} }
const limb& operator[](size_t index) const noexcept { FASTFLOAT_CONSTEXPR14 const limb& operator[](size_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index]; return data[index];
} }
// index from the end of the container // index from the end of the container
const limb& rindex(size_t index) const noexcept { FASTFLOAT_CONSTEXPR14 const limb& rindex(size_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
size_t rindex = length - index - 1; size_t rindex = length - index - 1;
return data[rindex]; return data[rindex];
} }
// set the length, without bounds checking. // set the length, without bounds checking.
void set_len(size_t len) noexcept { FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept {
length = uint16_t(len); length = uint16_t(len);
} }
constexpr size_t len() const noexcept { constexpr size_t len() const noexcept {
@ -83,12 +83,12 @@ struct stackvec {
return size; return size;
} }
// append item to vector, without bounds checking // append item to vector, without bounds checking
void push_unchecked(limb value) noexcept { FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept {
data[length] = value; data[length] = value;
length++; length++;
} }
// append item to vector, returning if item was added // append item to vector, returning if item was added
bool try_push(limb value) noexcept { FASTFLOAT_CONSTEXPR14 bool try_push(limb value) noexcept {
if (len() < capacity()) { if (len() < capacity()) {
push_unchecked(value); push_unchecked(value);
return true; return true;
@ -137,7 +137,7 @@ struct stackvec {
// check if any limbs are non-zero after the given index. // check if any limbs are non-zero after the given index.
// this needs to be done in reverse order, since the index // this needs to be done in reverse order, since the index
// is relative to the most significant limbs. // is relative to the most significant limbs.
bool nonzero(size_t index) const noexcept { FASTFLOAT_CONSTEXPR14 bool nonzero(size_t index) const noexcept {
while (index < len()) { while (index < len()) {
if (rindex(index) != 0) { if (rindex(index) != 0) {
return true; return true;
@ -147,14 +147,14 @@ struct stackvec {
return false; return false;
} }
// normalize the big integer, so most-significant zero limbs are removed. // normalize the big integer, so most-significant zero limbs are removed.
void normalize() noexcept { FASTFLOAT_CONSTEXPR14 void normalize() noexcept {
while (len() > 0 && rindex(0) == 0) { while (len() > 0 && rindex(0) == 0) {
length--; length--;
} }
} }
}; };
fastfloat_really_inline fastfloat_really_inline FASTFLOAT_CONSTEXPR14
uint64_t empty_hi64(bool& truncated) noexcept { uint64_t empty_hi64(bool& truncated) noexcept {
truncated = false; truncated = false;
return 0; return 0;

View File

@ -63,7 +63,7 @@ namespace detail {
// create an adjusted mantissa, biased by the invalid power2 // create an adjusted mantissa, biased by the invalid power2
// for significant digits already multiplied by 10 ** q. // for significant digits already multiplied by 10 ** q.
template <typename binary> template <typename binary>
fastfloat_really_inline fastfloat_really_inline FASTFLOAT_CONSTEXPR14
adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept { adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
int hilz = int(w >> 63) ^ 1; int hilz = int(w >> 63) ^ 1;
adjusted_mantissa answer; adjusted_mantissa answer;

View File

@ -23,7 +23,8 @@ constexpr static uint64_t powers_of_ten_uint64[] = {
// this algorithm is not even close to optimized, but it has no practical // this algorithm is not even close to optimized, but it has no practical
// effect on performance: in order to have a faster algorithm, we'd need // effect on performance: in order to have a faster algorithm, we'd need
// to slow down performance for faster algorithms, and this is still fast. // to slow down performance for faster algorithms, and this is still fast.
fastfloat_really_inline int32_t scientific_exponent(parsed_number_string& num) noexcept { fastfloat_really_inline FASTFLOAT_CONSTEXPR14
int32_t scientific_exponent(parsed_number_string& 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) {
@ -81,7 +82,8 @@ fastfloat_really_inline adjusted_mantissa to_extended_halfway(T value) noexcept
// round an extended-precision float to the nearest machine float. // round an extended-precision float to the nearest machine float.
template <typename T, typename callback> template <typename T, typename callback>
fastfloat_really_inline void round(adjusted_mantissa& am, callback cb) noexcept { fastfloat_really_inline FASTFLOAT_CONSTEXPR14
void round(adjusted_mantissa& am, callback cb) noexcept {
int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1; int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1;
if (-am.power2 >= mantissa_shift) { if (-am.power2 >= mantissa_shift) {
// have a denormal float // have a denormal float
@ -110,20 +112,16 @@ fastfloat_really_inline void round(adjusted_mantissa& am, callback cb) noexcept
} }
template <typename callback> template <typename callback>
fastfloat_really_inline fastfloat_really_inline FASTFLOAT_CONSTEXPR14
void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) noexcept { void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) noexcept {
uint64_t mask; const uint64_t mask
uint64_t halfway; = (shift == 64)
if (shift == 64) { ? UINT64_MAX
mask = UINT64_MAX; : (uint64_t(1) << shift) - 1;
} else { const uint64_t halfway
mask = (uint64_t(1) << shift) - 1; = (shift == 0)
} ? 0
if (shift == 0) { : uint64_t(1) << (shift - 1);
halfway = 0;
} else {
halfway = uint64_t(1) << (shift - 1);
}
uint64_t truncated_bits = am.mantissa & mask; uint64_t truncated_bits = am.mantissa & mask;
bool is_above = truncated_bits > halfway; bool is_above = truncated_bits > halfway;
bool is_halfway = truncated_bits == halfway; bool is_halfway = truncated_bits == halfway;
@ -140,7 +138,8 @@ void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) n
am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above)); am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above));
} }
fastfloat_really_inline void round_down(adjusted_mantissa& am, int32_t shift) noexcept { fastfloat_really_inline FASTFLOAT_CONSTEXPR14
void round_down(adjusted_mantissa& am, int32_t shift) noexcept {
if (shift == 64) { if (shift == 64) {
am.mantissa = 0; am.mantissa = 0;
} else { } else {
@ -199,7 +198,7 @@ void parse_eight_digits(const char*& p, limb& value, size_t& counter, size_t& co
count += 8; count += 8;
} }
fastfloat_really_inline fastfloat_really_inline FASTFLOAT_CONSTEXPR14
void parse_one_digit(const char*& p, limb& value, size_t& counter, size_t& count) noexcept { void parse_one_digit(const char*& p, limb& value, size_t& counter, size_t& count) noexcept {
value = value * 10 + limb(*p - '0'); value = value * 10 + limb(*p - '0');
p++; p++;

File diff suppressed because it is too large Load Diff

View File

@ -91,11 +91,18 @@
// rust style `try!()` macro, or `?` operator // rust style `try!()` macro, or `?` operator
#define FASTFLOAT_TRY(x) { if (!(x)) return false; } #define FASTFLOAT_TRY(x) { if (!(x)) return false; }
// Testing for https://wg21.link/N3652, adopted in C++14
#if __cpp_constexpr >= 201304
#define FASTFLOAT_CONSTEXPR14 constexpr
#else
#define FASTFLOAT_CONSTEXPR14
#endif
namespace fast_float { namespace fast_float {
// Compares two ASCII strings in a case insensitive manner. // Compares two ASCII strings in a case insensitive manner.
inline bool fastfloat_strncasecmp(const char *input1, const char *input2, inline FASTFLOAT_CONSTEXPR14 bool
size_t length) { fastfloat_strncasecmp(const char *input1, const char *input2, size_t length) {
char running_diff{0}; char running_diff{0};
for (size_t i = 0; i < length; i++) { for (size_t i = 0; i < length; i++) {
running_diff |= (input1[i] ^ input2[i]); running_diff |= (input1[i] ^ input2[i]);
@ -112,14 +119,14 @@ template <typename T>
struct span { struct span {
const T* ptr; const T* ptr;
size_t length; size_t length;
span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {} constexpr span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {}
span() : ptr(nullptr), length(0) {} constexpr span() : ptr(nullptr), length(0) {}
constexpr size_t len() const noexcept { constexpr size_t len() const noexcept {
return length; return length;
} }
const T& operator[](size_t index) const noexcept { FASTFLOAT_CONSTEXPR14 const T& operator[](size_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
return ptr[index]; return ptr[index];
} }
@ -128,8 +135,8 @@ struct span {
struct value128 { struct value128 {
uint64_t low; uint64_t low;
uint64_t high; uint64_t high;
value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {} constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
value128() : low(0), high(0) {} constexpr value128() : low(0), high(0) {}
}; };
/* result might be undefined when input_num is zero */ /* result might be undefined when input_num is zero */
@ -160,14 +167,14 @@ fastfloat_really_inline int leading_zeroes(uint64_t input_num) {
#ifdef FASTFLOAT_32BIT #ifdef FASTFLOAT_32BIT
// slow emulation routine for 32-bit // slow emulation routine for 32-bit
fastfloat_really_inline uint64_t emulu(uint32_t x, uint32_t y) { fastfloat_really_inline constexpr uint64_t emulu(uint32_t x, uint32_t y) {
return x * (uint64_t)y; return x * (uint64_t)y;
} }
// slow emulation routine for 32-bit // slow emulation routine for 32-bit
#if !defined(__MINGW64__) #if !defined(__MINGW64__)
fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd, fastfloat_really_inline constexpr uint64_t _umul128(uint64_t ab, uint64_t cd,
uint64_t *hi) { uint64_t *hi) {
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));
@ -207,10 +214,10 @@ 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() = default;
bool operator==(const adjusted_mantissa &o) const { constexpr bool operator==(const adjusted_mantissa &o) const {
return mantissa == o.mantissa && power2 == o.power2; return mantissa == o.mantissa && power2 == o.power2;
} }
bool operator!=(const adjusted_mantissa &o) const { constexpr bool operator!=(const adjusted_mantissa &o) const {
return mantissa != o.mantissa || power2 != o.power2; return mantissa != o.mantissa || power2 != o.power2;
} }
}; };
@ -453,21 +460,26 @@ fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &va
} }
#if FASTFLOAT_SKIP_WHITE_SPACE // disabled by default #if FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
inline bool is_space(uint8_t c) { template <typename = void>
static const bool table[] = { struct space_lut {
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, static constexpr bool value[] = {
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 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,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
return table[c]; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
} };
template <typename T>
constexpr bool space_lut<T>::value[];
inline constexpr bool is_space(uint8_t c) { return space_lut<>::value[c]; }
#endif #endif
} // namespace fast_float } // namespace fast_float