Skip to content

Commit 8c4405e

Browse files
authored
Merge pull request #101 from fastfloat/dlemire/const
C++ 20 support and tests
2 parents 898f54f + f74d505 commit 8c4405e

10 files changed

+137
-39
lines changed

.github/workflows/ubuntu20-cxx20.yml

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: Ubuntu 20.04 CI (C++20)
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
ubuntu-build:
7+
runs-on: ubuntu-20.04
8+
strategy:
9+
fail-fast: false
10+
steps:
11+
- uses: actions/checkout@v2
12+
- name: Use cmake
13+
run: |
14+
mkdir build &&
15+
cd build &&
16+
cmake -DCMAKE_CXX_STANDARD=20 -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
17+
cmake --build . &&
18+
ctest --output-on-failure &&
19+
cmake --install .

.github/workflows/ubuntu20.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Ubuntu 20.04 CI (GCC 9, 8)
1+
name: Ubuntu 20.04 CI (GCC 9)
22

33
on: [push, pull_request]
44

@@ -26,4 +26,4 @@ jobs:
2626
cd ../tests/installation_tests/find &&
2727
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build . &&
2828
cd ../../issue72_installation &&
29-
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .
29+
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.9)
22

33
project(fast_float VERSION 2.0.0 LANGUAGES CXX)
44
option(FASTFLOAT_TEST "Enable tests" OFF)
5-
set(CMAKE_CXX_STANDARD 11)
5+
set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard to be used")
66
set(CMAKE_CXX_STANDARD_REQUIRED ON)
77
if(FASTFLOAT_TEST)
88
enable_testing()

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");

0 commit comments

Comments
 (0)