Constexpr proof of concept

This commit is contained in:
Lenard Szolnoki 2023-02-12 22:54:36 +00:00
parent 3fd4c1b507
commit 51ec158148
8 changed files with 95 additions and 27 deletions

View File

@ -12,9 +12,11 @@ namespace fast_float {
// Next function can be micro-optimized, but compilers are entirely
// able to optimize it well.
fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; }
fastfloat_really_inline constexpr bool is_integer(char c) noexcept {
return c >= '0' && c <= '9';
}
fastfloat_really_inline uint64_t byteswap(uint64_t val) {
fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
return (val & 0xFF00000000000000) >> 56
| (val & 0x00FF000000000000) >> 40
| (val & 0x0000FF0000000000) >> 24
@ -84,8 +86,9 @@ struct parsed_number_string {
// Assuming that you use no more than 19 digits, this will
// parse an ASCII string.
fastfloat_really_inline
parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept {
fastfloat_really_inline constexpr parsed_number_string
parse_number_string(const char *p, const char *pend,
parse_options options) noexcept {
const chars_format fmt = options.format;
const char decimal_point = options.decimal_point;

View File

@ -17,8 +17,8 @@ namespace fast_float {
// low part corresponding to the least significant bits.
//
template <int bit_precision>
fastfloat_really_inline
value128 compute_product_approximation(int64_t q, uint64_t w) {
fastfloat_really_inline constexpr value128
compute_product_approximation(int64_t q, uint64_t w) {
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
// The line value128 firstproduct = full_multiplication(w, power_of_five_128[index]);
@ -90,8 +90,8 @@ 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
// in such cases.
template <typename binary>
fastfloat_really_inline
adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
fastfloat_really_inline constexpr adjusted_mantissa
compute_float(int64_t q, uint64_t w) noexcept {
adjusted_mantissa answer;
if ((w == 0) || (q < binary::smallest_power_of_ten())) {
answer.power2 = 0;

View File

@ -48,15 +48,17 @@ struct parse_options {
* The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
*/
template<typename T>
from_chars_result from_chars(const char *first, const char *last,
T &value, chars_format fmt = chars_format::general) noexcept;
constexpr from_chars_result
from_chars(const char *first, const char *last, T &value,
chars_format fmt = chars_format::general) noexcept;
/**
* Like from_chars, but accepts an `options` argument to govern number parsing.
*/
template<typename T>
from_chars_result from_chars_advanced(const char *first, const char *last,
T &value, parse_options options) noexcept;
constexpr from_chars_result from_chars_advanced(const char *first,
const char *last, T &value,
parse_options options) noexcept;
} // namespace fast_float
#include "parse_number.h"

View File

@ -40,7 +40,7 @@ static const uint64_t power_of_five_128[number_of_entries];
};
template <class unused>
const uint64_t powers_template<unused>::power_of_five_128[number_of_entries] = {
constexpr uint64_t powers_template<unused>::power_of_five_128[number_of_entries] = {
0xeef453d6923bd65a,0x113faa2906a13b3f,
0x9558b4661b6565f8,0x4ac7ca59a424c507,
0xbaaee17fa23ebf76,0x5d79bcf00d2df649,

View File

@ -6,6 +6,11 @@
#include <cassert>
#include <cstring>
#include <type_traits>
#include <version>
#ifdef __cpp_lib_bit_cast
# include <bit>
#endif
#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \
|| defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \
@ -112,14 +117,14 @@ template <typename T>
struct span {
const T* ptr;
size_t length;
span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {}
span() : ptr(nullptr), length(0) {}
constexpr span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {}
constexpr span() : ptr(nullptr), length(0) {}
constexpr size_t len() const noexcept {
return length;
}
const T& operator[](size_t index) const noexcept {
constexpr const T& operator[](size_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length);
return ptr[index];
}
@ -128,12 +133,12 @@ struct span {
struct value128 {
uint64_t low;
uint64_t high;
value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
value128() : low(0), high(0) {}
constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
constexpr value128() : low(0), high(0) {}
};
/* result might be undefined when input_num is zero */
fastfloat_really_inline int leading_zeroes(uint64_t input_num) {
fastfloat_really_inline constexpr int leading_zeroes(uint64_t input_num) {
assert(input_num > 0);
#ifdef FASTFLOAT_VISUAL_STUDIO
#if defined(_M_X64) || defined(_M_ARM64)
@ -183,8 +188,8 @@ fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd,
// compute 64-bit a*b
fastfloat_really_inline value128 full_multiplication(uint64_t a,
uint64_t b) {
fastfloat_really_inline constexpr value128 full_multiplication(uint64_t a,
uint64_t b) {
value128 answer;
#if defined(_M_ARM64) && !defined(__MINGW32__)
// ARM64 has native support for 64-bit multiplications, no need to emulate
@ -435,7 +440,8 @@ template <> inline constexpr binary_format<double>::equiv_uint
}
template<typename T>
fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) {
fastfloat_really_inline constexpr void
to_float(bool negative, adjusted_mantissa am, T &value) {
uint64_t word = am.mantissa;
word |= uint64_t(am.power2) << binary_format<T>::mantissa_explicit_bits();
word = negative
@ -448,8 +454,17 @@ fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &va
}
#else
// For little-endian systems:
#ifdef __cpp_lib_bit_cast
if constexpr (std::is_same_v<T, float>) {
uint32_t word32 = word;
value = std::bit_cast<float>(word32);
} else { // double
value = std::bit_cast<double>(word);
}
#else
::memcpy(&value, &word, sizeof(T));
#endif
#endif
}
#if FASTFLOAT_SKIP_WHITE_SPACE // disabled by default

View File

@ -9,6 +9,7 @@
#include <cstring>
#include <limits>
#include <system_error>
#include <type_traits>
namespace fast_float {
@ -127,14 +128,16 @@ fastfloat_really_inline bool rounds_to_nearest() noexcept {
} // namespace detail
template<typename T>
from_chars_result from_chars(const char *first, const char *last,
T &value, chars_format fmt /*= chars_format::general*/) noexcept {
constexpr from_chars_result
from_chars(const char *first, const char *last, T &value,
chars_format fmt /*= chars_format::general*/) noexcept {
return from_chars_advanced(first, last, value, parse_options{fmt});
}
template<typename T>
from_chars_result from_chars_advanced(const char *first, const char *last,
T &value, parse_options options) noexcept {
constexpr from_chars_result
from_chars_advanced(const char *first, const char *last, 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");
@ -169,7 +172,11 @@ from_chars_result from_chars_advanced(const char *first, const char *last,
// We could check it first (before the previous branch), but
// there might be performance advantages at having the check
// be last.
if(detail::rounds_to_nearest()) {
if (
#if __cpp_lib_is_constant_evaluated
std::is_constant_evaluated() ||
#endif
detail::rounds_to_nearest()) {
// We have that fegetround() == FE_TONEAREST.
// Next is Clinger's fast path.
if (pns.mantissa <=binary_format<T>::max_mantissa_fast_path()) {

View File

@ -65,6 +65,8 @@ target_compile_features(basictest PRIVATE cxx_std_17)
fast_float_add_cpp_test(long_test)
fast_float_add_cpp_test(powersoffive_hardround)
fast_float_add_cpp_test(string_test)
fast_float_add_cpp_test(constexpr)
target_compile_features(constexpr PRIVATE cxx_std_20)

39
tests/constexpr.cpp Normal file
View File

@ -0,0 +1,39 @@
#include "fast_float/fast_float.h"
constexpr double zero = [] {
double ret = 0;
const char *str = "0";
fast_float::from_chars(str, str + 1, ret);
return ret;
}();
static_assert(zero == 0.);
constexpr double pi = [] {
double ret = 0;
const char *str = "3.1415";
fast_float::from_chars(str, str + 6, ret);
return ret;
}();
static_assert(pi == 3.1415);
constexpr double googol = [] {
double ret = 0;
const char *str = "1e100";
fast_float::from_chars(str, str + 5, ret);
return ret;
}();
static_assert(googol == 1e100);
int main() { return 0; }