Skip to content

Commit b50a729

Browse files
authored
Merge pull request #182 from leni536/constexpr-from_chars
Constexpr from_chars
2 parents c09c855 + a1a7c4e commit b50a729

File tree

14 files changed

+278
-105
lines changed

14 files changed

+278
-105
lines changed

.github/workflows/vs17-cxx20.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@ jobs:
1818
- name: checkout
1919
uses: actions/checkout@v3
2020
- name: configure
21-
run: |
22-
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DCMAKE_CXX_STANDARD=20 -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination ..
21+
run: >-
22+
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}}
23+
-DCMAKE_CXX_STANDARD=20
24+
-DFASTFLOAT_TEST=ON
25+
-DFASTFLOAT_CONSTEXPR_TESTS=ON
26+
-DCMAKE_INSTALL_PREFIX:PATH=destination
2327
- name: build
2428
run: |
2529
cmake --build build --verbose --config ${{matrix.cfg}} --parallel

include/fast_float/ascii_number.h

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,16 @@ fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
2727
| (val & 0x00000000000000FF) << 56;
2828
}
2929

30-
fastfloat_really_inline uint64_t read_u64(const char *chars) {
30+
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
31+
uint64_t read_u64(const char *chars) {
32+
if (cpp20_and_in_constexpr()) {
33+
uint64_t val = 0;
34+
for(int i = 0; i < 8; ++i) {
35+
val |= uint64_t(*chars) << (i*8);
36+
++chars;
37+
}
38+
return val;
39+
}
3140
uint64_t val;
3241
::memcpy(&val, chars, sizeof(uint64_t));
3342
#if FASTFLOAT_IS_BIG_ENDIAN == 1
@@ -37,7 +46,16 @@ fastfloat_really_inline uint64_t read_u64(const char *chars) {
3746
return val;
3847
}
3948

40-
fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) {
49+
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
50+
void write_u64(uint8_t *chars, uint64_t val) {
51+
if (cpp20_and_in_constexpr()) {
52+
for(int i = 0; i < 8; ++i) {
53+
*chars = uint8_t(val);
54+
val >>= 8;
55+
++chars;
56+
}
57+
return;
58+
}
4159
#if FASTFLOAT_IS_BIG_ENDIAN == 1
4260
// Need to read as-if the number was in little-endian order.
4361
val = byteswap(val);
@@ -57,7 +75,8 @@ uint32_t parse_eight_digits_unrolled(uint64_t val) {
5775
return uint32_t(val);
5876
}
5977

60-
fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept {
78+
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
79+
uint32_t parse_eight_digits_unrolled(const char *chars) noexcept {
6180
return parse_eight_digits_unrolled(read_u64(chars));
6281
}
6382

@@ -67,7 +86,8 @@ fastfloat_really_inline constexpr bool is_made_of_eight_digits_fast(uint64_t val
6786
0x8080808080808080));
6887
}
6988

70-
fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept {
89+
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
90+
bool is_made_of_eight_digits_fast(const char *chars) noexcept {
7191
return is_made_of_eight_digits_fast(read_u64(chars));
7292
}
7393

@@ -87,7 +107,7 @@ struct parsed_number_string {
87107

88108
// Assuming that you use no more than 19 digits, this will
89109
// parse an ASCII string.
90-
fastfloat_really_inline
110+
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
91111
parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept {
92112
const chars_format fmt = options.format;
93113
const char decimal_point = options.decimal_point;

include/fast_float/bigint.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ struct bigint : pow5_tables<> {
514514
// move limbs
515515
limb* dst = vec.data + n;
516516
const limb* src = vec.data;
517-
::memmove(dst, src, sizeof(limb) * vec.len());
517+
std::copy_backward(src, src + vec.len(), dst + vec.len());
518518
// fill in empty limbs
519519
limb* first = vec.data;
520520
limb* last = first + n;
@@ -594,7 +594,12 @@ struct bigint : pow5_tables<> {
594594
exp -= small_step;
595595
}
596596
if (exp != 0) {
597-
FASTFLOAT_TRY(small_mul(vec, limb(small_power_of_5[exp])));
597+
// Work around clang bug https://godbolt.org/z/zedh7rrhc
598+
// This is similar to https://github.com/llvm/llvm-project/issues/47746,
599+
// except the workaround described there don't work here
600+
FASTFLOAT_TRY(
601+
small_mul(vec, limb(((void)small_power_of_5[0], small_power_of_5[exp])))
602+
);
598603
}
599604

600605
return true;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#ifndef FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H
2+
#define FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H
3+
4+
#ifdef __has_include
5+
#if __has_include(<version>)
6+
#include <version>
7+
#endif
8+
#endif
9+
10+
// Testing for https://wg21.link/N3652, adopted in C++14
11+
#if __cpp_constexpr >= 201304
12+
#define FASTFLOAT_CONSTEXPR14 constexpr
13+
#else
14+
#define FASTFLOAT_CONSTEXPR14
15+
#endif
16+
17+
#if __cpp_lib_bit_cast >= 201806L
18+
#define FASTFLOAT_HAS_BIT_CAST 1
19+
#else
20+
#define FASTFLOAT_HAS_BIT_CAST 0
21+
#endif
22+
23+
#if __cpp_lib_is_constant_evaluated >= 201811L
24+
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1
25+
#else
26+
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0
27+
#endif
28+
29+
// Testing for relevant C++20 constexpr library features
30+
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED \
31+
&& FASTFLOAT_HAS_BIT_CAST \
32+
&& __cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/
33+
#define FASTFLOAT_CONSTEXPR20 constexpr
34+
#define FASTFLOAT_IS_CONSTEXPR 1
35+
#else
36+
#define FASTFLOAT_CONSTEXPR20
37+
#define FASTFLOAT_IS_CONSTEXPR 0
38+
#endif
39+
40+
#endif // FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H

include/fast_float/decimal_to_binary.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespace fast_float {
1717
// low part corresponding to the least significant bits.
1818
//
1919
template <int bit_precision>
20-
fastfloat_really_inline
20+
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
2121
value128 compute_product_approximation(int64_t q, uint64_t w) {
2222
const int index = 2 * int(q - powers::smallest_power_of_five);
2323
// For small values of q, e.g., q in [0,27], the answer is always exact because
@@ -76,7 +76,7 @@ adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept
7676
// w * 10 ** q, without rounding the representation up.
7777
// the power2 in the exponent will be adjusted by invalid_am_bias.
7878
template <typename binary>
79-
fastfloat_really_inline
79+
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
8080
adjusted_mantissa compute_error(int64_t q, uint64_t w) noexcept {
8181
int lz = leading_zeroes(w);
8282
w <<= lz;
@@ -90,7 +90,7 @@ adjusted_mantissa compute_error(int64_t q, uint64_t w) noexcept {
9090
// return an adjusted_mantissa with a negative power of 2: the caller should recompute
9191
// in such cases.
9292
template <typename binary>
93-
fastfloat_really_inline
93+
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
9494
adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
9595
adjusted_mantissa answer;
9696
if ((w == 0) || (q < binary::smallest_power_of_ten())) {

include/fast_float/digit_comparison.h

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ int32_t scientific_exponent(parsed_number_string& num) noexcept {
4444

4545
// this converts a native floating-point number to an extended-precision float.
4646
template <typename T>
47-
fastfloat_really_inline adjusted_mantissa to_extended(T value) noexcept {
47+
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
48+
adjusted_mantissa to_extended(T value) noexcept {
4849
using equiv_uint = typename binary_format<T>::equiv_uint;
4950
constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
5051
constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
@@ -53,7 +54,11 @@ fastfloat_really_inline adjusted_mantissa to_extended(T value) noexcept {
5354
adjusted_mantissa am;
5455
int32_t bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent();
5556
equiv_uint bits;
57+
#if FASTFLOAT_HAS_BIT_CAST
58+
bits = std::bit_cast<equiv_uint>(value);
59+
#else
5660
::memcpy(&bits, &value, sizeof(T));
61+
#endif
5762
if ((bits & exponent_mask) == 0) {
5863
// denormal
5964
am.power2 = 1 - bias;
@@ -72,7 +77,8 @@ fastfloat_really_inline adjusted_mantissa to_extended(T value) noexcept {
7277
// we are given a native float that represents b, so we need to adjust it
7378
// halfway between b and b+u.
7479
template <typename T>
75-
fastfloat_really_inline adjusted_mantissa to_extended_halfway(T value) noexcept {
80+
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
81+
adjusted_mantissa to_extended_halfway(T value) noexcept {
7682
adjusted_mantissa am = to_extended(value);
7783
am.mantissa <<= 1;
7884
am.mantissa += 1;
@@ -148,9 +154,10 @@ void round_down(adjusted_mantissa& am, int32_t shift) noexcept {
148154
am.power2 += shift;
149155
}
150156

151-
fastfloat_really_inline void skip_zeros(const char*& first, const char* last) noexcept {
157+
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
158+
void skip_zeros(const char*& first, const char* last) noexcept {
152159
uint64_t val;
153-
while (std::distance(first, last) >= 8) {
160+
while (!cpp20_and_in_constexpr() && std::distance(first, last) >= 8) {
154161
::memcpy(&val, first, sizeof(uint64_t));
155162
if (val != 0x3030303030303030) {
156163
break;
@@ -167,10 +174,11 @@ fastfloat_really_inline void skip_zeros(const char*& first, const char* last) no
167174

168175
// determine if any non-zero digits were truncated.
169176
// all characters must be valid digits.
170-
fastfloat_really_inline bool is_truncated(const char* first, const char* last) noexcept {
177+
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
178+
bool is_truncated(const char* first, const char* last) noexcept {
171179
// do 8-bit optimizations, can just compare to 8 literal 0s.
172180
uint64_t val;
173-
while (std::distance(first, last) >= 8) {
181+
while (!cpp20_and_in_constexpr() && std::distance(first, last) >= 8) {
174182
::memcpy(&val, first, sizeof(uint64_t));
175183
if (val != 0x3030303030303030) {
176184
return true;
@@ -186,11 +194,12 @@ fastfloat_really_inline bool is_truncated(const char* first, const char* last) n
186194
return false;
187195
}
188196

189-
fastfloat_really_inline bool is_truncated(byte_span s) noexcept {
197+
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
198+
bool is_truncated(byte_span s) noexcept {
190199
return is_truncated(s.ptr, s.ptr + s.len());
191200
}
192201

193-
fastfloat_really_inline
202+
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
194203
void parse_eight_digits(const char*& p, limb& value, size_t& counter, size_t& count) noexcept {
195204
value = value * 100000000 + parse_eight_digits_unrolled(p);
196205
p += 8;
@@ -206,21 +215,23 @@ void parse_one_digit(const char*& p, limb& value, size_t& counter, size_t& count
206215
count++;
207216
}
208217

209-
fastfloat_really_inline
218+
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
210219
void add_native(bigint& big, limb power, limb value) noexcept {
211220
big.mul(power);
212221
big.add(value);
213222
}
214223

215-
fastfloat_really_inline void round_up_bigint(bigint& big, size_t& count) noexcept {
224+
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
225+
void round_up_bigint(bigint& big, size_t& count) noexcept {
216226
// need to round-up the digits, but need to avoid rounding
217227
// ....9999 to ...10000, which could cause a false halfway point.
218228
add_native(big, 10, 1);
219229
count++;
220230
}
221231

222232
// parse the significant digits into a big integer
223-
inline void parse_mantissa(bigint& result, parsed_number_string& num, size_t max_digits, size_t& digits) noexcept {
233+
inline FASTFLOAT_CONSTEXPR20
234+
void parse_mantissa(bigint& result, parsed_number_string& num, size_t max_digits, size_t& digits) noexcept {
224235
// try to minimize the number of big integer and scalar multiplication.
225236
// therefore, try to parse 8 digits at a time, and multiply by the largest
226237
// scalar value (9 or 19 digits) for each step.
@@ -300,7 +311,8 @@ inline void parse_mantissa(bigint& result, parsed_number_string& num, size_t max
300311
}
301312

302313
template <typename T>
303-
inline adjusted_mantissa positive_digit_comp(bigint& bigmant, int32_t exponent) noexcept {
314+
inline FASTFLOAT_CONSTEXPR20
315+
adjusted_mantissa positive_digit_comp(bigint& bigmant, int32_t exponent) noexcept {
304316
FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent)));
305317
adjusted_mantissa answer;
306318
bool truncated;
@@ -323,7 +335,8 @@ inline adjusted_mantissa positive_digit_comp(bigint& bigmant, int32_t exponent)
323335
// we then need to scale by `2^(f- e)`, and then the two significant digits
324336
// are of the same magnitude.
325337
template <typename T>
326-
inline adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int32_t exponent) noexcept {
338+
inline FASTFLOAT_CONSTEXPR20
339+
adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int32_t exponent) noexcept {
327340
bigint& real_digits = bigmant;
328341
int32_t real_exp = exponent;
329342

@@ -383,7 +396,8 @@ inline adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa
383396
// the actual digits. we then compare the big integer representations
384397
// of both, and use that to direct rounding.
385398
template <typename T>
386-
inline adjusted_mantissa digit_comp(parsed_number_string& num, adjusted_mantissa am) noexcept {
399+
inline FASTFLOAT_CONSTEXPR20
400+
adjusted_mantissa digit_comp(parsed_number_string& num, adjusted_mantissa am) noexcept {
387401
// remove the invalid exponent bias
388402
am.power2 -= invalid_am_bias;
389403

include/fast_float/fast_float.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
#include <system_error>
55

6+
#include "constexpr_feature_detect.h"
7+
68
namespace fast_float {
79
enum chars_format {
810
scientific = 1<<0,
@@ -48,13 +50,15 @@ struct parse_options {
4850
* The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
4951
*/
5052
template<typename T>
53+
FASTFLOAT_CONSTEXPR20
5154
from_chars_result from_chars(const char *first, const char *last,
5255
T &value, chars_format fmt = chars_format::general) noexcept;
5356

5457
/**
5558
* Like from_chars, but accepts an `options` argument to govern number parsing.
5659
*/
5760
template<typename T>
61+
FASTFLOAT_CONSTEXPR20
5862
from_chars_result from_chars_advanced(const char *first, const char *last,
5963
T &value, parse_options options) noexcept;
6064

include/fast_float/float_common.h

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,8 @@
77
#include <cstring>
88
#include <type_traits>
99

10-
#ifdef __has_include
11-
#if __has_include(<version>)
12-
#include <version>
13-
#endif
14-
#endif
15-
16-
#if __cpp_lib_bit_cast >= 201806L
10+
#if FASTFLOAT_HAS_BIT_CAST
1711
#include <bit>
18-
#define FASTFLOAT_HAS_BIT_CAST 1
19-
#else
20-
#define FASTFLOAT_HAS_BIT_CAST 0
21-
#endif
22-
23-
#if __cpp_lib_is_constant_evaluated >= 201811L
24-
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1
25-
#else
26-
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0
2712
#endif
2813

2914
#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \
@@ -110,22 +95,6 @@
11095
// rust style `try!()` macro, or `?` operator
11196
#define FASTFLOAT_TRY(x) { if (!(x)) return false; }
11297

113-
// Testing for https://wg21.link/N3652, adopted in C++14
114-
#if __cpp_constexpr >= 201304
115-
#define FASTFLOAT_CONSTEXPR14 constexpr
116-
#else
117-
#define FASTFLOAT_CONSTEXPR14
118-
#endif
119-
120-
// Testing for relevant C++20 constexpr library features
121-
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED \
122-
&& FASTFLOAT_HAS_BIT_CAST \
123-
&& __cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/
124-
#define FASTFLOAT_CONSTEXPR20 constexpr
125-
#else
126-
#define FASTFLOAT_CONSTEXPR20
127-
#endif
128-
12998
namespace fast_float {
13099

131100
fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() {

0 commit comments

Comments
 (0)