Skip to content

Commit 1a56fe5

Browse files
authored
Merge pull request #100 from jrahlf/constexpr
constexpr for c++20 compliant compilers
2 parents 898f54f + 4e13ec1 commit 1a56fe5

File tree

7 files changed

+115
-36
lines changed

7 files changed

+115
-36
lines changed

include/fast_float/ascii_number.h

+33-10
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,24 @@
77
#include <cstring>
88
#include <iterator>
99

10+
#if defined __has_include
11+
#if __has_include(<version>)
12+
#include <version>
13+
#if defined(__cpp_lib_bit_cast)
14+
#include <bit>
15+
#endif
16+
#endif
17+
#endif
18+
1019
#include "float_common.h"
1120

1221
namespace fast_float {
1322

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

18-
fastfloat_really_inline uint64_t byteswap(uint64_t val) {
27+
CXX20_CONSTEXPR fastfloat_really_inline uint64_t byteswap(uint64_t val) {
1928
return (val & 0xFF00000000000000) >> 56
2029
| (val & 0x00FF000000000000) >> 40
2130
| (val & 0x0000FF0000000000) >> 24
@@ -26,26 +35,40 @@ fastfloat_really_inline uint64_t byteswap(uint64_t val) {
2635
| (val & 0x00000000000000FF) << 56;
2736
}
2837

29-
fastfloat_really_inline uint64_t read_u64(const char *chars) {
38+
CXX20_CONSTEXPR fastfloat_really_inline uint64_t read_u64(const char *chars) {
3039
uint64_t val;
40+
#if defined(__cpp_lib_bit_cast)
41+
val = std::bit_cast<uint64_t>(reinterpret_cast<const char (&)[8]>(chars));
42+
#else
3143
::memcpy(&val, chars, sizeof(uint64_t));
44+
#endif
3245
#if FASTFLOAT_IS_BIG_ENDIAN == 1
3346
// Need to read as-if the number was in little-endian order.
3447
val = byteswap(val);
3548
#endif
3649
return val;
3750
}
3851

39-
fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) {
52+
CXX20_CONSTEXPR fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) {
4053
#if FASTFLOAT_IS_BIG_ENDIAN == 1
4154
// Need to read as-if the number was in little-endian order.
4255
val = byteswap(val);
4356
#endif
57+
#if defined(__cpp_lib_bit_cast)
58+
if (std::is_constant_evaluated()) {
59+
char (&dst)[8] = reinterpret_cast<char (&)[8]>(chars);
60+
const char (&src)[8] = reinterpret_cast<const char (&)[8]>(val);
61+
std::copy(std::begin(src), std::end(src), std::begin(dst));
62+
} else {
63+
::memcpy(chars, &val, sizeof(uint64_t));
64+
}
65+
#else
4466
::memcpy(chars, &val, sizeof(uint64_t));
67+
#endif
4568
}
4669

4770
// credit @aqrit
48-
fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) {
71+
CXX20_CONSTEXPR fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) {
4972
const uint64_t mask = 0x000000FF000000FF;
5073
const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
5174
const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
@@ -55,17 +78,17 @@ fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) {
5578
return uint32_t(val);
5679
}
5780

58-
fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept {
81+
CXX20_CONSTEXPR fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept {
5982
return parse_eight_digits_unrolled(read_u64(chars));
6083
}
6184

6285
// credit @aqrit
63-
fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
86+
CXX20_CONSTEXPR fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
6487
return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
6588
0x8080808080808080));
6689
}
6790

