Skip to content

Commit d8ee88e

Browse files
committed
initial version with working constexpr for c++20 compliant compilers
1 parent 898f54f commit d8ee88e

File tree

7 files changed

+100
-36
lines changed

7 files changed

+100
-36
lines changed

include/fast_float/ascii_number.h

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,21 @@
66
#include <cstdint>
77
#include <cstring>
88
#include <iterator>
9+
#include <version>
10+
11+
#if defined(__cpp_lib_bit_cast)
12+
#include <bit>
13+
#endif
914

1015
#include "float_common.h"
1116

1217
namespace fast_float {
1318

1419
// Next function can be micro-optimized, but compilers are entirely
1520
// able to optimize it well.
16-
fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; }
21+
CXX20_CONSTEXPR fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; }
1722

18-
fastfloat_really_inline uint64_t byteswap(uint64_t val) {
23+
CXX20_CONSTEXPR fastfloat_really_inline uint64_t byteswap(uint64_t val) {
1924
return (val & 0xFF00000000000000) >> 56
2025
| (val & 0x00FF000000000000) >> 40
2126
| (val & 0x0000FF0000000000) >> 24
@@ -26,26 +31,40 @@ fastfloat_really_inline uint64_t byteswap(uint64_t val) {
2631
| (val & 0x00000000000000FF) << 56;
2732
}
2833

29-
fastfloat_really_inline uint64_t read_u64(const char *chars) {
34+
CXX20_CONSTEXPR fastfloat_really_inline uint64_t read_u64(const char *chars) {
3035
uint64_t val;
36+
#if defined(__cpp_lib_bit_cast)
37+
val = std::bit_cast<uint64_t>(reinterpret_cast<const char (&)[8]>(chars));
38+
#else
3139
::memcpy(&val, chars, sizeof(uint64_t));
40+
#endif
3241
#if FASTFLOAT_IS_BIG_ENDIAN == 1
3342
// Need to read as-if the number was in little-endian order.
3443
val = byteswap(val);
3544
#endif
3645
return val;
3746
}
3847

39-
fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) {
48+
CXX20_CONSTEXPR fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) {
4049
#if FASTFLOAT_IS_BIG_ENDIAN == 1
4150
// Need to read as-if the number was in little-endian order.
4251
val = byteswap(val);
4352
#endif
53+
#if defined(__cpp_lib_bit_cast)
54+
if (std::is_constant_evaluated()) {
55+
char (&dst)[8] = reinterpret_cast<char (&)[8]>(chars);
56+
const char (&src)[8] = reinterpret_cast<const char (&)[8]>(val);
57+
std::copy(std::begin(src), std::end(src), std::begin(dst));
58+
} else {
59+
::memcpy(chars, &val, sizeof(uint64_t));
60+
}
61+
#else
4462
::memcpy(chars, &val, sizeof(uint64_t));
63+
#endif
4564
}
4665

4766
// credit @aqrit
48-
fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) {
67+
CXX20_CONSTEXPR fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) {
4968
const uint64_t mask = 0x000000FF000000FF;
5069
const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
5170
const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
@@ -55,17 +74,17 @@ fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) {
5574
return uint32_t(val);
5675
}
5776

58-
fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept {
77+
CXX20_CONSTEXPR fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept {
5978
return parse_eight_digits_unrolled(read_u64(chars));
6079
}
6180

6281
// credit @aqrit
63-
fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
82+
CXX20_CONSTEXPR fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
6483
return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
6584
0x8080808080808080));
6685
}
6786

