Merge pull request #112 from fastfloat/dlemire/remove_cxx20

Removing CXX20 support
This commit is contained in:
Daniel Lemire 2021-09-20 09:54:00 -04:00 committed by GitHub
commit 6d768256f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 51 additions and 107 deletions

24
.github/workflows/vs16-cxx20.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: VS16-CI C++20
on: [push, pull_request]
jobs:
ci:
name: windows-vs16
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 16 2019, arch: Win32}
- {gen: Visual Studio 16 2019, arch: x64}
steps:
- name: checkout
uses: actions/checkout@v2
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake ${{matrix.cxx}} ${{matrix.arch}} -DCMAKE_CXX_STANDARD=20 -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . --verbose &&
ctest --output-on-failure

View File

@ -6,24 +6,15 @@
#include <cstring> #include <cstring>
#include <iterator> #include <iterator>
#if defined __has_include
#if __has_include(<version>)
#include <version>
#if defined(__cpp_lib_bit_cast)
#include <bit>
#endif
#endif
#endif
#include "float_common.h" #include "float_common.h"
namespace fast_float { 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.
CXX20_CONSTEXPR fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; } fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; }
CXX20_CONSTEXPR fastfloat_really_inline uint64_t byteswap(uint64_t val) { fastfloat_really_inline 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
@ -34,13 +25,9 @@ CXX20_CONSTEXPR fastfloat_really_inline uint64_t byteswap(uint64_t val) {
| (val & 0x00000000000000FF) << 56; | (val & 0x00000000000000FF) << 56;
} }
CXX20_CONSTEXPR fastfloat_really_inline uint64_t read_u64(const char *chars) { fastfloat_really_inline uint64_t read_u64(const char *chars) {
uint64_t val; uint64_t val;
#if defined(__cpp_lib_bit_cast)
val = std::bit_cast<uint64_t>(reinterpret_cast<const char (&)[8]>(chars));
#else
::memcpy(&val, chars, sizeof(uint64_t)); ::memcpy(&val, chars, sizeof(uint64_t));
#endif
#if FASTFLOAT_IS_BIG_ENDIAN == 1 #if FASTFLOAT_IS_BIG_ENDIAN == 1
// Need to read as-if the number was in little-endian order. // Need to read as-if the number was in little-endian order.
val = byteswap(val); val = byteswap(val);
@ -48,26 +35,16 @@ CXX20_CONSTEXPR fastfloat_really_inline uint64_t read_u64(const char *chars) {
return val; return val;
} }
CXX20_CONSTEXPR fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) { fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) {
#if FASTFLOAT_IS_BIG_ENDIAN == 1 #if FASTFLOAT_IS_BIG_ENDIAN == 1
// Need to read as-if the number was in little-endian order. // Need to read as-if the number was in little-endian order.
val = byteswap(val); val = byteswap(val);
#endif #endif
#if defined(__cpp_lib_bit_cast)
if (std::is_constant_evaluated()) {
char (&dst)[8] = reinterpret_cast<char (&)[8]>(chars);
const char (&src)[8] = reinterpret_cast<const char (&)[8]>(val);
std::copy(std::begin(src), std::end(src), std::begin(dst));
} else {
::memcpy(chars, &val, sizeof(uint64_t));
}
#else
::memcpy(chars, &val, sizeof(uint64_t)); ::memcpy(chars, &val, sizeof(uint64_t));
#endif
} }
// credit @aqrit // credit @aqrit
CXX20_CONSTEXPR fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) { fastfloat_really_inline 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)
@ -77,17 +54,17 @@ CXX20_CONSTEXPR fastfloat_really_inline uint32_t parse_eight_digits_unrolled(ui
return uint32_t(val); return uint32_t(val);
} }
CXX20_CONSTEXPR fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept { fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept {
return parse_eight_digits_unrolled(read_u64(chars)); return parse_eight_digits_unrolled(read_u64(chars));
} }
// credit @aqrit // credit @aqrit
CXX20_CONSTEXPR fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept { fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
0x8080808080808080)); 0x8080808080808080));
} }
CXX20_CONSTEXPR fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept { fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept {
return is_made_of_eight_digits_fast(read_u64(chars)); return is_made_of_eight_digits_fast(read_u64(chars));
} }
@ -107,7 +84,7 @@ struct parsed_number_string {
// Assuming that you use no more than 19 digits, this will // Assuming that you use no more than 19 digits, this will
// parse an ASCII string. // parse an ASCII string.
CXX20_CONSTEXPR fastfloat_really_inline fastfloat_really_inline
parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept { parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept {
const chars_format fmt = options.format; const chars_format fmt = options.format;
const char decimal_point = options.decimal_point; const char decimal_point = options.decimal_point;

View File

@ -17,7 +17,7 @@ namespace fast_float {
// low part corresponding to the least significant bits. // low part corresponding to the least significant bits.
// //
template <int bit_precision> template <int bit_precision>
CXX20_CONSTEXPR fastfloat_really_inline fastfloat_really_inline
value128 compute_product_approximation(int64_t q, uint64_t w) { value128 compute_product_approximation(int64_t q, uint64_t w) {
const int index = 2 * int(q - powers::smallest_power_of_five); const int index = 2 * int(q - powers::smallest_power_of_five);
// For small values of q, e.g., q in [0,27], the answer is always exact because // For small values of q, e.g., q in [0,27], the answer is always exact because
@ -90,7 +90,7 @@ adjusted_mantissa compute_error(int64_t q, uint64_t w) noexcept {
// return an adjusted_mantissa with a negative power of 2: the caller should recompute // return an adjusted_mantissa with a negative power of 2: the caller should recompute
// in such cases. // in such cases.
template <typename binary> template <typename binary>
CXX20_CONSTEXPR fastfloat_really_inline fastfloat_really_inline
adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept { adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
adjusted_mantissa answer; adjusted_mantissa answer;
if ((w == 0) || (q < binary::smallest_power_of_ten())) { if ((w == 0) || (q < binary::smallest_power_of_ten())) {

View File

@ -1,23 +1,8 @@
#ifndef FASTFLOAT_FAST_FLOAT_H #ifndef FASTFLOAT_FAST_FLOAT_H
#define FASTFLOAT_FAST_FLOAT_H #define FASTFLOAT_FAST_FLOAT_H
#include <system_error> #include <system_error>
#if !defined(CXX20_CONSTEXPR)
#define CXX20_CONSTEXPR
#if defined __has_include
#if __has_include(<version>)
#include <version>
#if defined(__cpp_lib_bit_cast)
#undef CXX20_CONSTEXPR
#define CXX20_CONSTEXPR constexpr
#define HAS_CXX20_CONSTEXPR 1
#endif
#endif
#endif
#endif
namespace fast_float { namespace fast_float {
enum chars_format { enum chars_format {
scientific = 1<<0, scientific = 1<<0,
@ -63,14 +48,14 @@ struct parse_options {
* The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`. * The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
*/ */
template<typename T> template<typename T>
CXX20_CONSTEXPR from_chars_result from_chars(const char *first, const char *last, from_chars_result from_chars(const char *first, const char *last,
T &value, chars_format fmt = chars_format::general) noexcept; T &value, chars_format fmt = chars_format::general) noexcept;
/** /**
* Like from_chars, but accepts an `options` argument to govern number parsing. * Like from_chars, but accepts an `options` argument to govern number parsing.
*/ */
template<typename T> template<typename T>
CXX20_CONSTEXPR from_chars_result from_chars_advanced(const char *first, const char *last, from_chars_result from_chars_advanced(const char *first, const char *last,
T &value, parse_options options) noexcept; T &value, parse_options options) noexcept;
} }

View File

@ -74,20 +74,6 @@
#define fastfloat_really_inline inline __attribute__((always_inline)) #define fastfloat_really_inline inline __attribute__((always_inline))
#endif #endif
#if !defined(CXX20_CONSTEXPR)
#define CXX20_CONSTEXPR
#if defined __has_include
#if __has_include(<version>)
#include <version>
#if defined(__cpp_lib_bit_cast)
#undef CXX20_CONSTEXPR
#define CXX20_CONSTEXPR constexpr
#define HAS_CXX20_CONSTEXPR 1
#endif
#endif
#endif
#endif
#ifndef FASTFLOAT_ASSERT #ifndef FASTFLOAT_ASSERT
#define FASTFLOAT_ASSERT(x) { if (!(x)) abort(); } #define FASTFLOAT_ASSERT(x) { if (!(x)) abort(); }
#endif #endif
@ -103,7 +89,7 @@
namespace fast_float { namespace fast_float {
// Compares two ASCII strings in a case insensitive manner. // Compares two ASCII strings in a case insensitive manner.
CXX20_CONSTEXPR inline bool fastfloat_strncasecmp(const char *input1, const char *input2, inline bool fastfloat_strncasecmp(const char *input1, const char *input2,
size_t length) { 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++) {
@ -142,7 +128,7 @@ struct value128 {
}; };
/* result might be undefined when input_num is zero */ /* result might be undefined when input_num is zero */
CXX20_CONSTEXPR fastfloat_really_inline int leading_zeroes(uint64_t input_num) { fastfloat_really_inline int leading_zeroes(uint64_t input_num) {
assert(input_num > 0); assert(input_num > 0);
#ifdef FASTFLOAT_VISUAL_STUDIO #ifdef FASTFLOAT_VISUAL_STUDIO
#if defined(_M_X64) || defined(_M_ARM64) #if defined(_M_X64) || defined(_M_ARM64)
@ -169,13 +155,13 @@ CXX20_CONSTEXPR 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
CXX20_CONSTEXPR fastfloat_really_inline uint64_t emulu(uint32_t x, uint32_t y) { fastfloat_really_inline 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__)
CXX20_CONSTEXPR fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd, fastfloat_really_inline 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);
@ -354,7 +340,6 @@ template <> inline constexpr size_t binary_format<float>::max_digits() {
} }
template<typename T> template<typename T>
CXX20_CONSTEXPR
fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) { fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) {
uint64_t word = am.mantissa; uint64_t word = am.mantissa;
word |= uint64_t(am.power2) << binary_format<T>::mantissa_explicit_bits(); word |= uint64_t(am.power2) << binary_format<T>::mantissa_explicit_bits();

View File

@ -20,7 +20,7 @@ namespace detail {
* strings a null-free and fixed. * strings a null-free and fixed.
**/ **/
template <typename T> template <typename T>
CXX20_CONSTEXPR from_chars_result parse_infnan(const char *first, const char *last, T &value) noexcept { from_chars_result parse_infnan(const char *first, const char *last, T &value) noexcept {
from_chars_result answer; from_chars_result answer;
answer.ptr = first; answer.ptr = first;
answer.ec = std::errc(); // be optimistic answer.ec = std::errc(); // be optimistic
@ -63,13 +63,13 @@ CXX20_CONSTEXPR from_chars_result parse_infnan(const char *first, const char *la
} // namespace detail } // namespace detail
template<typename T> template<typename T>
CXX20_CONSTEXPR from_chars_result from_chars(const char *first, const char *last, from_chars_result from_chars(const char *first, const char *last,
T &value, chars_format fmt /*= chars_format::general*/) noexcept { T &value, chars_format fmt /*= chars_format::general*/) noexcept {
return from_chars_advanced(first, last, value, parse_options{fmt}); return from_chars_advanced(first, last, value, parse_options{fmt});
} }
template<typename T> template<typename T>
CXX20_CONSTEXPR from_chars_result from_chars_advanced(const char *first, const char *last, from_chars_result from_chars_advanced(const char *first, const char *last,
T &value, parse_options options) noexcept { T &value, parse_options options) noexcept {
static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported"); static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported");

View File

@ -22,7 +22,7 @@ namespace fast_float {
namespace detail { namespace detail {
// remove all final zeroes // remove all final zeroes
CXX20_CONSTEXPR inline void trim(decimal &h) { inline void trim(decimal &h) {
while ((h.num_digits > 0) && (h.digits[h.num_digits - 1] == 0)) { while ((h.num_digits > 0) && (h.digits[h.num_digits - 1] == 0)) {
h.num_digits--; h.num_digits--;
} }
@ -30,7 +30,7 @@ CXX20_CONSTEXPR inline void trim(decimal &h) {
CXX20_CONSTEXPR inline uint32_t number_of_digits_decimal_left_shift(const decimal &h, uint32_t shift) { inline uint32_t number_of_digits_decimal_left_shift(const decimal &h, uint32_t shift) {
shift &= 63; shift &= 63;
constexpr uint16_t number_of_digits_decimal_left_shift_table[65] = { constexpr uint16_t number_of_digits_decimal_left_shift_table[65] = {
0x0000, 0x0800, 0x0801, 0x0803, 0x1006, 0x1009, 0x100D, 0x1812, 0x1817, 0x0000, 0x0800, 0x0801, 0x0803, 0x1006, 0x1009, 0x100D, 0x1812, 0x1817,
@ -123,7 +123,7 @@ CXX20_CONSTEXPR inline uint32_t number_of_digits_decimal_left_shift(const decima
return num_new_digits; return num_new_digits;
} }
CXX20_CONSTEXPR inline uint64_t round(decimal &h) { inline uint64_t round(decimal &h) {
if ((h.num_digits == 0) || (h.decimal_point < 0)) { if ((h.num_digits == 0) || (h.decimal_point < 0)) {
return 0; return 0;
} else if (h.decimal_point > 18) { } else if (h.decimal_point > 18) {
@ -150,7 +150,7 @@ CXX20_CONSTEXPR inline uint64_t round(decimal &h) {
} }
// computes h * 2^-shift // computes h * 2^-shift
CXX20_CONSTEXPR inline void decimal_left_shift(decimal &h, uint32_t shift) { inline void decimal_left_shift(decimal &h, uint32_t shift) {
if (h.num_digits == 0) { if (h.num_digits == 0) {
return; return;
} }
@ -192,7 +192,7 @@ CXX20_CONSTEXPR inline void decimal_left_shift(decimal &h, uint32_t shift) {
} }
// computes h * 2^shift // computes h * 2^shift
CXX20_CONSTEXPR inline void decimal_right_shift(decimal &h, uint32_t shift) { inline void decimal_right_shift(decimal &h, uint32_t shift) {
uint32_t read_index = 0; uint32_t read_index = 0;
uint32_t write_index = 0; uint32_t write_index = 0;
@ -241,7 +241,7 @@ CXX20_CONSTEXPR inline void decimal_right_shift(decimal &h, uint32_t shift) {
} // namespace detail } // namespace detail
template <typename binary> template <typename binary>
CXX20_CONSTEXPR adjusted_mantissa compute_float(decimal &d) { adjusted_mantissa compute_float(decimal &d) {
adjusted_mantissa answer; adjusted_mantissa answer;
if (d.num_digits == 0) { if (d.num_digits == 0) {
// should be zero // should be zero
@ -351,7 +351,7 @@ CXX20_CONSTEXPR adjusted_mantissa compute_float(decimal &d) {
} }
template <typename binary> template <typename binary>
CXX20_CONSTEXPR adjusted_mantissa parse_long_mantissa(const char *first, const char* last, parse_options options) { adjusted_mantissa parse_long_mantissa(const char *first, const char* last, parse_options options) {
decimal d = parse_decimal(first, last, options); decimal d = parse_decimal(first, last, options);
return compute_float<binary>(d); return compute_float<binary>(d);
} }

View File

@ -42,11 +42,6 @@
#define FASTFLOAT_ODDPLATFORM 1 #define FASTFLOAT_ODDPLATFORM 1
#endif #endif
#if HAS_CXX20_CONSTEXPR
#include <bit>
#include <string_view>
#endif
// C++ 17 because it is otherwise annoying to browse all files in a directory. // C++ 17 because it is otherwise annoying to browse all files in a directory.
// We also only run these tests on little endian systems. // We also only run these tests on little endian systems.
#if (FASTFLOAT_CPLUSPLUS >= 201703L) && (FASTFLOAT_IS_BIG_ENDIAN == 0) && !defined(FASTFLOAT_ODDPLATFORM) #if (FASTFLOAT_CPLUSPLUS >= 201703L) && (FASTFLOAT_IS_BIG_ENDIAN == 0) && !defined(FASTFLOAT_ODDPLATFORM)
@ -120,28 +115,6 @@ TEST_CASE("supplemental") {
} }
#endif #endif
#if HAS_CXX20_CONSTEXPR
constexpr double tryParse(std::string_view input)
{
double result{};
fast_float::from_chars_result parseResult = fast_float::from_chars(input.data(), input.data() + input.size(), result);
if (parseResult.ec != std::errc()) {
return std::numeric_limits<double>::quiet_NaN();
}
return result;
}
static_assert(tryParse("1.0") == 1.0);
static_assert(tryParse("2.0") == 2.0);
static_assert(tryParse("3.14156") == 3.14156);
static_assert(tryParse("3.14156") != 3.1415600000001);
#if !defined(_MSVC_LANG)
static_assert(std::isnan(tryParse("hellothere"))); // technically isnan is not constexpr but GCC and clang allow it
#endif
#endif //#if HAS_CXX20_CONSTEXPR
TEST_CASE("leading_zeroes") { TEST_CASE("leading_zeroes") {
constexpr const uint64_t bit = 1; constexpr const uint64_t bit = 1;
CHECK(fast_float::leading_zeroes(bit << 0) == 63); CHECK(fast_float::leading_zeroes(bit << 0) == 63);