68-
fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept {
91+
CXX20_CONSTEXPR fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept {
6992
return is_made_of_eight_digits_fast(read_u64(chars));
7093
}
7194

@@ -81,7 +104,7 @@ struct parsed_number_string {
81104

82105
// Assuming that you use no more than 19 digits, this will
83106
// parse an ASCII string.
84-
fastfloat_really_inline
107+
CXX20_CONSTEXPR fastfloat_really_inline
85108
parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept {
86109
const chars_format fmt = options.format;
87110
const char decimal_point = options.decimal_point;
@@ -221,7 +244,7 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
221244
// This function could be optimized. In particular, we could stop after 19 digits
222245
// and try to bail out. Furthermore, we should be able to recover the computed
223246
// 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 {
247+
CXX20_CONSTEXPR fastfloat_really_inline decimal parse_decimal(const char *p, const char *pend, parse_options options) noexcept {
225248
const char decimal_point = options.decimal_point;
226249

227250
decimal answer;

include/fast_float/decimal_to_binary.h

+3-3
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

+18-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,23 @@
11
#ifndef FASTFLOAT_FAST_FLOAT_H
22
#define FASTFLOAT_FAST_FLOAT_H
33

4+
45
#include <system_error>
56

7+
#if !defined(CXX20_CONSTEXPR)
8+
#define CXX20_CONSTEXPR
9+
#if defined __has_include
10+
#if __has_include(<version>)
11+
#include <version>
12+
#if defined(__cpp_lib_bit_cast)
13+
#undef CXX20_CONSTEXPR
14+
#define CXX20_CONSTEXPR constexpr
15+
#define HAS_CXX20_CONSTEXPR 1
16+
#endif
17+
#endif
18+
#endif
19+
#endif
20+
621
namespace fast_float {
722
enum chars_format {
823
scientific = 1<<0,
@@ -18,7 +33,7 @@ struct from_chars_result {
1833
};
1934

2035
struct parse_options {
21-
explicit parse_options(chars_format fmt = chars_format::general,
36+
constexpr explicit parse_options(chars_format fmt = chars_format::general,
2237
char dot = '.')
2338
: format(fmt), decimal_point(dot) {}
2439

@@ -48,14 +63,14 @@ struct parse_options {
4863
* The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
4964
*/
5065
template<typename T>
51-
from_chars_result from_chars(const char *first, const char *last,
66+
CXX20_CONSTEXPR from_chars_result from_chars(const char *first, const char *last,
5267
T &value, chars_format fmt = chars_format::general) noexcept;
5368

5469
/**
5570
* Like from_chars, but accepts an `options` argument to govern number parsing.
5671
*/
5772
template<typename T>
58-
from_chars_result from_chars_advanced(const char *first, const char *last,
73+
CXX20_CONSTEXPR from_chars_result from_chars_advanced(const char *first, const char *last,
5974
T &value, parse_options options) noexcept;
6075

6176
}

include/fast_float/float_common.h

+18-4
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,24 @@
7373
#define fastfloat_really_inline inline __attribute__((always_inline))
7474
#endif
7575

76+
#if !defined(CXX20_CONSTEXPR)
77+
#define CXX20_CONSTEXPR
78+
#if defined __has_include
79+
#if __has_include(<version>)
80+
#include <version>
81+
#if defined(__cpp_lib_bit_cast)
82+
#undef CXX20_CONSTEXPR
83+
#define CXX20_CONSTEXPR constexpr
84+
#define HAS_CXX20_CONSTEXPR 1
85+
#endif
86+
#endif
87+
#endif
88+
#endif
89+
7690
namespace fast_float {
7791

7892
// Compares two ASCII strings in a case insensitive manner.
79-
inline bool fastfloat_strncasecmp(const char *input1, const char *input2,
93+
CXX20_CONSTEXPR inline bool fastfloat_strncasecmp(const char *input1, const char *input2,
8094
size_t length) {
8195
char running_diff{0};
8296
for (size_t i = 0; i < length; i++) {
@@ -103,7 +117,7 @@ struct value128 {
103117
};
104118

105119
/* result might be undefined when input_num is zero */
106-
fastfloat_really_inline int leading_zeroes(uint64_t input_num) {
120+
CXX20_CONSTEXPR fastfloat_really_inline int leading_zeroes(uint64_t input_num) {
107121
assert(input_num > 0);
108122
#ifdef FASTFLOAT_VISUAL_STUDIO
109123
#if defined(_M_X64) || defined(_M_ARM64)
@@ -130,13 +144,13 @@ fastfloat_really_inline int leading_zeroes(uint64_t input_num) {
130144
#ifdef FASTFLOAT_32BIT
131145

132146
// slow emulation routine for 32-bit
133-
fastfloat_really_inline uint64_t emulu(uint32_t x, uint32_t y) {
147+
CXX20_CONSTEXPR fastfloat_really_inline uint64_t emulu(uint32_t x, uint32_t y) {
134148
return x * (uint64_t)y;
135149
}
136150

137151
// slow emulation routine for 32-bit
138152
#if !defined(__MINGW64__)
139-
fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd,
153+
CXX20_CONSTEXPR fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd,
140154
uint64_t *hi) {
141155
uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
142156
uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);

include/fast_float/parse_number.h

+4-4
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

+12-12
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)