68-
fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept {
87+
CXX20_CONSTEXPR fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept {
6988
return is_made_of_eight_digits_fast(read_u64(chars));
7089
}
7190

@@ -81,7 +100,7 @@ struct parsed_number_string {
81100

82101
// Assuming that you use no more than 19 digits, this will
83102
// parse an ASCII string.
84-
fastfloat_really_inline
103+
CXX20_CONSTEXPR fastfloat_really_inline
85104
parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept {
86105
const chars_format fmt = options.format;
87106
const char decimal_point = options.decimal_point;
@@ -221,7 +240,7 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
221240
// This function could be optimized. In particular, we could stop after 19 digits
222241
// and try to bail out. Furthermore, we should be able to recover the computed
223242
// exponent from the pass in parse_number_string.
224-
fastfloat_really_inline decimal parse_decimal(const char *p, const char *pend, parse_options options) noexcept {
243+
CXX20_CONSTEXPR fastfloat_really_inline decimal parse_decimal(const char *p, const char *pend, parse_options options) noexcept {
225244
const char decimal_point = options.decimal_point;
226245

227246
decimal answer;

include/fast_float/decimal_to_binary.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace fast_float {
1818
// low part corresponding to the least significant bits.
1919
//
2020
template <int bit_precision>
21-
fastfloat_really_inline
21+
CXX20_CONSTEXPR fastfloat_really_inline
2222
value128 compute_product_approximation(int64_t q, uint64_t w) {
2323
const int index = 2 * int(q - powers::smallest_power_of_five);
2424
// For small values of q, e.g., q in [0,27], the answer is always exact because
@@ -56,7 +56,7 @@ namespace detail {
5656
* where
5757
* p = log(5**-q)/log(2) = -q * log(5)/log(2)
5858
*/
59-
fastfloat_really_inline int power(int q) noexcept {
59+
constexpr fastfloat_really_inline int power(int q) noexcept {
6060
return (((152170 + 65536) * q) >> 16) + 63;
6161
}
6262
} // namespace detail
@@ -68,7 +68,7 @@ namespace detail {
6868
// return an adjusted_mantissa with a negative power of 2: the caller should recompute
6969
// in such cases.
7070
template <typename binary>
71-
fastfloat_really_inline
71+
CXX20_CONSTEXPR fastfloat_really_inline
7272
adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
7373
adjusted_mantissa answer;
7474
if ((w == 0) || (q < binary::smallest_power_of_ten())) {

include/fast_float/fast_float.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
#ifndef FASTFLOAT_FAST_FLOAT_H
22
#define FASTFLOAT_FAST_FLOAT_H
33

4+
45
#include <system_error>
6+
#include <version>
7+
8+
#if defined(__cpp_lib_bit_cast)
9+
#define CXX20_CONSTEXPR constexpr
10+
#else
11+
#define CXX20_CONSTEXPR
12+
#endif
513

614
namespace fast_float {
715
enum chars_format {
@@ -18,7 +26,7 @@ struct from_chars_result {
1826
};
1927

2028
struct parse_options {
21-
explicit parse_options(chars_format fmt = chars_format::general,
29+
constexpr explicit parse_options(chars_format fmt = chars_format::general,
2230
char dot = '.')
2331
: format(fmt), decimal_point(dot) {}
2432

@@ -48,14 +56,14 @@ struct parse_options {
4856
* The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
4957
*/
5058
template<typename T>
51-
from_chars_result from_chars(const char *first, const char *last,
59+
CXX20_CONSTEXPR from_chars_result from_chars(const char *first, const char *last,
5260
T &value, chars_format fmt = chars_format::general) noexcept;
5361

5462
/**
5563
* Like from_chars, but accepts an `options` argument to govern number parsing.
5664
*/
5765
template<typename T>
58-
from_chars_result from_chars_advanced(const char *first, const char *last,
66+
CXX20_CONSTEXPR from_chars_result from_chars_advanced(const char *first, const char *last,
5967
T &value, parse_options options) noexcept;
6068

6169
}

include/fast_float/float_common.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <cfloat>
55
#include <cstdint>
66
#include <cassert>
7+
#include <version>
78

89
#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \
910
|| defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \
@@ -73,10 +74,18 @@
7374
#define fastfloat_really_inline inline __attribute__((always_inline))
7475
#endif
7576

77+
#if !defined(CXX20_CONSTEXPR)
78+
#if defined(__cpp_lib_bit_cast)
79+
#define CXX20_CONSTEXPR constexpr
80+
#else
81+
#define CXX20_CONSTEXPR
82+
#endif
83+
#endif
84+
7685
namespace fast_float {
7786

7887
// Compares two ASCII strings in a case insensitive manner.
79-
inline bool fastfloat_strncasecmp(const char *input1, const char *input2,
88+
CXX20_CONSTEXPR inline bool fastfloat_strncasecmp(const char *input1, const char *input2,
8089
size_t length) {
8190
char running_diff{0};
8291
for (size_t i = 0; i < length; i++) {
@@ -103,7 +112,7 @@ struct value128 {
103112
};
104113

105114
/* result might be undefined when input_num is zero */
106-
fastfloat_really_inline int leading_zeroes(uint64_t input_num) {
115+
CXX20_CONSTEXPR fastfloat_really_inline int leading_zeroes(uint64_t input_num) {
107116
assert(input_num > 0);
108117
#ifdef FASTFLOAT_VISUAL_STUDIO
109118
#if defined(_M_X64) || defined(_M_ARM64)
@@ -130,13 +139,13 @@ fastfloat_really_inline int leading_zeroes(uint64_t input_num) {
130139
#ifdef FASTFLOAT_32BIT
131140

132141
// slow emulation routine for 32-bit
133-
fastfloat_really_inline uint64_t emulu(uint32_t x, uint32_t y) {
142+
CXX20_CONSTEXPR fastfloat_really_inline uint64_t emulu(uint32_t x, uint32_t y) {
134143
return x * (uint64_t)y;
135144
}
136145

137146
// slow emulation routine for 32-bit
138147
#if !defined(__MINGW64__)
139-
fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd,
148+
CXX20_CONSTEXPR fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd,
140149
uint64_t *hi) {
141150
uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
142151
uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);

include/fast_float/parse_number.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace detail {
2020
* strings a null-free and fixed.
2121
**/
2222
template <typename T>
23-
from_chars_result parse_infnan(const char *first, const char *last, T &value) noexcept {
23+
CXX20_CONSTEXPR from_chars_result parse_infnan(const char *first, const char *last, T &value) noexcept {
2424
from_chars_result answer;
2525
answer.ptr = first;
2626
answer.ec = std::errc(); // be optimistic
@@ -61,7 +61,7 @@ from_chars_result parse_infnan(const char *first, const char *last, T &value) n
6161
}
6262

6363
template<typename T>
64-
fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) {
64+
CXX20_CONSTEXPR fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) {
6565
uint64_t word = am.mantissa;
6666
word |= uint64_t(am.power2) << binary_format<T>::mantissa_explicit_bits();
6767
word = negative
@@ -83,13 +83,13 @@ fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &va
8383

8484

8585
template<typename T>
86-
from_chars_result from_chars(const char *first, const char *last,
86+
CXX20_CONSTEXPR from_chars_result from_chars(const char *first, const char *last,
8787
T &value, chars_format fmt /*= chars_format::general*/) noexcept {
8888
return from_chars_advanced(first, last, value, parse_options{fmt});
8989
}
9090

9191
template<typename T>
92-
from_chars_result from_chars_advanced(const char *first, const char *last,
92+
CXX20_CONSTEXPR from_chars_result from_chars_advanced(const char *first, const char *last,
9393
T &value, parse_options options) noexcept {
9494

9595
static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported");

include/fast_float/simple_decimal_conversion.h

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,17 @@ namespace fast_float {
2222
namespace detail {
2323

2424
// remove all final zeroes
25-
inline void trim(decimal &h) {
25+
CXX20_CONSTEXPR inline void trim(decimal &h) {
2626
while ((h.num_digits > 0) && (h.digits[h.num_digits - 1] == 0)) {
2727
h.num_digits--;
2828
}
2929
}
3030

3131

3232

33-
inline uint32_t number_of_digits_decimal_left_shift(const decimal &h, uint32_t shift) {
33+
CXX20_CONSTEXPR inline uint32_t number_of_digits_decimal_left_shift(const decimal &h, uint32_t shift) {
3434
shift &= 63;
35-
const static uint16_t number_of_digits_decimal_left_shift_table[65] = {
35+
constexpr uint16_t number_of_digits_decimal_left_shift_table[65] = {
3636
0x0000, 0x0800, 0x0801, 0x0803, 0x1006, 0x1009, 0x100D, 0x1812, 0x1817,
3737
0x181D, 0x2024, 0x202B, 0x2033, 0x203C, 0x2846, 0x2850, 0x285B, 0x3067,
3838
0x3073, 0x3080, 0x388E, 0x389C, 0x38AB, 0x38BB, 0x40CC, 0x40DD, 0x40EF,
@@ -47,7 +47,7 @@ inline uint32_t number_of_digits_decimal_left_shift(const decimal &h, uint32_t s
4747
uint32_t num_new_digits = x_a >> 11;
4848
uint32_t pow5_a = 0x7FF & x_a;
4949
uint32_t pow5_b = 0x7FF & x_b;
50-
const static uint8_t
50+
constexpr uint8_t
5151
number_of_digits_decimal_left_shift_table_powers_of_5[0x051C] = {
5252
5, 2, 5, 1, 2, 5, 6, 2, 5, 3, 1, 2, 5, 1, 5, 6, 2, 5, 7, 8, 1, 2, 5, 3,
5353
9, 0, 6, 2, 5, 1, 9, 5, 3, 1, 2, 5, 9, 7, 6, 5, 6, 2, 5, 4, 8, 8, 2, 8,
@@ -123,7 +123,7 @@ inline uint32_t number_of_digits_decimal_left_shift(const decimal &h, uint32_t s
123123
return num_new_digits;
124124
}
125125

126-
inline uint64_t round(decimal &h) {
126+
CXX20_CONSTEXPR inline uint64_t round(decimal &h) {
127127
if ((h.num_digits == 0) || (h.decimal_point < 0)) {
128128
return 0;
129129
} else if (h.decimal_point > 18) {
@@ -150,7 +150,7 @@ inline uint64_t round(decimal &h) {
150150
}
151151

152152
// computes h * 2^-shift
153-
inline void decimal_left_shift(decimal &h, uint32_t shift) {
153+
CXX20_CONSTEXPR inline void decimal_left_shift(decimal &h, uint32_t shift) {
154154
if (h.num_digits == 0) {
155155
return;
156156
}
@@ -192,7 +192,7 @@ inline void decimal_left_shift(decimal &h, uint32_t shift) {
192192
}
193193

194194
// computes h * 2^shift
195-
inline void decimal_right_shift(decimal &h, uint32_t shift) {
195+
CXX20_CONSTEXPR inline void decimal_right_shift(decimal &h, uint32_t shift) {
196196
uint32_t read_index = 0;
197197
uint32_t write_index = 0;
198198

@@ -241,7 +241,7 @@ inline void decimal_right_shift(decimal &h, uint32_t shift) {
241241
} // namespace detail
242242

243243
template <typename binary>
244-
adjusted_mantissa compute_float(decimal &d) {
244+
CXX20_CONSTEXPR adjusted_mantissa compute_float(decimal &d) {
245245
adjusted_mantissa answer;
246246
if (d.num_digits == 0) {
247247
// should be zero
@@ -271,9 +271,9 @@ adjusted_mantissa compute_float(decimal &d) {
271271
answer.mantissa = 0;
272272
return answer;
273273
}
274-
static const uint32_t max_shift = 60;
275-
static const uint32_t num_powers = 19;
276-
static const uint8_t decimal_powers[19] = {
274+
constexpr uint32_t max_shift = 60;
275+
constexpr uint32_t num_powers = 19;
276+
constexpr uint8_t decimal_powers[19] = {
277277
0, 3, 6, 9, 13, 16, 19, 23, 26, 29, //
278278
33, 36, 39, 43, 46, 49, 53, 56, 59, //
279279
};
@@ -351,7 +351,7 @@ adjusted_mantissa compute_float(decimal &d) {
351351
}
352352

353353
template <typename binary>
354-
adjusted_mantissa parse_long_mantissa(const char *first, const char* last, parse_options options) {
354+
CXX20_CONSTEXPR adjusted_mantissa parse_long_mantissa(const char *first, const char* last, parse_options options) {
355355
decimal d = parse_decimal(first, last, options);
356356
return compute_float<binary>(d);
357357
}

0 commit comments

Comments
 (0)