From b60f5869d4a36c2a5a52f750ace7295cc0b6f307 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Tue, 10 Nov 2020 12:08:27 -0500 Subject: [PATCH 01/47] Revised the script. --- script/table_generation.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/script/table_generation.py b/script/table_generation.py index 92bf9a9..0dfada7 100644 --- a/script/table_generation.py +++ b/script/table_generation.py @@ -1,11 +1,4 @@ - def format(number): - # move the most significant bit in position - while(number < (1<<127)): - number *= 2 - # then *truncate* - while(number >= (1<<128)): - number //= 2 upper = number // (1<<64) lower = number % (1<<64) print(""+hex(upper)+","+hex(lower)+",") @@ -15,16 +8,24 @@ for q in range(-342,0): z = 0 while( (1<= -17 ): + if(q >= -17): b = z + 127 c = 2 ** b // power5 + 1 - assert c < (1<<128) format(c) else: b = 2 * z + 2 * 64 c = 2 ** b // power5 + 1 + # truncate + while(c >= (1<<128)): + c //= 2 format(c) for q in range(0,308+1): power5 = 5 ** q + # move the most significant bit in position + while(power5 < (1<<127)): + power5 *= 2 + # *truncate* + while(power5 >= (1<<128)): + power5 //= 2 format(power5) From 689fba95b5b45cc39f3e77986a6de96884489366 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Tue, 10 Nov 2020 20:38:23 -0500 Subject: [PATCH 02/47] Minor simplification. --- include/fast_float/decimal_to_binary.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/fast_float/decimal_to_binary.h b/include/fast_float/decimal_to_binary.h index 3ac4737..655d37d 100644 --- a/include/fast_float/decimal_to_binary.h +++ b/include/fast_float/decimal_to_binary.h @@ -107,9 +107,8 @@ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept { uint64_t upperbit = product.high >> 63; answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3); - lz += int(1 ^ upperbit); - - answer.power2 = power(int(q)) - lz - binary::minimum_exponent() + 1; + + answer.power2 = power(int(q)) + upperbit - lz - binary::minimum_exponent(); if (answer.power2 <= 0) { // we have a subnormal? // Here have that answer.power2 <= 0 so -answer.power2 >= 0 if(-answer.power2 + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure. From 9c5dac3705ce3ae83b7f23aed00fa3dc9a1634a2 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Wed, 11 Nov 2020 20:17:18 -0500 Subject: [PATCH 03/47] Build with pedantic flags under GNU GCC. --- include/fast_float/decimal_to_binary.h | 2 +- tests/CMakeLists.txt | 4 ++++ tests/basictest.cpp | 11 +++++------ tests/exhaustive32.cpp | 2 +- tests/exhaustive32_midpoint.cpp | 4 ++-- tests/random_string.cpp | 12 ++++++------ tests/short_random_string.cpp | 12 ++++++------ 7 files changed, 25 insertions(+), 22 deletions(-) diff --git a/include/fast_float/decimal_to_binary.h b/include/fast_float/decimal_to_binary.h index 655d37d..23cf169 100644 --- a/include/fast_float/decimal_to_binary.h +++ b/include/fast_float/decimal_to_binary.h @@ -108,7 +108,7 @@ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept { answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3); - answer.power2 = power(int(q)) + upperbit - lz - binary::minimum_exponent(); + answer.power2 = int(power(int(q)) + upperbit - lz - binary::minimum_exponent()); if (answer.power2 <= 0) { // we have a subnormal? // Here have that answer.power2 <= 0 so -answer.power2 >= 0 if(-answer.power2 + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure. diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c653145..db02991 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -2,6 +2,10 @@ function(fast_float_add_cpp_test TEST_NAME) add_executable(${TEST_NAME} ${TEST_NAME}.cpp) add_test(${TEST_NAME} ${TEST_NAME}) + if(NOT WIN32) + target_compile_options(${TEST_NAME} PUBLIC -Werror -Wall -Wextra -Weffc++) + target_compile_options(${TEST_NAME} PUBLIC -Wsign-compare -Wshadow -Wwrite-strings -Wpointer-arith -Winit-self -Wconversion -Wno-sign-conversion) + endif() target_link_libraries(${TEST_NAME} PUBLIC fast_float) endfunction(fast_float_add_cpp_test) fast_float_add_cpp_test(short_random_string) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index 2acf5bc..d01bd95 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -192,7 +192,6 @@ bool test_scientific_only() { return false; } input = "3.14e10"; - result; answer = fast_float::from_chars(input.data(), input.data()+input.size(), result, fast_float::chars_format::scientific); if(answer.ec != std::errc()) { std::cerr << "It is scientific!\n"; @@ -450,7 +449,7 @@ int main() { Assert(basic_test_32bit("0.0015924838953651488",0x1.a175cap-10f)); Assert(basic_test_32bit("0.0008602388261351734",0.0008602388261351734f)); Assert(basic_test_32bit("0.00036393293703440577",0x1.7d9c82p-12f)); - Assert(basic_test_32bit("0.00013746770127909258",0.00013746770127909258)); + Assert(basic_test_32bit("0.00013746770127909258",0.00013746770127909258f)); Assert(basic_test_32bit("16407.9462890625", 16407.9462890625f)); Assert(basic_test_32bit("1.1754947011469036e-38", 0x1.000006p-126f)); Assert(basic_test_32bit("7.0064923216240854e-46", 0x1p-149f)); @@ -458,7 +457,7 @@ int main() { Assert(basic_test_32bit("0e9999999999999999999999999999", 0)); Assert(basic_test_32bit("1234456789012345678901234567890e9999999999999999999999999999", std::numeric_limits::infinity())); Assert(basic_test_32bit("4.7019774032891500318749461488889827112746622270883500860350068251e-38",4.7019774032891500318749461488889827112746622270883500860350068251e-38f)); - Assert(basic_test_32bit("3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679", 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679)); + Assert(basic_test_32bit("3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679", 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679f)); Assert(basic_test_32bit("2.3509887016445750159374730744444913556373311135441750430175034126e-38", 2.3509887016445750159374730744444913556373311135441750430175034126e-38f)); Assert(basic_test_32bit("+1", 1)); Assert(basic_test_32bit("2e3000", std::numeric_limits::infinity())); @@ -472,9 +471,9 @@ int main() { Assert(basic_test_32bit("3.4028234665e38", 0x1.fffffep+127f)); Assert(basic_test_32bit("3.4028234666e38", 0x1.fffffep+127f)); Assert(basic_test_32bit("0.000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625", 0.000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625)); - Assert(basic_test_32bit("0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125", 0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125)); - Assert(basic_test_32bit("0.00000000000000000000000000000000000002350988561514728583455765982071533026645717985517980855365926236850006129930346077117064851336181163787841796875", 0.00000000000000000000000000000000000002350988561514728583455765982071533026645717985517980855365926236850006129930346077117064851336181163787841796875)); - Assert(basic_test_32bit("0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875", 0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875)); + Assert(basic_test_32bit("0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125", 0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125f)); + Assert(basic_test_32bit("0.00000000000000000000000000000000000002350988561514728583455765982071533026645717985517980855365926236850006129930346077117064851336181163787841796875", 0.00000000000000000000000000000000000002350988561514728583455765982071533026645717985517980855365926236850006129930346077117064851336181163787841796875f)); + Assert(basic_test_32bit("0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875", 0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875f)); std::cout << std::endl; Assert(powers_of_ten()); diff --git a/tests/exhaustive32.cpp b/tests/exhaustive32.cpp index 5116e07..dd354d9 100644 --- a/tests/exhaustive32.cpp +++ b/tests/exhaustive32.cpp @@ -19,7 +19,7 @@ void allvalues() { std::cout << "."; std::cout.flush(); } - uint32_t word = w; + uint32_t word = uint32_t(w); memcpy(&v, &word, sizeof(v)); { diff --git a/tests/exhaustive32_midpoint.cpp b/tests/exhaustive32_midpoint.cpp index 9e5b356..7c2080f 100644 --- a/tests/exhaustive32_midpoint.cpp +++ b/tests/exhaustive32_midpoint.cpp @@ -53,7 +53,7 @@ void allvalues() { std::cout << "."; std::cout.flush(); } - uint32_t word = w; + uint32_t word = uint32_t(w); memcpy(&v, &word, sizeof(v)); if(std::isfinite(v)) { float nextf = std::nextafterf(v, INFINITY); @@ -64,7 +64,7 @@ void allvalues() { double v2{nextf}; assert(float(v2) == nextf); double midv{v1 + (v2 - v1) / 2}; - float expected_midv(midv); + float expected_midv = float(midv); const char *string_end = to_string(midv, buffer); float str_answer; diff --git a/tests/random_string.cpp b/tests/random_string.cpp index 01c1f10..9f8732b 100644 --- a/tests/random_string.cpp +++ b/tests/random_string.cpp @@ -24,7 +24,7 @@ double cygwin_strtod_l(const char* start, char** end) { class RandomEngine { public: RandomEngine() = delete; - RandomEngine(int new_seed) { wyhash64_x_ = new_seed; }; + RandomEngine(int new_seed) : wyhash64_x_(new_seed) {}; uint64_t next() { // Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h // Inspired from @@ -47,7 +47,7 @@ public: /* if (min == max) { return min; }*/ - int s = max - min + 1; + uint64_t s = uint64_t(max - min + 1); uint64_t x = next(); fast_float::value128 m = fast_float::full_multiplication(x, s); uint64_t l = m.low; @@ -59,7 +59,7 @@ public: l = m.low; } } - return (m.high) + min; + return int(m.high + min); } int next_digit() { return next_ranged_int(0, 9); } @@ -78,8 +78,8 @@ size_t build_random_string(RandomEngine &rand, char *buffer) { number_of_digits = rand.next_ranged_int(1, 2000); } int location_of_decimal_separator = rand.next_ranged_int(1, number_of_digits); - for (size_t i = 0; i < number_of_digits; i++) { - if (i == location_of_decimal_separator) { + for (size_t i = 0; i < size_t(number_of_digits); i++) { + if (i == size_t(location_of_decimal_separator)) { buffer[pos++] = '.'; } buffer[pos++] = char(rand.next_digit() + '0'); @@ -98,7 +98,7 @@ size_t build_random_string(RandomEngine &rand, char *buffer) { } } number_of_digits = rand.next_ranged_int(1, 3); - for (size_t i = 0; i < number_of_digits; i++) { + for (size_t i = 0; i < size_t(number_of_digits); i++) { buffer[pos++] = char(rand.next_digit() + '0'); } } diff --git a/tests/short_random_string.cpp b/tests/short_random_string.cpp index 1ced98f..42389bc 100644 --- a/tests/short_random_string.cpp +++ b/tests/short_random_string.cpp @@ -24,7 +24,7 @@ double cygwin_strtod_l(const char* start, char** end) { class RandomEngine { public: RandomEngine() = delete; - RandomEngine(int new_seed) { wyhash64_x_ = new_seed; }; + RandomEngine(int new_seed) : wyhash64_x_(new_seed) { }; uint64_t next() { // Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h // Inspired from @@ -47,7 +47,7 @@ public: /* if (min == max) { return min; }*/ - int s = max - min + 1; + uint64_t s = uint64_t(max - min + 1); uint64_t x = next(); fast_float::value128 m = fast_float::full_multiplication(x, s); uint64_t l = m.low; @@ -59,7 +59,7 @@ public: l = m.low; } } - return (m.high) + min; + return int(m.high + min); } int next_digit() { return next_ranged_int(0, 9); } @@ -74,8 +74,8 @@ size_t build_random_string(RandomEngine &rand, char *buffer) { } int number_of_digits = rand.next_ranged_int(1, 19); int location_of_decimal_separator = rand.next_ranged_int(1, number_of_digits); - for (size_t i = 0; i < number_of_digits; i++) { - if (i == location_of_decimal_separator) { + for (size_t i = 0; i < size_t(number_of_digits); i++) { + if (i == size_t(location_of_decimal_separator)) { buffer[pos++] = '.'; } buffer[pos++] = char(rand.next_digit() + '0'); @@ -94,7 +94,7 @@ size_t build_random_string(RandomEngine &rand, char *buffer) { } } number_of_digits = rand.next_ranged_int(1, 3); - for (size_t i = 0; i < number_of_digits; i++) { + for (size_t i = 0; i < size_t(number_of_digits); i++) { buffer[pos++] = char(rand.next_digit() + '0'); } } From 1e92d5999790d58be255d4051c41471c2fd036b9 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Wed, 11 Nov 2020 20:43:36 -0500 Subject: [PATCH 04/47] Sign conversion pedantry. --- include/fast_float/ascii_number.h | 4 ++-- include/fast_float/decimal_to_binary.h | 4 ++-- include/fast_float/float_common.h | 2 +- include/fast_float/parse_number.h | 2 +- tests/CMakeLists.txt | 2 +- tests/basictest.cpp | 8 ++++---- tests/random_string.cpp | 6 +++--- tests/short_random_string.cpp | 6 +++--- tests/string_test.cpp | 2 +- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index d571fcb..689c908 100644 --- a/include/fast_float/ascii_number.h +++ b/include/fast_float/ascii_number.h @@ -71,7 +71,7 @@ parsed_number_string parse_number_string(const char *p, const char *pend, chars_ // a multiplication by 10 is cheaper than an arbitrary integer // multiplication i = 10 * i + - (*p - '0'); // might overflow, we will handle the overflow later + uint64_t(*p - '0'); // might overflow, we will handle the overflow later ++p; } int64_t exponent = 0; @@ -236,7 +236,7 @@ fastfloat_really_inline decimal parse_decimal(const char *p, const char *pend) n } answer.decimal_point += (neg_exp ? -exp_number : exp_number); } - answer.decimal_point += answer.num_digits; + answer.decimal_point += int32_t(answer.num_digits); if(answer.num_digits > max_digits) { answer.truncated = true; answer.num_digits = max_digits; diff --git a/include/fast_float/decimal_to_binary.h b/include/fast_float/decimal_to_binary.h index 23cf169..68ab079 100644 --- a/include/fast_float/decimal_to_binary.h +++ b/include/fast_float/decimal_to_binary.h @@ -104,7 +104,7 @@ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept { // value128 product = compute_product(q, w); // but in practice, we can win big with the compute_product_approximation if its additional branch // is easily predicted. Which is best is data specific. - uint64_t upperbit = product.high >> 63; + int upperbit = int(product.high >> 63); answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3); @@ -143,7 +143,7 @@ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept { // answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3); // ... we dropped out only zeroes. But if this happened, then we can go back!!! if((answer.mantissa << (upperbit + 64 - binary::mantissa_explicit_bits() - 3)) == product.high) { - answer.mantissa &= ~1; // flip it so that we do not round up + answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up } } diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 27a3c72..09917ad 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -175,7 +175,7 @@ struct decimal { } // Generate san exponent matching to_truncated_mantissa() inline int32_t to_truncated_exponent() { - return decimal_point - max_digit_without_overflow; + return decimal_point - int32_t(max_digit_without_overflow); } }; diff --git a/include/fast_float/parse_number.h b/include/fast_float/parse_number.h index 3e246d9..4652cc8 100644 --- a/include/fast_float/parse_number.h +++ b/include/fast_float/parse_number.h @@ -77,7 +77,7 @@ from_chars_result from_chars(const char *first, const char *last, from_chars_result answer; - while ((first != last) && fast_float::is_space(*first)) { + while ((first != last) && fast_float::is_space(uint8_t(*first))) { first++; } if (first == last) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index db02991..0b32b52 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,7 +4,7 @@ function(fast_float_add_cpp_test TEST_NAME) add_test(${TEST_NAME} ${TEST_NAME}) if(NOT WIN32) target_compile_options(${TEST_NAME} PUBLIC -Werror -Wall -Wextra -Weffc++) - target_compile_options(${TEST_NAME} PUBLIC -Wsign-compare -Wshadow -Wwrite-strings -Wpointer-arith -Winit-self -Wconversion -Wno-sign-conversion) + target_compile_options(${TEST_NAME} PUBLIC -Wsign-compare -Wshadow -Wwrite-strings -Wpointer-arith -Winit-self -Wconversion -Wsign-conversion) endif() target_link_libraries(${TEST_NAME} PUBLIC fast_float) endfunction(fast_float_add_cpp_test) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index d01bd95..64dadd4 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -10,7 +10,7 @@ template std::string to_string(T d) { std::string s(64, '\0'); auto written = std::snprintf(&s[0], s.size(), "%.*e", std::numeric_limits::max_digits10 - 1, d); - s.resize(written); + s.resize(size_t(written)); return s; } @@ -18,7 +18,7 @@ template std::string to_long_string(T d) { std::string s(4096, '\0'); auto written = std::snprintf(&s[0], s.size(), "%.*e", std::numeric_limits::max_digits10 * 10, d); - s.resize(written); + s.resize(size_t(written)); return s; } @@ -134,7 +134,7 @@ bool issue8() { auto answer = fast_float::from_chars(s, s + strlen(s) - i, d); if(answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return false; } if(d != 0x1.921fb54442d18p+1) { - printf("%.*s\n", int(strlen(s) - i), s); + printf("%.*s\n", int(strlen(s) - size_t(i)), s); std::cout << std::hexfloat << d << std::endl; std::cout << std::defaultfloat << d << std::endl; return false; @@ -308,7 +308,7 @@ bool test_fixed_only() { for (int i = start_point; i <= 308; ++i) {// large negative values should be zero. std::cout << "."; std::cout.flush(); - size_t n = snprintf(buf, sizeof(buf), "1e%d", i); + size_t n = size_t(snprintf(buf, sizeof(buf), "1e%d", i)); if (n >= sizeof(buf)) { abort(); } double actual; auto result = fast_float::from_chars(buf, buf + 1000, actual); diff --git a/tests/random_string.cpp b/tests/random_string.cpp index 9f8732b..8663691 100644 --- a/tests/random_string.cpp +++ b/tests/random_string.cpp @@ -24,7 +24,7 @@ double cygwin_strtod_l(const char* start, char** end) { class RandomEngine { public: RandomEngine() = delete; - RandomEngine(int new_seed) : wyhash64_x_(new_seed) {}; + RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed) {}; uint64_t next() { // Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h // Inspired from @@ -59,7 +59,7 @@ public: l = m.low; } } - return int(m.high + min); + return int(m.high) + min; } int next_digit() { return next_ranged_int(0, 9); } @@ -146,7 +146,7 @@ std::pair strtof_from_string(char *st) { * We generate random strings and we try to parse them with both strtod/strtof, * and we verify that we get the same answer with with fast_float::from_chars. */ -bool tester(int seed, size_t volume) { +bool tester(uint64_t seed, size_t volume) { char buffer[4096]; // large buffer (can't overflow) RandomEngine rand(seed); for (size_t i = 0; i < volume; i++) { diff --git a/tests/short_random_string.cpp b/tests/short_random_string.cpp index 42389bc..50503cc 100644 --- a/tests/short_random_string.cpp +++ b/tests/short_random_string.cpp @@ -24,7 +24,7 @@ double cygwin_strtod_l(const char* start, char** end) { class RandomEngine { public: RandomEngine() = delete; - RandomEngine(int new_seed) : wyhash64_x_(new_seed) { }; + RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed) { }; uint64_t next() { // Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h // Inspired from @@ -59,7 +59,7 @@ public: l = m.low; } } - return int(m.high + min); + return int(m.high) + min; } int next_digit() { return next_ranged_int(0, 9); } @@ -142,7 +142,7 @@ std::pair strtof_from_string(char *st) { * We generate random strings and we try to parse them with both strtod/strtof, * and we verify that we get the same answer with with fast_float::from_chars. */ -bool tester(int seed, size_t volume) { +bool tester(uint64_t seed, size_t volume) { char buffer[4096]; // large buffer (can't overflow) RandomEngine rand(seed); for (size_t i = 0; i < volume; i++) { diff --git a/tests/string_test.cpp b/tests/string_test.cpp index e2d6b96..7da2f7b 100644 --- a/tests/string_test.cpp +++ b/tests/string_test.cpp @@ -30,7 +30,7 @@ template std::string to_string(T d) { std::string s(64, '\0'); auto written = std::snprintf(&s[0], s.size(), "%.*e", std::numeric_limits::max_digits10 - 1, d); - s.resize(written); + s.resize(size_t(written)); return s; } From 4e3153379176647c85d6fd040a128d13c4e15e0a Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Wed, 11 Nov 2020 22:21:49 -0500 Subject: [PATCH 05/47] Added an extra test (which passes). --- tests/basictest.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index 64dadd4..8acb9ca 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -334,6 +334,7 @@ int main() { Assert(issue19()); Assert(check_behavior()); std::cout << "======= 64 bits " << std::endl; + Assert(basic_test_64bit("1.1920928955078125e-07",1.1920928955078125e-07)); Assert(basic_test_64bit("INF",std::numeric_limits::infinity())); Assert(basic_test_64bit("-INF",-std::numeric_limits::infinity())); Assert(basic_test_64bit("INFINITY",std::numeric_limits::infinity())); From 1b775cd9a7e6e30810abf92dfcfdd42180cce69e Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Wed, 11 Nov 2020 22:50:21 -0500 Subject: [PATCH 06/47] Minor code cleaning. --- tests/exhaustive32_64.cpp | 56 ++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/tests/exhaustive32_64.cpp b/tests/exhaustive32_64.cpp index 95cf6ca..c0d6184 100644 --- a/tests/exhaustive32_64.cpp +++ b/tests/exhaustive32_64.cpp @@ -11,6 +11,37 @@ template char *to_string(T d, char *buffer) { return buffer + written; } + +bool basic_test_64bit(std::string vals, double val) { + double result_value; + auto result = fast_float::from_chars(vals.data(), vals.data() + vals.size(), + result_value); + if (result.ec != std::errc()) { + std::cerr << " I could not parse " << vals << std::endl; + return false; + } + if(copysign(1,result_value) != copysign(1,val)) { + std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << val + << std::endl; + return false; + } else if (std::isnan(val)) { + if (!std::isnan(result_value)) { + std::cerr << vals << std::endl; + std::cerr << "not nan" << result_value << std::endl; + return false; + } + } else if (result_value != val) { + std::cerr << vals << std::endl; + std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << val + << std::endl; + std::cerr << std::dec; + std::cerr << "string: " << vals << std::endl; + return false; + } + return true; +} + + void all_32bit_values() { char buffer[64]; for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) { @@ -25,28 +56,9 @@ void all_32bit_values() { { const char *string_end = to_string(v, buffer); - double result_value; - auto result = fast_float::from_chars(buffer, string_end, result_value); - if (result.ec != std::errc()) { - std::cerr << "parsing error ? " << buffer << std::endl; - abort(); - } - if(copysign(1,result_value) != copysign(1,v)) { - std::cerr << buffer << std::endl; - std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v - << std::endl; - abort(); - } else if (std::isnan(v)) { - if (!std::isnan(result_value)) { - std::cerr << "not nan" << buffer << std::endl; - abort(); - } - } else if (result_value != v) { - std::cerr << "no match ? " << buffer << std::endl; - std::cout << "started with " << std::hexfloat << v << std::endl; - std::cout << "got back " << std::hexfloat << result_value << std::endl; - std::cout << std::dec; - abort(); + std::string s(buffer, size_t(string_end-buffer)); + if(!basic_test_64bit(s,v)) { + return; } } } From e79741ede251f45a243760686003f82d9a0f5cc4 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Thu, 12 Nov 2020 22:35:32 -0500 Subject: [PATCH 07/47] Minor cleaning. --- include/fast_float/ascii_number.h | 4 ++++ include/fast_float/float_common.h | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index 689c908..bd072c6 100644 --- a/include/fast_float/ascii_number.h +++ b/include/fast_float/ascii_number.h @@ -241,6 +241,10 @@ fastfloat_really_inline decimal parse_decimal(const char *p, const char *pend) n answer.truncated = true; answer.num_digits = max_digits; } + // In very rare cases, we may have fewer than 19 digits, we want to be able to reliably + // assume that all digits up to max_digit_without_overflow have been initialized. + for(uint32_t i = answer.num_digits; i < max_digit_without_overflow; i++) { answer.digits[i] = 0; } + return answer; } } // namespace fast_float diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 09917ad..d44b06d 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -149,9 +149,10 @@ struct decimal { // Moves are allowed: decimal(decimal &&) = default; decimal &operator=(decimal &&other) = default; - // Generates a mantissa by truncating to 19 digits; this function assumes - // that num_digits >= 19 (the caller is responsible for the check). + // Generates a mantissa by truncating to 19 digits. // This function should be reasonably fast. + // Note that the user is responsible to ensure that digits are + // initialized to zero when there are fewer than 19. inline uint64_t to_truncated_mantissa() { uint64_t val; // 8 first digits From e5917323ec65578d5cae66e1541aac4fe638273f Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Sun, 15 Nov 2020 14:47:43 -0500 Subject: [PATCH 08/47] Pedantic member initialization. --- include/fast_float/float_common.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index d44b06d..5c2f303 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -126,8 +126,8 @@ fastfloat_really_inline value128 full_multiplication(uint64_t value1, #endif struct adjusted_mantissa { - uint64_t mantissa; - int power2; // a negative value indicate an invalid result + uint64_t mantissa{0}; + int power2{0}; // a negative value indicate an invalid result adjusted_mantissa() = default; // bool operator==(const adjusted_mantissa &o) const = default; bool operator==(const adjusted_mantissa &o) const { From 8a0a0c4fc1afeabd65d55302e900aac2594e3bc5 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Sun, 15 Nov 2020 14:51:54 -0500 Subject: [PATCH 09/47] Being pedantic. --- include/fast_float/float_common.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 5c2f303..7879654 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -136,10 +136,10 @@ struct adjusted_mantissa { }; struct decimal { - uint32_t num_digits; - int32_t decimal_point; - bool negative; - bool truncated; + uint32_t num_digits{0}; + int32_t decimal_point{0}; + bool negative{false}; + bool truncated{false}; uint8_t digits[max_digits]; decimal() = default; // Copies are not allowed since this is a fat object. From 3eb7bce1e39bb960ed7ea55c2c068906e8787291 Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Sun, 15 Nov 2020 19:19:31 +0000 Subject: [PATCH 10/47] re #34: reproduce in the CI --- .github/workflows/msys2.yml | 15 ++++++++------- .github/workflows/ubuntu18.yml | 26 +++++++++++++++++++------- .github/workflows/ubuntu20.yml | 25 ++++++++++++++++++------- 3 files changed, 45 insertions(+), 21 deletions(-) diff --git a/.github/workflows/msys2.yml b/.github/workflows/msys2.yml index b6177f4..665c539 100644 --- a/.github/workflows/msys2.yml +++ b/.github/workflows/msys2.yml @@ -35,10 +35,11 @@ jobs: update: true msystem: ${{ matrix.msystem }} install: ${{ matrix.install }} - - name: Build and Test - run: | - mkdir build - cd build - cmake -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DFASTFLOAT_TEST=ON .. - cmake --build . --verbose - ctest --output-on-failure -R basictest \ No newline at end of file + - name: Prepare build dir + run: mkdir build + - name: Configure + run: cd build && cmake -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DFASTFLOAT_TEST=ON .. + - name: Build + run: cmake --build build + - name: Run basic tests + run: cd build && ctest --output-on-failure -R basictest diff --git a/.github/workflows/ubuntu18.yml b/.github/workflows/ubuntu18.yml index e1614ef..23850cc 100644 --- a/.github/workflows/ubuntu18.yml +++ b/.github/workflows/ubuntu18.yml @@ -1,20 +1,32 @@ -name: Ubuntu 18.04 CI (GCC 7) +name: Ubuntu 18.04 CI (GCC 7, 6, 5) on: [push, pull_request] jobs: ubuntu-build: runs-on: ubuntu-18.04 + strategy: + fail-fast: false + matrix: + include: + - {cxx: -DCMAKE_CXX_COMPILER=g++-5} + - {cxx: -DCMAKE_CXX_COMPILER=g++-6} + - {cxx: } # default compiler 7 steps: - uses: actions/checkout@v2 - name: Setup cmake uses: jwlawson/actions-setup-cmake@v1.0 with: cmake-version: '3.9.x' - - name: Use cmake + - name: Install older compilers run: | - mkdir build && - cd build && - cmake -DFASTFLOAT_TEST=ON .. && - cmake --build . && - ctest --output-on-failure -R basictest + sudo -E apt-get update + sudo -E apt-get install -y --force-yes g++-5 g++-6 + - name: Prepare build dir + run: mkdir build + - name: Configure + run: cd build && cmake ${{matrix.cxx}} -DFASTFLOAT_TEST=ON .. + - name: Build + run: cmake --build build + - name: Run basic tests + run: cd build && ctest --output-on-failure -R basictest diff --git a/.github/workflows/ubuntu20.yml b/.github/workflows/ubuntu20.yml index b267a5b..68aa1ee 100644 --- a/.github/workflows/ubuntu20.yml +++ b/.github/workflows/ubuntu20.yml @@ -1,20 +1,31 @@ -name: Ubuntu 20.04 CI (GCC 9) +name: Ubuntu 20.04 CI (GCC 9, 8) on: [push, pull_request] jobs: ubuntu-build: runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + include: + - {cxx: -DCMAKE_CXX_COMPILER=g++-8} + - {cxx: } # default compiler 9 steps: - uses: actions/checkout@v2 - name: Setup cmake uses: jwlawson/actions-setup-cmake@v1.0 with: cmake-version: '3.9.x' - - name: Use cmake + - name: install older compilers run: | - mkdir build && - cd build && - cmake -DFASTFLOAT_TEST=ON .. && - cmake --build . && - ctest --output-on-failure -R basictest + sudo -E apt-get update + sudo -E apt-get install -y --force-yes g++-8 g++-7 + - name: Prepare build dir + run: mkdir build + - name: Configure + run: cd build && cmake ${{matrix.cxx}} -DFASTFLOAT_TEST=ON .. + - name: Build + run: cmake --build build + - name: Run basic tests + run: cd build && ctest --output-on-failure -R basictest From 7ff364b59a9900a21f26c2609b2037277117b5eb Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Mon, 16 Nov 2020 12:04:57 -0500 Subject: [PATCH 11/47] This might add support for big endian systems (untested). --- include/fast_float/ascii_number.h | 5 +++++ include/fast_float/float_common.h | 36 +++++++++++++++++++++++++++++++ include/fast_float/parse_number.h | 11 +++++++++- 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index bd072c6..d39ad52 100644 --- a/include/fast_float/ascii_number.h +++ b/include/fast_float/ascii_number.h @@ -78,6 +78,8 @@ parsed_number_string parse_number_string(const char *p, const char *pend, chars_ if ((p != pend) && (*p == '.')) { ++p; const char *first_after_period = p; +#if FASTFLOAT_IS_BIG_ENDIAN == 0 + // Fast approach only tested under little endian systems if ((p + 8 <= pend) && is_made_of_eight_digits_fast(p)) { i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok p += 8; @@ -86,6 +88,7 @@ parsed_number_string parse_number_string(const char *p, const char *pend, chars_ p += 8; } } +#endif while ((p != pend) && is_integer(*p)) { uint8_t digit = uint8_t(*p - '0'); ++p; @@ -196,6 +199,7 @@ fastfloat_really_inline decimal parse_decimal(const char *p, const char *pend) n ++p; } } +#if FASTFLOAT_IS_BIG_ENDIAN == 0 // We expect that this loop will often take the bulk of the running time // because when a value has lots of digits, these digits often while ((p + 8 <= pend) && (answer.num_digits + 8 < max_digits)) { @@ -208,6 +212,7 @@ fastfloat_really_inline decimal parse_decimal(const char *p, const char *pend) n answer.num_digits += 8; p += 8; } +#endif while ((p != pend) && is_integer(*p)) { if (answer.num_digits < max_digits) { answer.digits[answer.num_digits] = uint8_t(*p - '0'); diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 7879654..a208741 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -8,6 +8,34 @@ #define FASTFLOAT_VISUAL_STUDIO 1 #endif + + +#ifdef _WIN32 +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#else +#if defined(__APPLE__) || defined(__FreeBSD__) +#include +#else +#include +#endif +# +#ifndef __BYTE_ORDER__ +// safe choice +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#endif +# +#ifndef __ORDER_LITTLE_ENDIAN__ +// safe choice +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#endif +# +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#else +#define FASTFLOAT_IS_BIG_ENDIAN 1 +#endif +#endif + #ifdef FASTFLOAT_VISUAL_STUDIO #define fastfloat_really_inline __forceinline #else @@ -154,6 +182,13 @@ struct decimal { // Note that the user is responsible to ensure that digits are // initialized to zero when there are fewer than 19. inline uint64_t to_truncated_mantissa() { +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + for (uint32_t i = 0; i < max_digit_without_overflow; + i++) { + mantissa = mantissa * 10 + digits[i]; // can be accelerated + } + return mantissa; +#else uint64_t val; // 8 first digits ::memcpy(&val, digits, sizeof(uint64_t)); @@ -173,6 +208,7 @@ struct decimal { mantissa = mantissa * 10 + digits[i]; // can be accelerated } return mantissa; +#endif } // Generate san exponent matching to_truncated_mantissa() inline int32_t to_truncated_exponent() { diff --git a/include/fast_float/parse_number.h b/include/fast_float/parse_number.h index 4652cc8..1b7a419 100644 --- a/include/fast_float/parse_number.h +++ b/include/fast_float/parse_number.h @@ -107,7 +107,16 @@ from_chars_result from_chars(const char *first, const char *last, word |= uint64_t(am.power2) << binary_format::mantissa_explicit_bits(); word = pns.negative ? word | (uint64_t(1) << binary_format::sign_index()) : word; - ::memcpy(&value, &word, sizeof(T)); +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + if (std::is_same::value) { + ::memcpy(&value, (char *)&word + 4, sizeof(T)); // extract value at offset 4-7 if float on big-endian + } else { + ::memcpy(&value, &word, sizeof(T)); + } +#else + // For little-endian systems: + ::memcpy(&value, &word, sizeof(T)); +#endif return answer; } From 1d9116b4f241eec3286b02143305f6719b8658d8 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Mon, 16 Nov 2020 12:17:20 -0500 Subject: [PATCH 12/47] Adding travis. --- .travis.yml | 188 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..29746eb --- /dev/null +++ b/.travis.yml @@ -0,0 +1,188 @@ +language: cpp + +dist: bionic + +arch: + - amd64 + - ppc64le + - s390x + +cache: + directories: + - $HOME/.dep_cache + +env: + global: + - simdjson_DEPENDENCY_CACHE_DIR=$HOME/.dep_cache + +matrix: + include: + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-8 + env: + - COMPILER="CC=gcc-8 && CXX=g++-8" + compiler: gcc-8 + + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-9 + env: + - COMPILER="CC=gcc-9 && CXX=g++-9" + compiler: gcc-9 + + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-10 + env: + - COMPILER="CC=gcc-10 && CXX=g++-10" + compiler: gcc-10 + + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-10 + env: + - COMPILER="CC=gcc-10 && CXX=g++-10" + - SANITIZE="on" + compiler: gcc-10-sanitize + + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-10 + env: + - COMPILER="CC=gcc-10 && CXX=g++-10" + - STATIC="on" + compiler: gcc-10-static + + - os: linux + addons: + apt: + sources: + - llvm-toolchain-bionic-6.0 + packages: + - clang-6.0 + env: + - COMPILER="CC=clang-6.0 && CXX=clang++-6.0" + compiler: clang-6 + + - os: linux + addons: + apt: + sources: + - llvm-toolchain-bionic-7 + packages: + - clang-7 + env: + - COMPILER="CC=clang-7 && CXX=clang++-7" + compiler: clang-7 + + - os: linux + addons: + apt: + sources: + - llvm-toolchain-bionic-8 + packages: + - clang-8 + env: + - COMPILER="CC=clang-8 && CXX=clang++-8" + compiler: clang-8 + + - os: linux + addons: + apt: + sources: + - llvm-toolchain-bionic-9 + packages: + - clang-9 + env: + - COMPILER="CC=clang-9 && CXX=clang++-9" + compiler: clang-9 + + - os: linux + addons: + apt: + packages: + - clang-10 + sources: + - ubuntu-toolchain-r-test + - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + env: + - COMPILER="CC=clang-10 && CXX=clang++-10" + compiler: clang-10 + + - os: linux + addons: + apt: + packages: + - clang-10 + sources: + - ubuntu-toolchain-r-test + - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + env: + - COMPILER="CC=clang-10 && CXX=clang++-10" + - STATIC="on" + compiler: clang-10-static + + - os: linux + addons: + apt: + packages: + - clang-10 + sources: + - ubuntu-toolchain-r-test + - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + env: + - COMPILER="CC=clang-10 && CXX=clang++-10" + - SANITIZE="on" + compiler: clang-10-sanitize + +before_install: + - eval "${COMPILER}" + +install: + - wget -q -O - "https://raw.githubusercontent.com/simdjson/debian-ppa/master/key.gpg" | sudo apt-key add - + - sudo apt-add-repository "deb https://raw.githubusercontent.com/simdjson/debian-ppa/master simdjson main" + - sudo apt-get -qq update + - sudo apt-get purge cmake cmake-data + - sudo apt-get -t simdjson -y install cmake + - export CMAKE_CXX_FLAGS="-maltivec -mcpu=power9 -mtune=power9" + - export CMAKE_C_FLAGS="${CMAKE_CXX_FLAGS}" + - export CMAKE_FLAGS="-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} -DSIMDJSON_IMPLEMENTATION=ppc64;fallback"; + - if [[ "${SANITIZE}" == "on" ]]; then + export CMAKE_FLAGS="${CMAKE_FLAGS} -DSIMDJSON_SANITIZE=ON"; + export ASAN_OPTIONS="detect_leaks=0"; + fi + - if [[ "${STATIC}" == "on" ]]; then + export CMAKE_FLAGS="${CMAKE_FLAGS} -DSIMDJSON_BUILD_STATIC=ON"; + fi + - export CTEST_FLAGS="-j4 --output-on-failure -E checkperf" + +script: + - cmake -DFASTFLOAT_TEST=ON -B build + - cmake --build build + - cd build + - ctest --output-on-failure -R basictest + From 59d5313432e7c6c6dccfa9e16e3353621f29b933 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Mon, 16 Nov 2020 12:36:15 -0500 Subject: [PATCH 13/47] Trimming. --- .travis.yml | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 29746eb..067abe0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ cache: env: global: - - simdjson_DEPENDENCY_CACHE_DIR=$HOME/.dep_cache + - fastfloat_DEPENDENCY_CACHE_DIR=$HOME/.dep_cache matrix: include: @@ -163,22 +163,7 @@ before_install: - eval "${COMPILER}" install: - - wget -q -O - "https://raw.githubusercontent.com/simdjson/debian-ppa/master/key.gpg" | sudo apt-key add - - - sudo apt-add-repository "deb https://raw.githubusercontent.com/simdjson/debian-ppa/master simdjson main" - sudo apt-get -qq update - - sudo apt-get purge cmake cmake-data - - sudo apt-get -t simdjson -y install cmake - - export CMAKE_CXX_FLAGS="-maltivec -mcpu=power9 -mtune=power9" - - export CMAKE_C_FLAGS="${CMAKE_CXX_FLAGS}" - - export CMAKE_FLAGS="-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} -DSIMDJSON_IMPLEMENTATION=ppc64;fallback"; - - if [[ "${SANITIZE}" == "on" ]]; then - export CMAKE_FLAGS="${CMAKE_FLAGS} -DSIMDJSON_SANITIZE=ON"; - export ASAN_OPTIONS="detect_leaks=0"; - fi - - if [[ "${STATIC}" == "on" ]]; then - export CMAKE_FLAGS="${CMAKE_FLAGS} -DSIMDJSON_BUILD_STATIC=ON"; - fi - - export CTEST_FLAGS="-j4 --output-on-failure -E checkperf" script: - cmake -DFASTFLOAT_TEST=ON -B build From 7ebbb9d5be9a2a3c14f310b7d6b30f9e8f7a2f1e Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Mon, 16 Nov 2020 12:40:05 -0500 Subject: [PATCH 14/47] Going old school. --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 067abe0..616ab64 100644 --- a/.travis.yml +++ b/.travis.yml @@ -166,8 +166,9 @@ install: - sudo apt-get -qq update script: - - cmake -DFASTFLOAT_TEST=ON -B build - - cmake --build build + - mkdir build - cd build + - cmake -DFASTFLOAT_TEST=ON .. + - make - ctest --output-on-failure -R basictest From 1b5e3f3945a5e2839252675d79728a5325023e19 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Mon, 16 Nov 2020 12:56:57 -0500 Subject: [PATCH 15/47] patching be support. (typo) --- include/fast_float/float_common.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index a208741..60e5f6e 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -183,6 +183,7 @@ struct decimal { // initialized to zero when there are fewer than 19. inline uint64_t to_truncated_mantissa() { #if FASTFLOAT_IS_BIG_ENDIAN == 1 + uint64_t mantissa = 0; for (uint32_t i = 0; i < max_digit_without_overflow; i++) { mantissa = mantissa * 10 + digits[i]; // can be accelerated From 1afba556e344360c403ea060b041fa27032fa4b4 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Tue, 17 Nov 2020 21:55:01 -0500 Subject: [PATCH 16/47] Extending the fast path. --- include/fast_float/decimal_to_binary.h | 43 +++++++++++- tests/CMakeLists.txt | 1 + tests/powersoffive_hardround.cpp | 94 ++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 tests/powersoffive_hardround.cpp diff --git a/include/fast_float/decimal_to_binary.h b/include/fast_float/decimal_to_binary.h index 68ab079..0b2741e 100644 --- a/include/fast_float/decimal_to_binary.h +++ b/include/fast_float/decimal_to_binary.h @@ -60,8 +60,40 @@ namespace { fastfloat_really_inline int power(int q) noexcept { return (((152170 + 65536) * q) >> 16) + 63; } + // Checks whether w is divisible by 5**-q. If it returns true, then + // w is definitively divisible by 5**-q. + inline bool is_divisible(int64_t q, uint64_t w) noexcept { + if((q>=-18) || (q<-27)) { return false; } + int64_t pos_q = -q; + // For each pair, first entry is the multiplicative inverse of 5**-q + // and the second one is the largest quotient. + // + // This could be more efficient by using... + // Faster remainder by direct computation: Applications to compilers and software libraries + // Software: Practice and Experience 49 (6), 2019. + // but the following is simple enough. + constexpr static uint64_t table[10][2] = { + {0xc1773b91fac10669,0x49c977}, // inverse of 5**18 + {0x26b172506559ce15,0xec1e4}, // inverse of 5**19 + {0xd489e3a9addec2d1,0x2f394}, // inverse of 5**20 + {0x90e860bb892c8d5d,0x971d}, // inverse of 5**21 + {0x502e79bf1b6f4f79,0x1e39}, // inverse of 5**22 + {0xdcd618596be30fe5,0x60b}, // inverse of 5**23 + {0x2c2ad1ab7bfa3661,0x135}, // inverse of 5**24 + {0x8d55d224bfed7ad,0x3d}, // inverse of 5**25 + {0x1c445d3a8cc9189,0xc}, // inverse of 5**26 + {0xcd27412a54f5b6b5,0x2}, // inverse of 5**27 + }; + uint64_t inverse = table[pos_q-18][0]; + uint64_t threshold = table[pos_q-18][1]; + uint64_t product = w * inverse; + if(product > threshold) { return false; } + return true; + } } // namespace + + // w * 10 ** q // The returned value should be a valid ieee64 number that simply need to be packed. // However, in some very rare cases, the computation will fail. In such cases, we @@ -93,12 +125,19 @@ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept { // 1. We need the implicit bit // 2. We need an extra bit for rounding purposes // 3. We might lose a bit due to the "upperbit" routine (result too small, requiring a shift) + value128 product = compute_product_approximation(q, w); if(product.low == 0xFFFFFFFFFFFFFFFF) { // could guard it further // In some very rare cases, this could happen, in which case we might need a more accurate // computation that what we can provide cheaply. This is very, very unlikely. - answer.power2 = -1; // This (a negative value) indicates an error condition. - return answer; + // + // There is still a chance to recover. If w is divisible by 5**-q, + if(!is_divisible(q,w)) { + answer.power2 = -1; // This (a negative value) indicates an error condition. + return answer; + } + product.low += 1; + product.high += 1; } // The "compute_product_approximation" function can be slightly slower than a branchless approach: // value128 product = compute_product(q, w); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0b32b52..74cfbad 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,6 +8,7 @@ function(fast_float_add_cpp_test TEST_NAME) endif() target_link_libraries(${TEST_NAME} PUBLIC fast_float) endfunction(fast_float_add_cpp_test) +fast_float_add_cpp_test(powersoffive_hardround) fast_float_add_cpp_test(short_random_string) fast_float_add_cpp_test(exhaustive32_midpoint) fast_float_add_cpp_test(random_string) diff --git a/tests/powersoffive_hardround.cpp b/tests/powersoffive_hardround.cpp new file mode 100644 index 0000000..eea82bf --- /dev/null +++ b/tests/powersoffive_hardround.cpp @@ -0,0 +1,94 @@ +#include "fast_float/fast_float.h" + +#include +#include +#include +#include + +std::pair strtod_from_string(const char *st) { + double d; + char *pr; +#ifdef _WIN32 + static _locale_t c_locale = _create_locale(LC_ALL, "C"); + d = _strtod_l(st, &pr, c_locale); +#else + static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); + d = strtod_l(st, &pr, c_locale); +#endif + if (st == pr) { + std::cerr << "strtod_l could not parse '" << st << std::endl; + return std::make_pair(0, false); + } + return std::make_pair(d, true); +} + +std::pair strtof_from_string(char *st) { + float d; + char *pr; +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) + d = cygwin_strtod_l(st, &pr); +#elif defined(_WIN32) + static _locale_t c_locale = _create_locale(LC_ALL, "C"); + d = _strtof_l(st, &pr, c_locale); +#else + static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); + d = strtof_l(st, &pr, c_locale); +#endif + if (st == pr) { + std::cerr << "strtof_l could not parse '" << st << std::endl; + return std::make_pair(0.0f, false); + } + return std::make_pair(d, true); +} + +bool tester() { + std::random_device rd; + std::mt19937 gen(rd()); + for (int q = 18; q <= 27; q++) { + std::cout << "q = " << -q << std::endl; + uint64_t power5 = 1; + for (int k = 0; k < q; k++) { + power5 *= 5; + } + uint64_t low_threshold = 0x20000000000000 / power5 + 1; + uint64_t threshold = 0xFFFFFFFFFFFFFFFF / power5; + std::uniform_int_distribution dis(low_threshold, threshold); + for (size_t i = 0; i < 10000; i++) { + uint64_t mantissa = dis(gen) * power5; + std::stringstream ss; + ss << mantissa; + ss << "e"; + ss << -q; + std::string to_be_parsed = ss.str(); + std::pair expected_double = + strtod_from_string(to_be_parsed.c_str()); + double result_value; + auto result = + fast_float::from_chars(to_be_parsed.data(), to_be_parsed.data() + to_be_parsed.size(), result_value); + if (result.ec != std::errc()) { + std::cout << to_be_parsed << std::endl; + std::cerr << " I could not parse " << std::endl; + return false; + } + if (result_value != expected_double.first) { + std::cout << to_be_parsed << std::endl; + std::cerr << std::hexfloat << result_value << std::endl; + std::cerr << std::hexfloat << expected_double.first << std::endl; + std::cerr << " Mismatch " << std::endl; + return false; + } + } + } + return true; +} + +int main() { + if (tester()) { + std::cout << std::endl; + std::cout << "all ok" << std::endl; + return EXIT_SUCCESS; + } + std::cerr << std::endl; + std::cerr << "errors were encountered" << std::endl; + return EXIT_FAILURE; +} From d521ddf7f7fca0b0004396397cfb7a6c9b78b2ad Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Thu, 19 Nov 2020 18:15:42 -0500 Subject: [PATCH 17/47] Let us adjust the powers instead. --- include/fast_float/decimal_to_binary.h | 39 ++------------------------ include/fast_float/fast_table.h | 20 ++++++------- script/table_generation.py | 2 +- 3 files changed, 13 insertions(+), 48 deletions(-) diff --git a/include/fast_float/decimal_to_binary.h b/include/fast_float/decimal_to_binary.h index 0b2741e..d5d1311 100644 --- a/include/fast_float/decimal_to_binary.h +++ b/include/fast_float/decimal_to_binary.h @@ -60,36 +60,6 @@ namespace { fastfloat_really_inline int power(int q) noexcept { return (((152170 + 65536) * q) >> 16) + 63; } - // Checks whether w is divisible by 5**-q. If it returns true, then - // w is definitively divisible by 5**-q. - inline bool is_divisible(int64_t q, uint64_t w) noexcept { - if((q>=-18) || (q<-27)) { return false; } - int64_t pos_q = -q; - // For each pair, first entry is the multiplicative inverse of 5**-q - // and the second one is the largest quotient. - // - // This could be more efficient by using... - // Faster remainder by direct computation: Applications to compilers and software libraries - // Software: Practice and Experience 49 (6), 2019. - // but the following is simple enough. - constexpr static uint64_t table[10][2] = { - {0xc1773b91fac10669,0x49c977}, // inverse of 5**18 - {0x26b172506559ce15,0xec1e4}, // inverse of 5**19 - {0xd489e3a9addec2d1,0x2f394}, // inverse of 5**20 - {0x90e860bb892c8d5d,0x971d}, // inverse of 5**21 - {0x502e79bf1b6f4f79,0x1e39}, // inverse of 5**22 - {0xdcd618596be30fe5,0x60b}, // inverse of 5**23 - {0x2c2ad1ab7bfa3661,0x135}, // inverse of 5**24 - {0x8d55d224bfed7ad,0x3d}, // inverse of 5**25 - {0x1c445d3a8cc9189,0xc}, // inverse of 5**26 - {0xcd27412a54f5b6b5,0x2}, // inverse of 5**27 - }; - uint64_t inverse = table[pos_q-18][0]; - uint64_t threshold = table[pos_q-18][1]; - uint64_t product = w * inverse; - if(product > threshold) { return false; } - return true; - } } // namespace @@ -131,13 +101,8 @@ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept { // In some very rare cases, this could happen, in which case we might need a more accurate // computation that what we can provide cheaply. This is very, very unlikely. // - // There is still a chance to recover. If w is divisible by 5**-q, - if(!is_divisible(q,w)) { - answer.power2 = -1; // This (a negative value) indicates an error condition. - return answer; - } - product.low += 1; - product.high += 1; + answer.power2 = -1; // This (a negative value) indicates an error condition. + return answer; } // The "compute_product_approximation" function can be slightly slower than a branchless approach: // value128 product = compute_product(q, w); diff --git a/include/fast_float/fast_table.h b/include/fast_float/fast_table.h index f92d6f6..b68fcac 100644 --- a/include/fast_float/fast_table.h +++ b/include/fast_float/fast_table.h @@ -347,16 +347,16 @@ const uint64_t power_of_five_128[]= { 0xa2425ff75e14fc31,0xa1258379a94d028d, 0xcad2f7f5359a3b3e,0x96ee45813a04330, 0xfd87b5f28300ca0d,0x8bca9d6e188853fc, - 0x9e74d1b791e07e48,0x775ea264cf55347d, - 0xc612062576589dda,0x95364afe032a819d, - 0xf79687aed3eec551,0x3a83ddbd83f52204, - 0x9abe14cd44753b52,0xc4926a9672793542, - 0xc16d9a0095928a27,0x75b7053c0f178293, - 0xf1c90080baf72cb1,0x5324c68b12dd6338, - 0x971da05074da7bee,0xd3f6fc16ebca5e03, - 0xbce5086492111aea,0x88f4bb1ca6bcf584, - 0xec1e4a7db69561a5,0x2b31e9e3d06c32e5, - 0x9392ee8e921d5d07,0x3aff322e62439fcf, + 0x9e74d1b791e07e48,0x775ea264cf55347e, + 0xc612062576589dda,0x95364afe032a819e, + 0xf79687aed3eec551,0x3a83ddbd83f52205, + 0x9abe14cd44753b52,0xc4926a9672793543, + 0xc16d9a0095928a27,0x75b7053c0f178294, + 0xf1c90080baf72cb1,0x5324c68b12dd6339, + 0x971da05074da7bee,0xd3f6fc16ebca5e04, + 0xbce5086492111aea,0x88f4bb1ca6bcf585, + 0xec1e4a7db69561a5,0x2b31e9e3d06c32e6, + 0x9392ee8e921d5d07,0x3aff322e62439fd0, 0xb877aa3236a4b449,0x9befeb9fad487c3, 0xe69594bec44de15b,0x4c2ebe687989a9b4, 0x901d7cf73ab0acd9,0xf9d37014bf60a11, diff --git a/script/table_generation.py b/script/table_generation.py index 0dfada7..a85dc47 100644 --- a/script/table_generation.py +++ b/script/table_generation.py @@ -8,7 +8,7 @@ for q in range(-342,0): z = 0 while( (1<= -17): + if(q >= -27): b = z + 127 c = 2 ** b // power5 + 1 format(c) From 829ac72f87658053f0d004e8803a3d3eca80dbea Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Sun, 15 Nov 2020 22:38:21 +0000 Subject: [PATCH 18/47] re #33: 32bit version. gcc compiles successfully, fails tests. --- .github/workflows/ubuntu18.yml | 12 +++++++----- .github/workflows/ubuntu20.yml | 12 +++++++----- .github/workflows/vs16-ci.yml | 10 ++++++++-- .github/workflows/vs16-clang-ci.yml | 8 +++++++- .gitignore | 2 ++ include/fast_float/float_common.h | 24 ++++++++++++++++++++---- tests/long_random64.cpp | 2 +- tests/random64.cpp | 2 +- 8 files changed, 53 insertions(+), 19 deletions(-) create mode 100644 .gitignore diff --git a/.github/workflows/ubuntu18.yml b/.github/workflows/ubuntu18.yml index 23850cc..bf1f2d0 100644 --- a/.github/workflows/ubuntu18.yml +++ b/.github/workflows/ubuntu18.yml @@ -9,9 +9,10 @@ jobs: fail-fast: false matrix: include: - - {cxx: -DCMAKE_CXX_COMPILER=g++-5} - - {cxx: -DCMAKE_CXX_COMPILER=g++-6} - - {cxx: } # default compiler 7 + - {cxx: -DCMAKE_CXX_COMPILER=g++-5, arch: } + - {cxx: -DCMAKE_CXX_COMPILER=g++-6, arch: } + - {cxx: , arch: } # default=gcc7 + - {cxx: , arch: -DCMAKE_CXX_FLAGS="-m32"} # default=gcc7 steps: - uses: actions/checkout@v2 - name: Setup cmake @@ -20,12 +21,13 @@ jobs: cmake-version: '3.9.x' - name: Install older compilers run: | + sudo -E dpkg --add-architecture i386 sudo -E apt-get update - sudo -E apt-get install -y --force-yes g++-5 g++-6 + sudo -E apt-get install -y --force-yes g++-5 g++-6 g++-5-multilib g++-6-multilib g++-multilib linux-libc-dev:i386 libc6:i386 libc6-dev:i386 libc6-dbg:i386 - name: Prepare build dir run: mkdir build - name: Configure - run: cd build && cmake ${{matrix.cxx}} -DFASTFLOAT_TEST=ON .. + run: cd build && cmake ${{matrix.cxx}} ${{matrix.arch}} -DFASTFLOAT_TEST=ON .. - name: Build run: cmake --build build - name: Run basic tests diff --git a/.github/workflows/ubuntu20.yml b/.github/workflows/ubuntu20.yml index 68aa1ee..451c9b4 100644 --- a/.github/workflows/ubuntu20.yml +++ b/.github/workflows/ubuntu20.yml @@ -9,8 +9,9 @@ jobs: fail-fast: false matrix: include: - - {cxx: -DCMAKE_CXX_COMPILER=g++-8} - - {cxx: } # default compiler 9 + - {cxx: -DCMAKE_CXX_COMPILER=g++-8, arch: } + - {cxx: , arch: } # default=gcc9 + - {cxx: , arch: -DCMAKE_CXX_FLAGS="-m32"} # default=gcc9 steps: - uses: actions/checkout@v2 - name: Setup cmake @@ -19,12 +20,13 @@ jobs: cmake-version: '3.9.x' - name: install older compilers run: | + sudo -E dpkg --add-architecture i386 sudo -E apt-get update - sudo -E apt-get install -y --force-yes g++-8 g++-7 - - name: Prepare build dir + sudo -E apt-get install -y g++-8 g++-8-multilib g++-multilib linux-libc-dev:i386 libc6:i386 libc6-dev:i386 libc6-dbg:i386 + - name: Prepare build dir run: mkdir build - name: Configure - run: cd build && cmake ${{matrix.cxx}} -DFASTFLOAT_TEST=ON .. + run: cd build && cmake ${{matrix.cxx}} ${{matrix.arch}} -DFASTFLOAT_TEST=ON .. - name: Build run: cmake --build build - name: Run basic tests diff --git a/.github/workflows/vs16-ci.yml b/.github/workflows/vs16-ci.yml index 5a7071a..4f237fd 100644 --- a/.github/workflows/vs16-ci.yml +++ b/.github/workflows/vs16-ci.yml @@ -6,6 +6,12 @@ jobs: ci: name: windows-vs16 runs-on: windows-latest + strategy: + fail-fast: false + matrix: + include: + - vs: VS16Win64 + - vs: VS16Win32 steps: - uses: actions/checkout@v2 - name: 'Run CMake with VS16' @@ -16,9 +22,9 @@ jobs: buildDirectory: "${{ github.workspace }}/../../_temp/windows" cmakeBuildType: Release buildWithCMake: true - cmakeGenerator: VS16Win64 + cmakeGenerator: ${{matrix.vs}} cmakeAppendedArgs: -DFASTFLOAT_TEST=ON - buildWithCMakeArgs: --config Release + buildWithCMakeArgs: --config Release - name: 'Run CTest' run: ctest -C Release --output-on-failure -R basictest diff --git a/.github/workflows/vs16-clang-ci.yml b/.github/workflows/vs16-clang-ci.yml index 08afa25..2bf8e2b 100644 --- a/.github/workflows/vs16-clang-ci.yml +++ b/.github/workflows/vs16-clang-ci.yml @@ -6,6 +6,12 @@ jobs: ci: name: windows-vs16 runs-on: windows-latest + strategy: + fail-fast: false + matrix: + include: + - vs: VS16Win64 + - vs: VS16Win32 steps: - uses: actions/checkout@v2 - name: 'Run CMake with VS16' @@ -16,7 +22,7 @@ jobs: buildDirectory: "${{ github.workspace }}/../../_temp/windows" cmakeBuildType: Release buildWithCMake: true - cmakeGenerator: VS16Win64 + cmakeGenerator: ${{matrix.vs}} cmakeAppendedArgs: -T ClangCL -DFASTFLOAT_TEST=ON buildWithCMakeArgs: --config Release diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a5a3ead --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/* +Testing/* \ No newline at end of file diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 60e5f6e..a98a54b 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -139,15 +139,31 @@ fastfloat_really_inline value128 full_multiplication(uint64_t value1, return answer; } -#else +#else // gcc // compute value1 * value2 -fastfloat_really_inline value128 full_multiplication(uint64_t value1, - uint64_t value2) { +fastfloat_really_inline value128 full_multiplication(uint64_t a, + uint64_t b) { value128 answer; - __uint128_t r = ((__uint128_t)value1) * value2; +#if defined(__i386) || defined(__i386__) || defined(_M_IX86) || defined(__arm__) + static constexpr const uint64_t lo32 = 0xffffffffu; + // https://stackoverflow.com/questions/28868367/getting-the-high-part-of-64-bit-integer-multiplication + uint64_t a_lo = a & lo32; + uint64_t a_hi = a >> 32; + uint64_t b_lo = b & lo32; + uint64_t b_hi = b >> 32; + uint64_t ab_hi = a_hi * b_hi; + uint64_t ab_mid = a_hi * b_lo; + uint64_t ba_mid = b_hi * a_lo; + uint64_t ab_lo = a_lo * b_lo; + uint64_t carry_bit = ((ab_mid & lo32) + (ba_mid & lo32) + (ab_lo >> 32)) >> 32; + answer.high = ab_hi + (ab_mid >> 32) + (ba_mid >> 32) + carry_bit; + answer.low = ab_lo + (ab_mid & lo32) + (ba_mid & lo32); +#else // if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64) || defined(__aarch64__) || defined(_M_ARM64) + __uint128_t r = ((__uint128_t)a) * b; answer.low = uint64_t(r); answer.high = uint64_t(r >> 64); +#endif return answer; } diff --git a/tests/long_random64.cpp b/tests/long_random64.cpp index b8a6b05..47c4d0c 100644 --- a/tests/long_random64.cpp +++ b/tests/long_random64.cpp @@ -90,7 +90,7 @@ void random_values(size_t N) { int main() { errors = 0; - size_t N = size_t(1) << 32; + size_t N = size_t(1) << (sizeof(size_t) * 4); // shift: 32 for 64bit, 16 for 32bit random_values(N); if (errors == 0) { std::cout << std::endl; diff --git a/tests/random64.cpp b/tests/random64.cpp index 3f37975..d169fbb 100644 --- a/tests/random64.cpp +++ b/tests/random64.cpp @@ -92,7 +92,7 @@ void random_values(size_t N) { int main() { errors = 0; - size_t N = size_t(1) << 32; + size_t N = size_t(1) << (sizeof(size_t) * 4); // shift: 32 for 64bit, 16 for 32bit random_values(N); if (errors == 0) { std::cout << std::endl; From c4693cc86f5e8b4de5c30263954dc55b866fc9cb Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Mon, 16 Nov 2020 11:58:03 +0000 Subject: [PATCH 19/47] re #33: win32 is working --- include/fast_float/float_common.h | 18 +++++++++++++++--- tests/basictest.cpp | 24 +++++++++++++++++++++--- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index a98a54b..93782e9 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -90,6 +90,7 @@ struct value128 { /* result might be undefined when input_num is zero */ fastfloat_really_inline int leading_zeroes(uint64_t input_num) { #ifdef FASTFLOAT_VISUAL_STUDIO +#if defined(_M_X64) || defined(_M_ARM64) unsigned long leading_zero = 0; // Search the mask data from most significant bit (MSB) // to least significant bit (LSB) for a set bit (1). @@ -97,6 +98,17 @@ fastfloat_really_inline int leading_zeroes(uint64_t input_num) { return (int)(63 - leading_zero); else return 64; +#else + if(!input_num) return 64; + int n = 0; + if(input_num & uint64_t(0xffffffff00000000)) input_num >>= 32, n |= 32; + if(input_num & uint64_t( 0xffff0000)) input_num >>= 16, n |= 16; + if(input_num & uint64_t( 0xff00)) input_num >>= 8, n |= 8; + if(input_num & uint64_t( 0xf0)) input_num >>= 4, n |= 4; + if(input_num & uint64_t( 0xc)) input_num >>= 2, n |= 2; + if(input_num & uint64_t( 0x2)) input_num >>= 1, n |= 1; + return 63 - n; +#endif #else return __builtin_clzll(input_num); #endif @@ -109,9 +121,9 @@ fastfloat_really_inline int leading_zeroes(uint64_t input_num) { #if !defined(_M_X64) && !defined(_M_ARM64) // _umul128 for x86, arm // this is a slow emulation routine for 32-bit Windows // -fastfloat_really_inline uint64_t __emulu(uint32_t x, uint32_t y) { - return x * (uint64_t)y; -} +//fastfloat_really_inline uint64_t __emulu(uint32_t x, uint32_t y) { +// return x * (uint64_t)y; +//} fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); diff --git a/tests/basictest.cpp b/tests/basictest.cpp index 8acb9ca..8d9d750 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -1,9 +1,14 @@ #include "fast_float/fast_float.h" #include -inline void Assert(bool Assertion) { - if (!Assertion) - throw std::runtime_error("bug"); +#define Q(x) #x +#define QUOTE(x) Q(x) +#define Assert(assertion) \ + if (!(assertion)) {\ + AssertionFailed(__FILE__ ":" QUOTE(__LINE__) ": assertion failed: " #assertion);\ + } +inline void AssertionFailed(const char *msg) { + throw std::runtime_error(msg); } template std::string to_string(T d) { @@ -327,7 +332,20 @@ bool test_fixed_only() { return true; } + void test_leading_zeroes() + { + constexpr const uint64_t bit = 1; + Assert(fast_float::leading_zeroes(0) == 64); + Assert(fast_float::leading_zeroes(bit << 0) == 63); + Assert(fast_float::leading_zeroes(bit << 1) == 62); + Assert(fast_float::leading_zeroes(bit << 2) == 61); + Assert(fast_float::leading_zeroes(bit << 61) == 2); + Assert(fast_float::leading_zeroes(bit << 62) == 1); + Assert(fast_float::leading_zeroes(bit << 63) == 0); + } + int main() { + test_leading_zeroes(); Assert(test_fixed_only()); Assert(test_scientific_only()); Assert(issue8()); From bb03cb615ca807e3f7c2be783aed57880675c0f4 Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Mon, 16 Nov 2020 11:58:38 +0000 Subject: [PATCH 20/47] make sure CI runs Win32 compilations --- .github/workflows/vs16-ci.yml | 33 +++++++++++++---------------- .github/workflows/vs16-clang-ci.yml | 33 +++++++++++++---------------- 2 files changed, 30 insertions(+), 36 deletions(-) diff --git a/.github/workflows/vs16-ci.yml b/.github/workflows/vs16-ci.yml index 4f237fd..e486a74 100644 --- a/.github/workflows/vs16-ci.yml +++ b/.github/workflows/vs16-ci.yml @@ -10,22 +10,19 @@ jobs: fail-fast: false matrix: include: - - vs: VS16Win64 - - vs: VS16Win32 + - {gen: Visual Studio 16 2019, arch: Win32} + - {gen: Visual Studio 16 2019, arch: x64} steps: - - uses: actions/checkout@v2 - - name: 'Run CMake with VS16' - uses: lukka/run-cmake@v2 - with: - cmakeListsOrSettingsJson: CMakeListsTxtAdvanced - cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' - buildDirectory: "${{ github.workspace }}/../../_temp/windows" - cmakeBuildType: Release - buildWithCMake: true - cmakeGenerator: ${{matrix.vs}} - cmakeAppendedArgs: -DFASTFLOAT_TEST=ON - buildWithCMakeArgs: --config Release - - - name: 'Run CTest' - run: ctest -C Release --output-on-failure -R basictest - working-directory: "${{ github.workspace }}/../../_temp/windows" + - uses: actions/checkout@v2 + - name: Setup cmake + uses: jwlawson/actions-setup-cmake@v1.0 + with: + cmake-version: '3.9.x' + - name: Prepare build dir + run: mkdir build + - name: Configure + run: cd build && cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_TEST=ON .. + - name: Build + run: cmake --build build --configuration Release + - name: Run basic tests + run: cd build && ctest -C Release --output-on-failure -R basictest diff --git a/.github/workflows/vs16-clang-ci.yml b/.github/workflows/vs16-clang-ci.yml index 2bf8e2b..d3858bd 100644 --- a/.github/workflows/vs16-clang-ci.yml +++ b/.github/workflows/vs16-clang-ci.yml @@ -10,22 +10,19 @@ jobs: fail-fast: false matrix: include: - - vs: VS16Win64 - - vs: VS16Win32 + - {gen: Visual Studio 16 2019, arch: Win32} + - {gen: Visual Studio 16 2019, arch: x64} steps: - - uses: actions/checkout@v2 - - name: 'Run CMake with VS16' - uses: lukka/run-cmake@v2 - with: - cmakeListsOrSettingsJson: CMakeListsTxtAdvanced - cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' - buildDirectory: "${{ github.workspace }}/../../_temp/windows" - cmakeBuildType: Release - buildWithCMake: true - cmakeGenerator: ${{matrix.vs}} - cmakeAppendedArgs: -T ClangCL -DFASTFLOAT_TEST=ON - buildWithCMakeArgs: --config Release - - - name: 'Run CTest' - run: ctest -C Release -R basictest --output-on-failure - working-directory: "${{ github.workspace }}/../../_temp/windows" + - uses: actions/checkout@v2 + - name: Setup cmake + uses: jwlawson/actions-setup-cmake@v1.0 + with: + cmake-version: '3.9.x' + - name: Prepare build dir + run: mkdir build + - name: Configure + run: cd build && cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -DFASTFLOAT_TEST=ON .. + - name: Build + run: cmake --build build --configuration Release + - name: Run basic tests + run: cd build && ctest -C Release --output-on-failure -R basictest From 7da4b05645425649ef772b488914157795ce8573 Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Mon, 16 Nov 2020 11:59:18 +0000 Subject: [PATCH 21/47] add unit test for full multiplication --- tests/basictest.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index 8d9d750..2a2ab92 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -344,8 +344,33 @@ bool test_fixed_only() { Assert(fast_float::leading_zeroes(bit << 63) == 0); } + void test_full_multiplication(uint64_t lhs, uint64_t rhs, uint64_t expected_lo, uint64_t expected_hi) + { + fast_float::value128 v; + v = fast_float::full_multiplication(lhs, rhs); + Assert(v.low == expected_lo); + Assert(v.high == expected_hi); + v = fast_float::full_multiplication(rhs, lhs); + Assert(v.low == expected_lo); + Assert(v.high == expected_hi); + } + + void test_full_multiplication() + { + constexpr const uint64_t bit = 1; + // lhs rhs lo hi + test_full_multiplication(bit << 0 , bit << 0, 1u , 0u); + test_full_multiplication(bit << 0 , bit << 63, bit << 63, 0u); + test_full_multiplication(bit << 1 , bit << 63, 0u , 1u); + test_full_multiplication(bit << 63, bit << 0, bit << 63, 0u); + test_full_multiplication(bit << 63, bit << 1, 0u , 1u); + test_full_multiplication(bit << 63, bit << 2, 0u , 2u); + test_full_multiplication(bit << 63, bit << 63, 0u , bit << 62); + } + int main() { test_leading_zeroes(); + test_full_multiplication(); Assert(test_fixed_only()); Assert(test_scientific_only()); Assert(issue8()); From 83a29d4eab7511f121213d128cb0865ded00e055 Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Mon, 16 Nov 2020 13:32:36 +0000 Subject: [PATCH 22/47] windows CI: cmake 3.9 is too old --- .github/workflows/vs16-ci.yml | 13 +++++-------- .github/workflows/vs16-clang-ci.yml | 13 +++++-------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/.github/workflows/vs16-ci.yml b/.github/workflows/vs16-ci.yml index e486a74..3be2ba7 100644 --- a/.github/workflows/vs16-ci.yml +++ b/.github/workflows/vs16-ci.yml @@ -13,15 +13,12 @@ jobs: - {gen: Visual Studio 16 2019, arch: Win32} - {gen: Visual Studio 16 2019, arch: x64} steps: - - uses: actions/checkout@v2 - - name: Setup cmake - uses: jwlawson/actions-setup-cmake@v1.0 - with: - cmake-version: '3.9.x' - - name: Prepare build dir - run: mkdir build + - name: checkout + uses: actions/checkout@v2 - name: Configure - run: cd build && cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_TEST=ON .. + run: | + mkdir build + cd build && cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_TEST=ON .. - name: Build run: cmake --build build --configuration Release - name: Run basic tests diff --git a/.github/workflows/vs16-clang-ci.yml b/.github/workflows/vs16-clang-ci.yml index d3858bd..0c4faa7 100644 --- a/.github/workflows/vs16-clang-ci.yml +++ b/.github/workflows/vs16-clang-ci.yml @@ -13,15 +13,12 @@ jobs: - {gen: Visual Studio 16 2019, arch: Win32} - {gen: Visual Studio 16 2019, arch: x64} steps: - - uses: actions/checkout@v2 - - name: Setup cmake - uses: jwlawson/actions-setup-cmake@v1.0 - with: - cmake-version: '3.9.x' - - name: Prepare build dir - run: mkdir build + - name: checkout + uses: actions/checkout@v2 - name: Configure - run: cd build && cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -DFASTFLOAT_TEST=ON .. + run: | + mkdir build + cd build && cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -DFASTFLOAT_TEST=ON .. - name: Build run: cmake --build build --configuration Release - name: Run basic tests From 449c6286454f0e8f864f2b67ef3dbadab3db6bef Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Mon, 16 Nov 2020 13:33:11 +0000 Subject: [PATCH 23/47] __emulu() is needed for mingw32 --- include/fast_float/float_common.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 93782e9..2c0c18e 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -120,10 +120,13 @@ fastfloat_really_inline int leading_zeroes(uint64_t input_num) { #if !defined(_M_X64) && !defined(_M_ARM64) // _umul128 for x86, arm // this is a slow emulation routine for 32-bit Windows -// -//fastfloat_really_inline uint64_t __emulu(uint32_t x, uint32_t y) { -// return x * (uint64_t)y; -//} + +#ifdef __MINGW32__ +fastfloat_really_inline uint64_t __emulu(uint32_t x, uint32_t y) { + return x * (uint64_t)y; +} +#endif + fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); From 11a1c143a38819be3b094ff5322994a18972c264 Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Mon, 16 Nov 2020 13:38:19 +0000 Subject: [PATCH 24/47] windows CI: fix call to cmake --build --- .github/workflows/vs16-ci.yml | 2 +- .github/workflows/vs16-clang-ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/vs16-ci.yml b/.github/workflows/vs16-ci.yml index 3be2ba7..8548aba 100644 --- a/.github/workflows/vs16-ci.yml +++ b/.github/workflows/vs16-ci.yml @@ -20,6 +20,6 @@ jobs: mkdir build cd build && cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_TEST=ON .. - name: Build - run: cmake --build build --configuration Release + run: cmake --build build --config Release --parallel - name: Run basic tests run: cd build && ctest -C Release --output-on-failure -R basictest diff --git a/.github/workflows/vs16-clang-ci.yml b/.github/workflows/vs16-clang-ci.yml index 0c4faa7..559630d 100644 --- a/.github/workflows/vs16-clang-ci.yml +++ b/.github/workflows/vs16-clang-ci.yml @@ -20,6 +20,6 @@ jobs: mkdir build cd build && cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -DFASTFLOAT_TEST=ON .. - name: Build - run: cmake --build build --configuration Release + run: cmake --build build --config Release --parallel - name: Run basic tests run: cd build && ctest -C Release --output-on-failure -R basictest From 27345da2e2a83556fe72246de158ae5b1af38671 Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Mon, 16 Nov 2020 16:00:03 +0000 Subject: [PATCH 25/47] ubuntu20 CI: fix typo --- .github/workflows/ubuntu20.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ubuntu20.yml b/.github/workflows/ubuntu20.yml index 451c9b4..6bf0dcc 100644 --- a/.github/workflows/ubuntu20.yml +++ b/.github/workflows/ubuntu20.yml @@ -23,7 +23,7 @@ jobs: sudo -E dpkg --add-architecture i386 sudo -E apt-get update sudo -E apt-get install -y g++-8 g++-8-multilib g++-multilib linux-libc-dev:i386 libc6:i386 libc6-dev:i386 libc6-dbg:i386 - - name: Prepare build dir + - name: Prepare build dir run: mkdir build - name: Configure run: cd build && cmake ${{matrix.cxx}} ${{matrix.arch}} -DFASTFLOAT_TEST=ON .. From 1e795800656553ed5cb7b35484bcbf136bd7b208 Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Tue, 17 Nov 2020 01:49:58 +0000 Subject: [PATCH 26/47] basictest: move to doctest benefits: * shows all failures instead of throwing * every failure shows detailed information * more concise (~400 lines -> ~300 lines) --- tests/CMakeLists.txt | 10 +- tests/basictest.cpp | 695 ++++++++++++++++++------------------------- 2 files changed, 302 insertions(+), 403 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0b32b52..cce9438 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,3 +1,9 @@ +include(FetchContent) + +FetchContent_Declare(doctest + GIT_REPOSITORY https://github.com/onqtam/doctest.git + GIT_TAG master) +FetchContent_MakeAvailable(doctest) function(fast_float_add_cpp_test TEST_NAME) add_executable(${TEST_NAME} ${TEST_NAME}.cpp) @@ -6,7 +12,7 @@ function(fast_float_add_cpp_test TEST_NAME) target_compile_options(${TEST_NAME} PUBLIC -Werror -Wall -Wextra -Weffc++) target_compile_options(${TEST_NAME} PUBLIC -Wsign-compare -Wshadow -Wwrite-strings -Wpointer-arith -Winit-self -Wconversion -Wsign-conversion) endif() - target_link_libraries(${TEST_NAME} PUBLIC fast_float) + target_link_libraries(${TEST_NAME} PUBLIC fast_float doctest) endfunction(fast_float_add_cpp_test) fast_float_add_cpp_test(short_random_string) fast_float_add_cpp_test(exhaustive32_midpoint) @@ -19,4 +25,4 @@ fast_float_add_cpp_test(long_exhaustive32_64) fast_float_add_cpp_test(long_random64) fast_float_add_cpp_test(random64) fast_float_add_cpp_test(basictest) -fast_float_add_cpp_test(example_test) \ No newline at end of file +fast_float_add_cpp_test(example_test) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index 2a2ab92..a699198 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -1,125 +1,51 @@ #include "fast_float/fast_float.h" #include +#define DOCTEST_CONFIG_SUPER_FAST_ASSERTS +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include -#define Q(x) #x -#define QUOTE(x) Q(x) -#define Assert(assertion) \ - if (!(assertion)) {\ - AssertionFailed(__FILE__ ":" QUOTE(__LINE__) ": assertion failed: " #assertion);\ - } -inline void AssertionFailed(const char *msg) { - throw std::runtime_error(msg); + +TEST_CASE("leading_zeroes") { + constexpr const uint64_t bit = 1; + CHECK(fast_float::leading_zeroes(0) == 64); + CHECK(fast_float::leading_zeroes(bit << 0) == 63); + CHECK(fast_float::leading_zeroes(bit << 1) == 62); + CHECK(fast_float::leading_zeroes(bit << 2) == 61); + CHECK(fast_float::leading_zeroes(bit << 61) == 2); + CHECK(fast_float::leading_zeroes(bit << 62) == 1); + CHECK(fast_float::leading_zeroes(bit << 63) == 0); } -template std::string to_string(T d) { - std::string s(64, '\0'); - auto written = std::snprintf(&s[0], s.size(), "%.*e", - std::numeric_limits::max_digits10 - 1, d); - s.resize(size_t(written)); - return s; +#define iHexAndDec(v) std::hex << "0x" << (v) << " (" << std::dec << (v) << ")" +#define fHexAndDec(v) std::hexfloat << "0x" << (v) << " (" << std::defaultfloat << (v) << ")" + +void test_full_multiplication(uint64_t lhs, uint64_t rhs, uint64_t expected_lo, uint64_t expected_hi) { + fast_float::value128 v; + v = fast_float::full_multiplication(lhs, rhs); + INFO("lhs=" << iHexAndDec(lhs) << " " << "rhs=" << iHexAndDec(rhs) + << "\n actualLo=" << iHexAndDec(v.low) << " " << "actualHi=" << iHexAndDec(v.high) + << "\n expectedLo=" << iHexAndDec(expected_lo) << " " << "expectedHi=" << iHexAndDec(expected_hi)); + CHECK_EQ(v.low, expected_lo); + CHECK_EQ(v.high, expected_hi); + v = fast_float::full_multiplication(rhs, lhs); + CHECK_EQ(v.low, expected_lo); + CHECK_EQ(v.high, expected_hi); } -template std::string to_long_string(T d) { - std::string s(4096, '\0'); - auto written = std::snprintf(&s[0], s.size(), "%.*e", - std::numeric_limits::max_digits10 * 10, d); - s.resize(size_t(written)); - return s; -} - -bool basic_test_32bit(std::string vals) { - std::cout << " parsing " << vals << std::endl; - float result_value; - auto result = fast_float::from_chars(vals.data(), vals.data() + vals.size(), - result_value); - if (result.ec != std::errc()) { - std::cerr << " I could not parse " << vals << std::endl; - return false; - } - - std::cout << std::hexfloat << result_value << std::endl; - std::cout << std::dec; - return true; -} - -bool basic_test_32bit(std::string vals, float val) { - std::cout << " parsing " << vals << std::endl; - float result_value; - auto result = fast_float::from_chars(vals.data(), vals.data() + vals.size(), - result_value); - if (result.ec != std::errc()) { - std::cerr << " I could not parse " << vals << std::endl; - return false; - } - if(copysign(1,result_value) != copysign(1,val)) { - std::cerr << vals << std::endl; - std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << val - << std::endl; - return false; - } else if (std::isnan(val)) { - if (!std::isnan(result_value)) { - std::cerr << vals << std::endl; - std::cerr << "not nan" << result_value << std::endl; - return false; - } - } else if (result_value != val) { - std::cerr << vals << std::endl; - std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << val - << std::endl; - std::cerr << std::dec; - uint32_t word; - memcpy(&word, &result_value, sizeof(word)); - std::cout << "got mantissa = " << (word & ((1<<23)-1)) << std::endl; - memcpy(&word, &val, sizeof(word)); - std::cout << "wanted mantissa = " << (word & ((1<<23)-1)) << std::endl; - std::cerr << "string: " << vals << std::endl; - return false; - } - std::cout << std::hexfloat << result_value << " == " << val << std::endl; - std::cout << std::dec; - return true; -} - -bool basic_test_32bit(float val) { - std::string long_vals = to_long_string(val); - std::string vals = to_string(val); - return basic_test_32bit(long_vals, val) && basic_test_32bit(vals, val); -} - -bool basic_test_64bit(std::string vals, double val) { - std::cout << " parsing " << vals << std::endl; - double result_value; - auto result = fast_float::from_chars(vals.data(), vals.data() + vals.size(), - result_value); - if (result.ec != std::errc()) { - std::cerr << " I could not parse " << vals << std::endl; - return false; - } - if(copysign(1,result_value) != copysign(1,val)) { - std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << val - << std::endl; - return false; - } else if (std::isnan(val)) { - if (!std::isnan(result_value)) { - std::cerr << vals << std::endl; - std::cerr << "not nan" << result_value << std::endl; - return false; - } - } else if (result_value != val) { - std::cerr << vals << std::endl; - std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << val - << std::endl; - std::cerr << std::dec; - std::cerr << "string: " << vals << std::endl; - return false; - } - std::cout << std::hexfloat << result_value << " == " << val << std::endl; - return true; +TEST_CASE("full_multiplication") { + constexpr const uint64_t bit = 1; + // lhs rhs lo hi + test_full_multiplication(bit << 0 , bit << 0, 1u , 0u); + test_full_multiplication(bit << 0 , bit << 63, bit << 63, 0u); + test_full_multiplication(bit << 1 , bit << 63, 0u , 1u); + test_full_multiplication(bit << 63, bit << 0, bit << 63, 0u); + test_full_multiplication(bit << 63, bit << 1, 0u , 1u); + test_full_multiplication(bit << 63, bit << 2, 0u , 2u); + test_full_multiplication(bit << 63, bit << 63, 0u , bit << 62); } -bool issue8() { - std::cout << __func__ << std::endl; +TEST_CASE("issue8") { const char* s = "3." "141592653589793238462643383279502884197169399375105820974944592307816406" @@ -137,98 +63,62 @@ bool issue8() { // Parse all but the last i chars. We should still get 3.141ish. double d = 0.0; auto answer = fast_float::from_chars(s, s + strlen(s) - i, d); - if(answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return false; } - if(d != 0x1.921fb54442d18p+1) { - printf("%.*s\n", int(strlen(s) - size_t(i)), s); - std::cout << std::hexfloat << d << std::endl; - std::cout << std::defaultfloat << d << std::endl; - return false; - } - + CHECK_MESSAGE(answer.ec == std::errc(), "i=" << i); + CHECK_MESSAGE(d == 0x1.921fb54442d18p+1, "i=" << i << "\n" + << std::string(s, strlen(s) - size_t(i)) << "\n" + << std::hexfloat << d << "\n" + << std::defaultfloat << "\n"); } - return true; } -bool check_behavior() { - std::cout << __func__ << std::endl; - const std::string input = "abc"; - double result; - auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result); - if(answer.ec != std::errc()) { - std::cerr << "parsing failure as expected\n"; - // specification says ptr should point at first - if(answer.ptr != input.data()) { - std::cerr << "If there is no pattern match, we should have ptr equals first\n"; - return false; - } - return true; - } - std::cout << "parsed the number " << result << std::endl; - return false; -} - -bool issue19() { - std::cout << __func__ << std::endl; - const std::string input = "3.14e"; - double result; - auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result); - if(answer.ec != std::errc()) { - std::cerr << "We want to parse up to 3.14\n"; - return false; - } - std::cout << "parsed the number " << result << std::endl; - if(answer.ptr == input.data() + 4) { - std::cout << "Parsed the number and stopped at the right character." << result << std::endl; - return true; - } - std::cout << "stopped after " << (answer.ptr - input.data()) << " characters" << std::endl; - return false; +TEST_CASE("check_behavior") { + const std::string input = "abc"; + double result; + auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result); + CHECK_MESSAGE(answer.ec != std::errc(), "expected parse failure"); + CHECK_MESSAGE(answer.ptr == input.data(), "If there is no pattern match, we should have ptr equals first"); } -bool test_scientific_only() { - std::cout << __func__ << std::endl; +TEST_CASE("issue19") { + const std::string input = "3.14e"; + double result; + auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result); + CHECK_MESSAGE(answer.ec == std::errc(), "We want to parse up to 3.14\n"); + CHECK_MESSAGE(answer.ptr == input.data() + 4, + "Parsed the number " << result + << " and stopped at the wrong character: after " << (answer.ptr - input.data()) << " characters"); +} + + +TEST_CASE("scientific_only") { // first, we try with something that should fail... std::string input = "3.14"; double result; auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result, fast_float::chars_format::scientific); - if(answer.ec == std::errc()) { - std::cerr << "It is not scientific!\n"; - return false; - } + CHECK_MESSAGE(answer.ec != std::errc(), "It is not scientific! Parsed: " << result); + input = "3.14e10"; answer = fast_float::from_chars(input.data(), input.data()+input.size(), result, fast_float::chars_format::scientific); - if(answer.ec != std::errc()) { - std::cerr << "It is scientific!\n"; - return false; - } - std::cout << "parsed the number " << result << std::endl; - if(answer.ptr == input.data() + input.size()) { - std::cout << "Parsed the number and stopped at the right character." << result << std::endl; - return true; - } - return false; + CHECK_MESSAGE(answer.ec == std::errc(), "It is scientific! Parsed: " << result); + CHECK_MESSAGE(answer.ptr == input.data() + input.size(), + "Parsed the number " << result + << " and stopped at the wrong character: after " << (answer.ptr - input.data()) << " characters"); } -bool test_fixed_only() { - std::cout << __func__ << std::endl; + +TEST_CASE("test_fixed_only") { const std::string input = "3.14e10"; double result; auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result, fast_float::chars_format::fixed); - if(answer.ec != std::errc()) { - std::cerr << "We want to parse up to 3.14\n"; - return false; - } - std::cout << "parsed the number " << result << std::endl; - if(answer.ptr == input.data() + 4) { - std::cout << "Parsed the number and stopped at the right character." << result << std::endl; - return true; - } - return false; + CHECK_MESSAGE(answer.ec == std::errc(), "We want to parse up to 3.14; parsed: " << result); + CHECK_MESSAGE(answer.ptr == input.data() + 4, + "Parsed the number " << result + << " and stopped at the wrong character: after " << (answer.ptr - input.data()) << " characters"); } - static const double testing_power_of_ten[] = { +static const double testing_power_of_ten[] = { 1e-307, 1e-306, 1e-305, 1e-304, 1e-303, 1e-302, 1e-301, 1e-300, 1e-299, 1e-298, 1e-297, 1e-296, 1e-295, 1e-294, 1e-293, 1e-292, 1e-291, 1e-290, 1e-289, 1e-288, 1e-287, 1e-286, 1e-285, 1e-284, 1e-283, 1e-282, 1e-281, @@ -300,228 +190,231 @@ bool test_fixed_only() { 1e305, 1e306, 1e307, 1e308}; - - bool powers_of_ten() { - std::cout << __func__ << std::endl; +TEST_CASE("powers_of_ten") { char buf[1024]; - + CHECK_MESSAGE(1e-308 == std::pow(10, -308), "On your system, the pow function is busted. Sorry about that."); bool is_pow_correct{1e-308 == std::pow(10,-308)}; + // large negative values should be zero. int start_point = is_pow_correct ? -1000 : -307; - if(!is_pow_correct) { - std::cout << "On your system, the pow function is busted. Sorry about that. " << std::endl; - } - for (int i = start_point; i <= 308; ++i) {// large negative values should be zero. - std::cout << "."; - std::cout.flush(); + for (int i = start_point; i <= 308; ++i) { + INFO("i=" << i); size_t n = size_t(snprintf(buf, sizeof(buf), "1e%d", i)); - if (n >= sizeof(buf)) { abort(); } + REQUIRE(n < sizeof(buf)); // if false, fails the test and exits double actual; auto result = fast_float::from_chars(buf, buf + 1000, actual); - if (result.ec != std::errc()) { - std::cerr << " I could not parse " << buf << std::endl; - return false; - } - double expected = ((i >= -307) ? testing_power_of_ten[i + 307]: std::pow(10, i)); - if(actual!=expected) { - std::cerr << "String '" << buf << " parsed to " << actual; - return false; - } + CHECK_MESSAGE(result.ec == std::errc(), " I could not parse " << buf); + double expected = ((i >= -307) ? testing_power_of_ten[i + 307] : std::pow(10, i)); + CHECK_MESSAGE(actual == expected, "String '" << buf << "'parsed to " << actual); } - std::cout << std::endl; - std::cout << "Powers of 10 can be parsed exactly.\n"; - return true; - } - - void test_leading_zeroes() - { - constexpr const uint64_t bit = 1; - Assert(fast_float::leading_zeroes(0) == 64); - Assert(fast_float::leading_zeroes(bit << 0) == 63); - Assert(fast_float::leading_zeroes(bit << 1) == 62); - Assert(fast_float::leading_zeroes(bit << 2) == 61); - Assert(fast_float::leading_zeroes(bit << 61) == 2); - Assert(fast_float::leading_zeroes(bit << 62) == 1); - Assert(fast_float::leading_zeroes(bit << 63) == 0); - } - - void test_full_multiplication(uint64_t lhs, uint64_t rhs, uint64_t expected_lo, uint64_t expected_hi) - { - fast_float::value128 v; - v = fast_float::full_multiplication(lhs, rhs); - Assert(v.low == expected_lo); - Assert(v.high == expected_hi); - v = fast_float::full_multiplication(rhs, lhs); - Assert(v.low == expected_lo); - Assert(v.high == expected_hi); - } - - void test_full_multiplication() - { - constexpr const uint64_t bit = 1; - // lhs rhs lo hi - test_full_multiplication(bit << 0 , bit << 0, 1u , 0u); - test_full_multiplication(bit << 0 , bit << 63, bit << 63, 0u); - test_full_multiplication(bit << 1 , bit << 63, 0u , 1u); - test_full_multiplication(bit << 63, bit << 0, bit << 63, 0u); - test_full_multiplication(bit << 63, bit << 1, 0u , 1u); - test_full_multiplication(bit << 63, bit << 2, 0u , 2u); - test_full_multiplication(bit << 63, bit << 63, 0u , bit << 62); - } - -int main() { - test_leading_zeroes(); - test_full_multiplication(); - Assert(test_fixed_only()); - Assert(test_scientific_only()); - Assert(issue8()); - Assert(issue19()); - Assert(check_behavior()); - std::cout << "======= 64 bits " << std::endl; - Assert(basic_test_64bit("1.1920928955078125e-07",1.1920928955078125e-07)); - Assert(basic_test_64bit("INF",std::numeric_limits::infinity())); - Assert(basic_test_64bit("-INF",-std::numeric_limits::infinity())); - Assert(basic_test_64bit("INFINITY",std::numeric_limits::infinity())); - Assert(basic_test_64bit("-INFINITY",-std::numeric_limits::infinity())); - Assert(basic_test_64bit("infinity",std::numeric_limits::infinity())); - Assert(basic_test_64bit("-infinity",-std::numeric_limits::infinity())); - Assert(basic_test_64bit("inf",std::numeric_limits::infinity())); - Assert(basic_test_64bit("-inf",-std::numeric_limits::infinity())); - Assert(basic_test_64bit("9355950000000000000.00000000000000000000000000000000001844674407370955161600000184467440737095516161844674407370955161407370955161618446744073709551616000184467440737095516166000001844674407370955161618446744073709551614073709551616184467440737095516160001844674407370955161601844674407370955674451616184467440737095516140737095516161844674407370955161600018446744073709551616018446744073709551611616000184467440737095001844674407370955161600184467440737095516160018446744073709551168164467440737095516160001844073709551616018446744073709551616184467440737095516160001844674407536910751601611616000184467440737095001844674407370955161600184467440737095516160018446744073709551616184467440737095516160001844955161618446744073709551616000184467440753691075160018446744073709",0x1.03ae05e8fca1cp+63)); - Assert(basic_test_64bit("-0",-0.0)); - Assert(basic_test_64bit("2.22507385850720212418870147920222032907240528279439037814303133837435107319244194686754406432563881851382188218502438069999947733013005649884107791928741341929297200970481951993067993290969042784064731682041565926728632933630474670123316852983422152744517260835859654566319282835244787787799894310779783833699159288594555213714181128458251145584319223079897504395086859412457230891738946169368372321191373658977977723286698840356390251044443035457396733706583981055420456693824658413747607155981176573877626747665912387199931904006317334709003012790188175203447190250028061277777916798391090578584006464715943810511489154282775041174682194133952466682503431306181587829379004205392375072083366693241580002758391118854188641513168478436313080237596295773983001708984375e-308", 0x1.0000000000002p-1022)); - Assert(basic_test_64bit("1.0000000000000006661338147750939242541790008544921875",1.0000000000000007)); - Assert(basic_test_64bit("1090544144181609348835077142190",0x1.b8779f2474dfbp+99)); - Assert(basic_test_64bit("2.2250738585072013e-308",2.2250738585072013e-308)); - Assert(basic_test_64bit("-92666518056446206563E3", -92666518056446206563E3)); - Assert(basic_test_64bit("-92666518056446206563E3", -92666518056446206563E3)); - Assert(basic_test_64bit("-42823146028335318693e-128",-42823146028335318693e-128)); - Assert(basic_test_64bit("90054602635948575728E72",90054602635948575728E72)); - Assert(basic_test_64bit("1.00000000000000188558920870223463870174566020691753515394643550663070558368373221972569761144603605635692374830246134201063722058e-309", 1.00000000000000188558920870223463870174566020691753515394643550663070558368373221972569761144603605635692374830246134201063722058e-309)); - Assert(basic_test_64bit("0e9999999999999999999999999999", 0)); - Assert(basic_test_32bit("1234456789012345678901234567890e9999999999999999999999999999", std::numeric_limits::infinity())); - Assert(basic_test_64bit("-2139879401095466344511101915470454744.9813888656856943E+272", -std::numeric_limits::infinity())); - Assert(basic_test_64bit("-2402844368454405395.2", -2402844368454405395.2)); - Assert(basic_test_64bit("2402844368454405395.2", 2402844368454405395.2)); - Assert(basic_test_64bit("7.0420557077594588669468784357561207962098443483187940792729600000e+59", 7.0420557077594588669468784357561207962098443483187940792729600000e+59)); - Assert(basic_test_64bit("7.0420557077594588669468784357561207962098443483187940792729600000e+59", 7.0420557077594588669468784357561207962098443483187940792729600000e+59)); - Assert(basic_test_64bit("-1.7339253062092163730578609458683877051596800000000000000000000000e+42", -1.7339253062092163730578609458683877051596800000000000000000000000e+42)); - Assert(basic_test_64bit("-2.0972622234386619214559824785284023792871122537545728000000000000e+52", -2.0972622234386619214559824785284023792871122537545728000000000000e+52)); - Assert(basic_test_64bit("-1.0001803374372191849407179462120053338028379051879898808320000000e+57", -1.0001803374372191849407179462120053338028379051879898808320000000e+57)); - Assert(basic_test_64bit("-1.8607245283054342363818436991534856973992070520151142825984000000e+58", -1.8607245283054342363818436991534856973992070520151142825984000000e+58)); - Assert(basic_test_64bit("-1.9189205311132686907264385602245237137907390376574976000000000000e+52", -1.9189205311132686907264385602245237137907390376574976000000000000e+52)); - Assert(basic_test_64bit("-2.8184483231688951563253238886553506793085187889855201280000000000e+54", -2.8184483231688951563253238886553506793085187889855201280000000000e+54)); - Assert(basic_test_64bit("-1.7664960224650106892054063261344555646357024359107788800000000000e+53", -1.7664960224650106892054063261344555646357024359107788800000000000e+53)); - Assert(basic_test_64bit("-2.1470977154320536489471030463761883783915110400000000000000000000e+45", -2.1470977154320536489471030463761883783915110400000000000000000000e+45)); - Assert(basic_test_64bit("-4.4900312744003159009338275160799498340862630046359789166919680000e+61", -4.4900312744003159009338275160799498340862630046359789166919680000e+61)); - Assert(basic_test_64bit("+1", 1)); - Assert(basic_test_64bit("1.8e308", std::numeric_limits::infinity())); - Assert(basic_test_64bit("1.797693134862315700000000000000001e308", 1.7976931348623157e308)); - Assert(basic_test_64bit("1.832312213213213232132132143451234453123412321321312e308", std::numeric_limits::infinity())); - Assert(basic_test_64bit("2e30000000000000000", std::numeric_limits::infinity())); - Assert(basic_test_64bit("2e3000", std::numeric_limits::infinity())); - Assert(basic_test_64bit("1.9e308", std::numeric_limits::infinity())); - Assert(basic_test_64bit("3e-324", 0x0.0000000000001p-1022)); - Assert(basic_test_64bit("1.00000006e+09", 0x1.dcd651ep+29)); - Assert(basic_test_64bit("4.9406564584124653e-324", 0x0.0000000000001p-1022)); - Assert(basic_test_64bit("4.9406564584124654e-324", 0x0.0000000000001p-1022)); - Assert(basic_test_64bit("2.2250738585072009e-308", 0x0.fffffffffffffp-1022)); - Assert(basic_test_64bit("2.2250738585072014e-308", 0x1p-1022)); - Assert(basic_test_64bit("1.7976931348623157e308", 0x1.fffffffffffffp+1023)); - Assert(basic_test_64bit("1.7976931348623158e308", 0x1.fffffffffffffp+1023)); - Assert(basic_test_64bit("4503599627370496.5", 4503599627370496.5)); - Assert(basic_test_64bit("4503599627475352.5", 4503599627475352.5)); - Assert(basic_test_64bit("4503599627475353.5", 4503599627475353.5)); - Assert(basic_test_64bit("2251799813685248.25", 2251799813685248.25)); - Assert(basic_test_64bit("1125899906842624.125", 1125899906842624.125)); - Assert(basic_test_64bit("1125899906842901.875", 1125899906842901.875)); - Assert(basic_test_64bit("2251799813685803.75", 2251799813685803.75)); - Assert(basic_test_64bit("4503599627370497.5", 4503599627370497.5)); - Assert(basic_test_64bit("45035996.273704995", 45035996.273704995)); - Assert(basic_test_64bit("45035996.273704985", 45035996.273704985)); - Assert(basic_test_64bit("0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044501477170144022721148195934182639518696390927032912960468522194496444440421538910330590478162701758282983178260792422137401728773891892910553144148156412434867599762821265346585071045737627442980259622449029037796981144446145705102663115100318287949527959668236039986479250965780342141637013812613333119898765515451440315261253813266652951306000184917766328660755595837392240989947807556594098101021612198814605258742579179000071675999344145086087205681577915435923018910334964869420614052182892431445797605163650903606514140377217442262561590244668525767372446430075513332450079650686719491377688478005309963967709758965844137894433796621993967316936280457084866613206797017728916080020698679408551343728867675409720757232455434770912461317493580281734466552734375", 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044501477170144022721148195934182639518696390927032912960468522194496444440421538910330590478162701758282983178260792422137401728773891892910553144148156412434867599762821265346585071045737627442980259622449029037796981144446145705102663115100318287949527959668236039986479250965780342141637013812613333119898765515451440315261253813266652951306000184917766328660755595837392240989947807556594098101021612198814605258742579179000071675999344145086087205681577915435923018910334964869420614052182892431445797605163650903606514140377217442262561590244668525767372446430075513332450079650686719491377688478005309963967709758965844137894433796621993967316936280457084866613206797017728916080020698679408551343728867675409720757232455434770912461317493580281734466552734375)); - Assert(basic_test_64bit("0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375", 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375)); - Assert(basic_test_64bit("1438456663141390273526118207642235581183227845246331231162636653790368152091394196930365828634687637948157940776599182791387527135353034738357134110310609455693900824193549772792016543182680519740580354365467985440183598701312257624545562331397018329928613196125590274187720073914818062530830316533158098624984118889298281371812288789537310599037529113415438738954894752124724983067241108764488346454376699018673078404751121414804937224240805993123816932326223683090770561597570457793932985826162604255884529134126396282202126526253389383421806727954588525596114379801269094096329805054803089299736996870951258573010877404407451953846698609198213926882692078557033228265259305481198526059813164469187586693257335779522020407645498684263339921905227556616698129967412891282231685504660671277927198290009824680186319750978665734576683784255802269708917361719466043175201158849097881370477111850171579869056016061666173029059588433776015644439705050377554277696143928278093453792803846252715966016733222646442382892123940052441346822429721593884378212558701004356924243030059517489346646577724622498919752597382095222500311124181823512251071356181769376577651390028297796156208815375089159128394945710515861334486267101797497111125909272505194792870889617179758703442608016143343262159998149700606597792535574457560429226974273443630323818747730771316763398572110874959981923732463076884528677392654150010269822239401993427482376513231389212353583573566376915572650916866553612366187378959554983566712767093372906030188976220169058025354973622211666504549316958271880975697143546564469806791358707318873075708383345004090151974068325838177531266954177406661392229801349994695941509935655355652985723782153570084089560139142231.738475042362596875449154552392299548947138162081694168675340677843807613129780449323363759027012972466987370921816813162658754726545121090545507240267000456594786540949605260722461937870630634874991729398208026467698131898691830012167897399682179601734569071423681e-733", std::numeric_limits::infinity())); - - std::cout << std::endl; - - std::cout << "======= 32 bits " << std::endl; - Assert(basic_test_32bit("INF",std::numeric_limits::infinity())); - Assert(basic_test_32bit("-INF",-std::numeric_limits::infinity())); - Assert(basic_test_32bit("INFINITY",std::numeric_limits::infinity())); - Assert(basic_test_32bit("-INFINITY",-std::numeric_limits::infinity())); - Assert(basic_test_32bit("infinity",std::numeric_limits::infinity())); - Assert(basic_test_32bit("-infinity",-std::numeric_limits::infinity())); - Assert(basic_test_32bit("inf",std::numeric_limits::infinity())); - Assert(basic_test_32bit("-inf",-std::numeric_limits::infinity())); - Assert(basic_test_32bit("-0",-0.0f)); - Assert(basic_test_32bit("1090544144181609348835077142190",0x1.b877ap+99f)); - Assert(basic_test_32bit("1.1754943508e-38",1.1754943508e-38f)); - Assert(basic_test_32bit("30219.0830078125",30219.0830078125f)); - Assert(basic_test_32bit("16252921.5",16252921.5f)); - Assert(basic_test_32bit("5322519.25",5322519.25f)); - Assert(basic_test_32bit("3900245.875",3900245.875f)); - Assert(basic_test_32bit("1510988.3125",1510988.3125f)); - Assert(basic_test_32bit("782262.28125",782262.28125f)); - Assert(basic_test_32bit("328381.484375",328381.484375f)); - Assert(basic_test_32bit("156782.0703125",156782.0703125f)); - Assert(basic_test_32bit("85003.24609375",85003.24609375f)); - Assert(basic_test_32bit("43827.048828125",43827.048828125f)); - Assert(basic_test_32bit("17419.6494140625",17419.6494140625f)); - Assert(basic_test_32bit("15498.36376953125",15498.36376953125f)); - Assert(basic_test_32bit("6318.580322265625",6318.580322265625f)); - Assert(basic_test_32bit("2525.2840576171875",2525.2840576171875f)); - Assert(basic_test_32bit("1370.9265747070312",1370.9265747070312f)); - Assert(basic_test_32bit("936.3702087402344",936.3702087402344f)); - Assert(basic_test_32bit("411.88682556152344",411.88682556152344f)); - Assert(basic_test_32bit("206.50310516357422",206.50310516357422f)); - Assert(basic_test_32bit("124.16878890991211",124.16878890991211f)); - Assert(basic_test_32bit("50.811574935913086",50.811574935913086f)); - Assert(basic_test_32bit("17.486443519592285",17.486443519592285f)); - Assert(basic_test_32bit("13.91745138168335",13.91745138168335f)); - Assert(basic_test_32bit("7.5464513301849365",0x1.e2f90ep+2f)); - Assert(basic_test_32bit("2.687217116355896",2.687217116355896f)); - Assert(basic_test_32bit("1.1877630352973938",0x1.30113ep+0f)); - Assert(basic_test_32bit("0.7622503340244293",0.7622503340244293f)); - Assert(basic_test_32bit("0.30531780421733856",0x1.38a53ap-2f)); - Assert(basic_test_32bit("0.21791061013936996",0x1.be47eap-3f)); - Assert(basic_test_32bit("0.09289376810193062",0x1.7c7e2ep-4f)); - Assert(basic_test_32bit("0.03706067614257336",0.03706067614257336f)); - Assert(basic_test_32bit("0.028068351559340954",0.028068351559340954f)); - Assert(basic_test_32bit("0.012114629615098238",0x1.8cf8e2p-7f)); - Assert(basic_test_32bit("0.004221370676532388",0x1.14a6dap-8f)); - Assert(basic_test_32bit("0.002153817447833717",0.002153817447833717f)); - Assert(basic_test_32bit("0.0015924838953651488",0x1.a175cap-10f)); - Assert(basic_test_32bit("0.0008602388261351734",0.0008602388261351734f)); - Assert(basic_test_32bit("0.00036393293703440577",0x1.7d9c82p-12f)); - Assert(basic_test_32bit("0.00013746770127909258",0.00013746770127909258f)); - Assert(basic_test_32bit("16407.9462890625", 16407.9462890625f)); - Assert(basic_test_32bit("1.1754947011469036e-38", 0x1.000006p-126f)); - Assert(basic_test_32bit("7.0064923216240854e-46", 0x1p-149f)); - Assert(basic_test_32bit("8388614.5", 8388614.5f)); - Assert(basic_test_32bit("0e9999999999999999999999999999", 0)); - Assert(basic_test_32bit("1234456789012345678901234567890e9999999999999999999999999999", std::numeric_limits::infinity())); - Assert(basic_test_32bit("4.7019774032891500318749461488889827112746622270883500860350068251e-38",4.7019774032891500318749461488889827112746622270883500860350068251e-38f)); - Assert(basic_test_32bit("3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679", 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679f)); - Assert(basic_test_32bit("2.3509887016445750159374730744444913556373311135441750430175034126e-38", 2.3509887016445750159374730744444913556373311135441750430175034126e-38f)); - Assert(basic_test_32bit("+1", 1)); - Assert(basic_test_32bit("2e3000", std::numeric_limits::infinity())); - Assert(basic_test_32bit("3.5028234666e38", std::numeric_limits::infinity())); - Assert(basic_test_32bit("7.0060e-46", 0)); - Assert(basic_test_32bit(1.00000006e+09f)); - Assert(basic_test_32bit(1.4012984643e-45f)); - Assert(basic_test_32bit(1.1754942107e-38f)); - Assert(basic_test_32bit(1.1754943508e-45f)); - Assert(basic_test_32bit("3.4028234664e38", 0x1.fffffep+127f)); - Assert(basic_test_32bit("3.4028234665e38", 0x1.fffffep+127f)); - Assert(basic_test_32bit("3.4028234666e38", 0x1.fffffep+127f)); - Assert(basic_test_32bit("0.000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625", 0.000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625)); - Assert(basic_test_32bit("0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125", 0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125f)); - Assert(basic_test_32bit("0.00000000000000000000000000000000000002350988561514728583455765982071533026645717985517980855365926236850006129930346077117064851336181163787841796875", 0.00000000000000000000000000000000000002350988561514728583455765982071533026645717985517980855365926236850006129930346077117064851336181163787841796875f)); - Assert(basic_test_32bit("0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875", 0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875f)); - std::cout << std::endl; - - Assert(powers_of_ten()); - - std::cout << "All ok" << std::endl; - return EXIT_SUCCESS; +} + + +template std::string to_string(T d) { + std::string s(64, '\0'); + auto written = std::snprintf(&s[0], s.size(), "%.*e", + std::numeric_limits::max_digits10 - 1, d); + s.resize(size_t(written)); + return s; +} + +template std::string to_long_string(T d) { + std::string s(4096, '\0'); + auto written = std::snprintf(&s[0], s.size(), "%.*e", + std::numeric_limits::max_digits10 * 10, d); + s.resize(size_t(written)); + return s; +} + +uint32_t get_mantissa(float f) { + uint32_t m; + memcpy(&m, &f, sizeof(f)); + return (m & ((uint32_t(1)<<23)-1)); +} + +uint64_t get_mantissa(double f) { + uint64_t m; + memcpy(&m, &f, sizeof(f)); + return (m & ((uint64_t(1)<<57)-1)); +} + + +template +void basic_test(std::string str, T expected) { + T actual; + auto result = fast_float::from_chars(str.data(), str.data() + str.size(), actual); + INFO("str=" << str << "\n" + << " expected=" << fHexAndDec(expected) << "\n" + << " ..actual=" << fHexAndDec(actual) << "\n" + << " expected mantissa=" << iHexAndDec(get_mantissa(expected)) << "\n" + << " ..actual mantissa=" << iHexAndDec(get_mantissa(actual))); + CHECK_EQ(result.ec, std::errc()); + CHECK_EQ(copysign(1, actual), copysign(1, expected)); + CHECK_EQ(std::isnan(actual), std::isnan(expected)); + CHECK_EQ(actual, expected); +} + +void basic_test(float val) { + { + std::string long_vals = to_long_string(val); + INFO("long vals: " << long_vals); + basic_test(long_vals, val); + } + { + std::string vals = to_string(val); + INFO("vals: " << vals); + basic_test(vals, val); + } +} + +#define verify(lhs, rhs) { INFO(lhs); basic_test(lhs, rhs); } +#define verify32(val) { INFO(#val); basic_test(val); } + +TEST_CASE("64bit.inf") { + verify("INF", std::numeric_limits::infinity()); + verify("-INF", -std::numeric_limits::infinity()); + verify("INFINITY", std::numeric_limits::infinity()); + verify("-INFINITY", -std::numeric_limits::infinity()); + verify("infinity", std::numeric_limits::infinity()); + verify("-infinity", -std::numeric_limits::infinity()); + verify("inf", std::numeric_limits::infinity()); + verify("-inf", -std::numeric_limits::infinity()); + verify("1234456789012345678901234567890e9999999999999999999999999999", std::numeric_limits::infinity()); + verify("-2139879401095466344511101915470454744.9813888656856943E+272", -std::numeric_limits::infinity()); + verify("1.8e308", std::numeric_limits::infinity()); + verify("1.832312213213213232132132143451234453123412321321312e308", std::numeric_limits::infinity()); + verify("2e30000000000000000", std::numeric_limits::infinity()); + verify("2e3000", std::numeric_limits::infinity()); + verify("1.9e308", std::numeric_limits::infinity()); +} + +TEST_CASE("64bit.general") { + verify("1.1920928955078125e-07", 1.1920928955078125e-07); + verify("9355950000000000000.00000000000000000000000000000000001844674407370955161600000184467440737095516161844674407370955161407370955161618446744073709551616000184467440737095516166000001844674407370955161618446744073709551614073709551616184467440737095516160001844674407370955161601844674407370955674451616184467440737095516140737095516161844674407370955161600018446744073709551616018446744073709551611616000184467440737095001844674407370955161600184467440737095516160018446744073709551168164467440737095516160001844073709551616018446744073709551616184467440737095516160001844674407536910751601611616000184467440737095001844674407370955161600184467440737095516160018446744073709551616184467440737095516160001844955161618446744073709551616000184467440753691075160018446744073709",0x1.03ae05e8fca1cp+63); + verify("-0",-0.0); + verify("2.22507385850720212418870147920222032907240528279439037814303133837435107319244194686754406432563881851382188218502438069999947733013005649884107791928741341929297200970481951993067993290969042784064731682041565926728632933630474670123316852983422152744517260835859654566319282835244787787799894310779783833699159288594555213714181128458251145584319223079897504395086859412457230891738946169368372321191373658977977723286698840356390251044443035457396733706583981055420456693824658413747607155981176573877626747665912387199931904006317334709003012790188175203447190250028061277777916798391090578584006464715943810511489154282775041174682194133952466682503431306181587829379004205392375072083366693241580002758391118854188641513168478436313080237596295773983001708984375e-308", 0x1.0000000000002p-1022); + verify("1.0000000000000006661338147750939242541790008544921875",1.0000000000000007); + verify("1090544144181609348835077142190",0x1.b8779f2474dfbp+99); + verify("2.2250738585072013e-308", 2.2250738585072013e-308); + verify("-92666518056446206563E3", -92666518056446206563E3); + verify("-92666518056446206563E3", -92666518056446206563E3); + verify("-42823146028335318693e-128",-42823146028335318693e-128); + verify("90054602635948575728E72",90054602635948575728E72); + verify("1.00000000000000188558920870223463870174566020691753515394643550663070558368373221972569761144603605635692374830246134201063722058e-309", 1.00000000000000188558920870223463870174566020691753515394643550663070558368373221972569761144603605635692374830246134201063722058e-309); + verify("0e9999999999999999999999999999", 0.0); + verify("-2402844368454405395.2", -2402844368454405395.2); + verify("2402844368454405395.2", 2402844368454405395.2); + verify("7.0420557077594588669468784357561207962098443483187940792729600000e+59", 7.0420557077594588669468784357561207962098443483187940792729600000e+59); + verify("7.0420557077594588669468784357561207962098443483187940792729600000e+59", 7.0420557077594588669468784357561207962098443483187940792729600000e+59); + verify("-1.7339253062092163730578609458683877051596800000000000000000000000e+42", -1.7339253062092163730578609458683877051596800000000000000000000000e+42); + verify("-2.0972622234386619214559824785284023792871122537545728000000000000e+52", -2.0972622234386619214559824785284023792871122537545728000000000000e+52); + verify("-1.0001803374372191849407179462120053338028379051879898808320000000e+57", -1.0001803374372191849407179462120053338028379051879898808320000000e+57); + verify("-1.8607245283054342363818436991534856973992070520151142825984000000e+58", -1.8607245283054342363818436991534856973992070520151142825984000000e+58); + verify("-1.9189205311132686907264385602245237137907390376574976000000000000e+52", -1.9189205311132686907264385602245237137907390376574976000000000000e+52); + verify("-2.8184483231688951563253238886553506793085187889855201280000000000e+54", -2.8184483231688951563253238886553506793085187889855201280000000000e+54); + verify("-1.7664960224650106892054063261344555646357024359107788800000000000e+53", -1.7664960224650106892054063261344555646357024359107788800000000000e+53); + verify("-2.1470977154320536489471030463761883783915110400000000000000000000e+45", -2.1470977154320536489471030463761883783915110400000000000000000000e+45); + verify("-4.4900312744003159009338275160799498340862630046359789166919680000e+61", -4.4900312744003159009338275160799498340862630046359789166919680000e+61); + verify("+1", 1.0); + verify("1.797693134862315700000000000000001e308", 1.7976931348623157e308); + verify("3e-324", 0x0.0000000000001p-1022); + verify("1.00000006e+09", 0x1.dcd651ep+29); + verify("4.9406564584124653e-324", 0x0.0000000000001p-1022); + verify("4.9406564584124654e-324", 0x0.0000000000001p-1022); + verify("2.2250738585072009e-308", 0x0.fffffffffffffp-1022); + verify("2.2250738585072014e-308", 0x1p-1022); + verify("1.7976931348623157e308", 0x1.fffffffffffffp+1023); + verify("1.7976931348623158e308", 0x1.fffffffffffffp+1023); + verify("4503599627370496.5", 4503599627370496.5); + verify("4503599627475352.5", 4503599627475352.5); + verify("4503599627475353.5", 4503599627475353.5); + verify("2251799813685248.25", 2251799813685248.25); + verify("1125899906842624.125", 1125899906842624.125); + verify("1125899906842901.875", 1125899906842901.875); + verify("2251799813685803.75", 2251799813685803.75); + verify("4503599627370497.5", 4503599627370497.5); + verify("45035996.273704995", 45035996.273704995); + verify("45035996.273704985", 45035996.273704985); + verify("0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044501477170144022721148195934182639518696390927032912960468522194496444440421538910330590478162701758282983178260792422137401728773891892910553144148156412434867599762821265346585071045737627442980259622449029037796981144446145705102663115100318287949527959668236039986479250965780342141637013812613333119898765515451440315261253813266652951306000184917766328660755595837392240989947807556594098101021612198814605258742579179000071675999344145086087205681577915435923018910334964869420614052182892431445797605163650903606514140377217442262561590244668525767372446430075513332450079650686719491377688478005309963967709758965844137894433796621993967316936280457084866613206797017728916080020698679408551343728867675409720757232455434770912461317493580281734466552734375", 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044501477170144022721148195934182639518696390927032912960468522194496444440421538910330590478162701758282983178260792422137401728773891892910553144148156412434867599762821265346585071045737627442980259622449029037796981144446145705102663115100318287949527959668236039986479250965780342141637013812613333119898765515451440315261253813266652951306000184917766328660755595837392240989947807556594098101021612198814605258742579179000071675999344145086087205681577915435923018910334964869420614052182892431445797605163650903606514140377217442262561590244668525767372446430075513332450079650686719491377688478005309963967709758965844137894433796621993967316936280457084866613206797017728916080020698679408551343728867675409720757232455434770912461317493580281734466552734375); + verify("0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375", 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375); + verify("1438456663141390273526118207642235581183227845246331231162636653790368152091394196930365828634687637948157940776599182791387527135353034738357134110310609455693900824193549772792016543182680519740580354365467985440183598701312257624545562331397018329928613196125590274187720073914818062530830316533158098624984118889298281371812288789537310599037529113415438738954894752124724983067241108764488346454376699018673078404751121414804937224240805993123816932326223683090770561597570457793932985826162604255884529134126396282202126526253389383421806727954588525596114379801269094096329805054803089299736996870951258573010877404407451953846698609198213926882692078557033228265259305481198526059813164469187586693257335779522020407645498684263339921905227556616698129967412891282231685504660671277927198290009824680186319750978665734576683784255802269708917361719466043175201158849097881370477111850171579869056016061666173029059588433776015644439705050377554277696143928278093453792803846252715966016733222646442382892123940052441346822429721593884378212558701004356924243030059517489346646577724622498919752597382095222500311124181823512251071356181769376577651390028297796156208815375089159128394945710515861334486267101797497111125909272505194792870889617179758703442608016143343262159998149700606597792535574457560429226974273443630323818747730771316763398572110874959981923732463076884528677392654150010269822239401993427482376513231389212353583573566376915572650916866553612366187378959554983566712767093372906030188976220169058025354973622211666504549316958271880975697143546564469806791358707318873075708383345004090151974068325838177531266954177406661392229801349994695941509935655355652985723782153570084089560139142231.738475042362596875449154552392299548947138162081694168675340677843807613129780449323363759027012972466987370921816813162658754726545121090545507240267000456594786540949605260722461937870630634874991729398208026467698131898691830012167897399682179601734569071423681e-733", std::numeric_limits::infinity()); +} + + +TEST_CASE("32bit.inf") { + verify("INF", std::numeric_limits::infinity()); + verify("-INF", -std::numeric_limits::infinity()); + verify("INFINITY", std::numeric_limits::infinity()); + verify("-INFINITY", -std::numeric_limits::infinity()); + verify("infinity", std::numeric_limits::infinity()); + verify("-infinity", -std::numeric_limits::infinity()); + verify("inf", std::numeric_limits::infinity()); + verify("-inf", -std::numeric_limits::infinity()); + verify("1234456789012345678901234567890e9999999999999999999999999999", std::numeric_limits::infinity()); + verify("2e3000", std::numeric_limits::infinity()); + verify("3.5028234666e38", std::numeric_limits::infinity()); +} + +TEST_CASE("32bit.general") { + verify32(1.00000006e+09f); + verify32(1.4012984643e-45f); + verify32(1.1754942107e-38f); + verify32(1.1754943508e-45f); + verify("-0", -0.0f); + verify("1090544144181609348835077142190", 0x1.b877ap+99f); + verify("1.1754943508e-38", 1.1754943508e-38f); + verify("30219.0830078125", 30219.0830078125f); + verify("16252921.5", 16252921.5f); + verify("5322519.25", 5322519.25f); + verify("3900245.875", 3900245.875f); + verify("1510988.3125", 1510988.3125f); + verify("782262.28125", 782262.28125f); + verify("328381.484375", 328381.484375f); + verify("156782.0703125", 156782.0703125f); + verify("85003.24609375", 85003.24609375f); + verify("43827.048828125", 43827.048828125f); + verify("17419.6494140625", 17419.6494140625f); + verify("15498.36376953125", 15498.36376953125f); + verify("6318.580322265625", 6318.580322265625f); + verify("2525.2840576171875", 2525.2840576171875f); + verify("1370.9265747070312", 1370.9265747070312f); + verify("936.3702087402344", 936.3702087402344f); + verify("411.88682556152344", 411.88682556152344f); + verify("206.50310516357422", 206.50310516357422f); + verify("124.16878890991211", 124.16878890991211f); + verify("50.811574935913086", 50.811574935913086f); + verify("17.486443519592285", 17.486443519592285f); + verify("13.91745138168335", 13.91745138168335f); + verify("7.5464513301849365", 0x1.e2f90ep+2f); + verify("2.687217116355896", 2.687217116355896f); + verify("1.1877630352973938", 0x1.30113ep+0f); + verify("0.7622503340244293", 0.7622503340244293f); + verify("0.30531780421733856", 0x1.38a53ap-2f); + verify("0.21791061013936996", 0x1.be47eap-3f); + verify("0.09289376810193062", 0x1.7c7e2ep-4f); + verify("0.03706067614257336", 0.03706067614257336f); + verify("0.028068351559340954", 0.028068351559340954f); + verify("0.012114629615098238", 0x1.8cf8e2p-7f); + verify("0.004221370676532388", 0x1.14a6dap-8f); + verify("0.002153817447833717", 0.002153817447833717f); + verify("0.0015924838953651488", 0x1.a175cap-10f); + verify("0.0008602388261351734", 0.0008602388261351734f); + verify("0.00036393293703440577", 0x1.7d9c82p-12f); + verify("0.00013746770127909258", 0.00013746770127909258f); + verify("16407.9462890625", 16407.9462890625f); + verify("1.1754947011469036e-38", 0x1.000006p-126f); + verify("7.0064923216240854e-46", 0x1p-149f); + verify("8388614.5", 8388614.5f); + verify("0e9999999999999999999999999999", 0.f); + verify("4.7019774032891500318749461488889827112746622270883500860350068251e-38",4.7019774032891500318749461488889827112746622270883500860350068251e-38f); + verify("3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679", 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679f); + verify("2.3509887016445750159374730744444913556373311135441750430175034126e-38", 2.3509887016445750159374730744444913556373311135441750430175034126e-38f); + verify("+1", 1.f); + verify("7.0060e-46", 0.f); + verify("3.4028234664e38", 0x1.fffffep+127f); + verify("3.4028234665e38", 0x1.fffffep+127f); + verify("3.4028234666e38", 0x1.fffffep+127f); + verify("0.000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625", 0.000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625); + verify("0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125", 0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125f); + verify("0.00000000000000000000000000000000000002350988561514728583455765982071533026645717985517980855365926236850006129930346077117064851336181163787841796875", 0.00000000000000000000000000000000000002350988561514728583455765982071533026645717985517980855365926236850006129930346077117064851336181163787841796875f); + verify("0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875", 0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875f); } From aca3ca782848f4937c51644cdda0611b45bf2981 Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Tue, 17 Nov 2020 02:07:31 +0000 Subject: [PATCH 27/47] fix CI files: ubuntu required a more recent cmake, vs had a typo --- .github/workflows/ubuntu18.yml | 2 +- .github/workflows/ubuntu20.yml | 2 +- .github/workflows/vs16-ci.yml | 4 +++- .github/workflows/vs16-clang-ci.yml | 4 +++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ubuntu18.yml b/.github/workflows/ubuntu18.yml index bf1f2d0..2636550 100644 --- a/.github/workflows/ubuntu18.yml +++ b/.github/workflows/ubuntu18.yml @@ -18,7 +18,7 @@ jobs: - name: Setup cmake uses: jwlawson/actions-setup-cmake@v1.0 with: - cmake-version: '3.9.x' + cmake-version: '3.12.x' - name: Install older compilers run: | sudo -E dpkg --add-architecture i386 diff --git a/.github/workflows/ubuntu20.yml b/.github/workflows/ubuntu20.yml index 6bf0dcc..883aab4 100644 --- a/.github/workflows/ubuntu20.yml +++ b/.github/workflows/ubuntu20.yml @@ -17,7 +17,7 @@ jobs: - name: Setup cmake uses: jwlawson/actions-setup-cmake@v1.0 with: - cmake-version: '3.9.x' + cmake-version: '3.12.x' - name: install older compilers run: | sudo -E dpkg --add-architecture i386 diff --git a/.github/workflows/vs16-ci.yml b/.github/workflows/vs16-ci.yml index 8548aba..eb4330b 100644 --- a/.github/workflows/vs16-ci.yml +++ b/.github/workflows/vs16-ci.yml @@ -22,4 +22,6 @@ jobs: - name: Build run: cmake --build build --config Release --parallel - name: Run basic tests - run: cd build && ctest -C Release --output-on-failure -R basictest + run: | + cd build + ctest -C Release --output-on-failure -R basictest diff --git a/.github/workflows/vs16-clang-ci.yml b/.github/workflows/vs16-clang-ci.yml index 559630d..a32c6b8 100644 --- a/.github/workflows/vs16-clang-ci.yml +++ b/.github/workflows/vs16-clang-ci.yml @@ -22,4 +22,6 @@ jobs: - name: Build run: cmake --build build --config Release --parallel - name: Run basic tests - run: cd build && ctest -C Release --output-on-failure -R basictest + run: | + cd build + ctest -C Release --output-on-failure -R basictest From 3566d303f4af292e1d7695b2141ea11ebf995b65 Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Tue, 17 Nov 2020 02:13:25 +0000 Subject: [PATCH 28/47] cmake: fetch content requires v3.11+ --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6be510f..70c4b19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.11 REQUIRED) project(fast_float VERSION 0.1.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 11) @@ -31,4 +31,4 @@ endif() if(FASTFLOAT_TEST) enable_testing() add_subdirectory(tests) -endif(FASTFLOAT_TEST) \ No newline at end of file +endif(FASTFLOAT_TEST) From 2bc8dcc734047125941829976e8e545f88c8604f Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Tue, 17 Nov 2020 02:14:24 +0000 Subject: [PATCH 29/47] fix: jwlawson/actions-setup-cmake uses deprecated github commands see https://github.com/lemire/fast_float/pull/36/checks?check_run_id=1409838155#step:3:13 --- .github/workflows/ubuntu18.yml | 4 ---- .github/workflows/ubuntu20.yml | 4 ---- CMakeLists.txt | 2 +- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/ubuntu18.yml b/.github/workflows/ubuntu18.yml index 2636550..4f06fa5 100644 --- a/.github/workflows/ubuntu18.yml +++ b/.github/workflows/ubuntu18.yml @@ -15,10 +15,6 @@ jobs: - {cxx: , arch: -DCMAKE_CXX_FLAGS="-m32"} # default=gcc7 steps: - uses: actions/checkout@v2 - - name: Setup cmake - uses: jwlawson/actions-setup-cmake@v1.0 - with: - cmake-version: '3.12.x' - name: Install older compilers run: | sudo -E dpkg --add-architecture i386 diff --git a/.github/workflows/ubuntu20.yml b/.github/workflows/ubuntu20.yml index 883aab4..11ac3a4 100644 --- a/.github/workflows/ubuntu20.yml +++ b/.github/workflows/ubuntu20.yml @@ -14,10 +14,6 @@ jobs: - {cxx: , arch: -DCMAKE_CXX_FLAGS="-m32"} # default=gcc9 steps: - uses: actions/checkout@v2 - - name: Setup cmake - uses: jwlawson/actions-setup-cmake@v1.0 - with: - cmake-version: '3.12.x' - name: install older compilers run: | sudo -E dpkg --add-architecture i386 diff --git a/CMakeLists.txt b/CMakeLists.txt index 70c4b19..4be7a45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.11 REQUIRED) +cmake_minimum_required(VERSION 3.11 FATAL_ERROR) project(fast_float VERSION 0.1.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 11) From 216d191384f913703ce77295523692e23fa90bca Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Thu, 19 Nov 2020 21:54:13 +0000 Subject: [PATCH 30/47] basictest: do not print double 0x --- tests/basictest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index a699198..fc42681 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -17,7 +17,7 @@ TEST_CASE("leading_zeroes") { } #define iHexAndDec(v) std::hex << "0x" << (v) << " (" << std::dec << (v) << ")" -#define fHexAndDec(v) std::hexfloat << "0x" << (v) << " (" << std::defaultfloat << (v) << ")" +#define fHexAndDec(v) std::hexfloat << (v) << " (" << std::defaultfloat << (v) << ")" void test_full_multiplication(uint64_t lhs, uint64_t rhs, uint64_t expected_lo, uint64_t expected_hi) { fast_float::value128 v; From 8a04a06a88997c624a746eaf81c0b6e9e183884b Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Thu, 19 Nov 2020 22:55:48 +0000 Subject: [PATCH 31/47] leading_zeroes(): 0 is not a valid input --- include/fast_float/float_common.h | 30 ++++++++++++++---------------- tests/basictest.cpp | 1 - 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 2c0c18e..0f17329 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -3,6 +3,7 @@ #include #include +#include #if defined(_MSC_VER) && !defined(__clang__) #define FASTFLOAT_VISUAL_STUDIO 1 @@ -89,26 +90,23 @@ struct value128 { /* result might be undefined when input_num is zero */ fastfloat_really_inline int leading_zeroes(uint64_t input_num) { + assert(input_num > 0); #ifdef FASTFLOAT_VISUAL_STUDIO -#if defined(_M_X64) || defined(_M_ARM64) + #if defined(_M_X64) || defined(_M_ARM64) unsigned long leading_zero = 0; // Search the mask data from most significant bit (MSB) // to least significant bit (LSB) for a set bit (1). - if (_BitScanReverse64(&leading_zero, input_num)) - return (int)(63 - leading_zero); - else - return 64; -#else - if(!input_num) return 64; - int n = 0; - if(input_num & uint64_t(0xffffffff00000000)) input_num >>= 32, n |= 32; - if(input_num & uint64_t( 0xffff0000)) input_num >>= 16, n |= 16; - if(input_num & uint64_t( 0xff00)) input_num >>= 8, n |= 8; - if(input_num & uint64_t( 0xf0)) input_num >>= 4, n |= 4; - if(input_num & uint64_t( 0xc)) input_num >>= 2, n |= 2; - if(input_num & uint64_t( 0x2)) input_num >>= 1, n |= 1; - return 63 - n; -#endif + return (int)(_BitScanReverse64(&leading_zero, input_num) - leading_zero); + #else + int n = 0; + if(input_num & uint64_t(0xffffffff00000000)) input_num >>= 32, n |= 32; + if(input_num & uint64_t( 0xffff0000)) input_num >>= 16, n |= 16; + if(input_num & uint64_t( 0xff00)) input_num >>= 8, n |= 8; + if(input_num & uint64_t( 0xf0)) input_num >>= 4, n |= 4; + if(input_num & uint64_t( 0xc)) input_num >>= 2, n |= 2; + if(input_num & uint64_t( 0x2)) input_num >>= 1, n |= 1; + return 63 - n; + #endif #else return __builtin_clzll(input_num); #endif diff --git a/tests/basictest.cpp b/tests/basictest.cpp index fc42681..a2f3110 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -7,7 +7,6 @@ TEST_CASE("leading_zeroes") { constexpr const uint64_t bit = 1; - CHECK(fast_float::leading_zeroes(0) == 64); CHECK(fast_float::leading_zeroes(bit << 0) == 63); CHECK(fast_float::leading_zeroes(bit << 1) == 62); CHECK(fast_float::leading_zeroes(bit << 2) == 61); From 5ce64de5243d0cfbded8ec6b66804db50ff786b2 Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Thu, 19 Nov 2020 23:22:55 +0000 Subject: [PATCH 32/47] fix: full 64bit multiplication working on 32bit gcc/clang --- .gitignore | 4 +- include/fast_float/float_common.h | 74 +++++++++++++------------------ 2 files changed, 34 insertions(+), 44 deletions(-) diff --git a/.gitignore b/.gitignore index a5a3ead..1566557 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ build/* -Testing/* \ No newline at end of file +Testing/* +.cache/ +compile_commands.json diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 0f17329..7e84b01 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -112,19 +112,29 @@ fastfloat_really_inline int leading_zeroes(uint64_t input_num) { #endif } -#if defined(_WIN32) && !defined(__clang__) -// Note MinGW falls here too -#include -#if !defined(_M_X64) && !defined(_M_ARM64) // _umul128 for x86, arm -// this is a slow emulation routine for 32-bit Windows - -#ifdef __MINGW32__ -fastfloat_really_inline uint64_t __emulu(uint32_t x, uint32_t y) { - return x * (uint64_t)y; -} +#if (defined(__i386) || defined(__i386__) || defined(_M_IX86) \ + || defined(__arm__) || defined(__MINGW32__)) +#define FASTFLOAT_32BIT +#elif (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \ + || defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64)) +#define FASTFLOAT_64BIT +#else +#error Unknown platform #endif + +#ifdef FASTFLOAT_32BIT +#if (defined(_WIN32) && !defined(__clang__)) +#include +#endif + +// slow emulation routine for 32-bit +fastfloat_really_inline uint64_t __emulu(uint32_t x, uint32_t y) { + return x * (uint64_t)y; +} + +// slow emulation routine for 32-bit fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); @@ -136,51 +146,29 @@ fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd, (adbc_carry << 32) + !!(lo < bd); return lo; } -#endif +#endif // FASTFLOAT_32BIT -fastfloat_really_inline value128 full_multiplication(uint64_t value1, - uint64_t value2) { - value128 answer; -#ifdef _M_ARM64 - // ARM64 has native support for 64-bit multiplications, no need to emultate - answer.high = __umulh(value1, value2); - answer.low = value1 * value2; -#else - answer.low = - _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64 -#endif // _M_ARM64 - return answer; -} -#else // gcc - -// compute value1 * value2 +// compute 64-bit a*b fastfloat_really_inline value128 full_multiplication(uint64_t a, uint64_t b) { value128 answer; -#if defined(__i386) || defined(__i386__) || defined(_M_IX86) || defined(__arm__) - static constexpr const uint64_t lo32 = 0xffffffffu; - // https://stackoverflow.com/questions/28868367/getting-the-high-part-of-64-bit-integer-multiplication - uint64_t a_lo = a & lo32; - uint64_t a_hi = a >> 32; - uint64_t b_lo = b & lo32; - uint64_t b_hi = b >> 32; - uint64_t ab_hi = a_hi * b_hi; - uint64_t ab_mid = a_hi * b_lo; - uint64_t ba_mid = b_hi * a_lo; - uint64_t ab_lo = a_lo * b_lo; - uint64_t carry_bit = ((ab_mid & lo32) + (ba_mid & lo32) + (ab_lo >> 32)) >> 32; - answer.high = ab_hi + (ab_mid >> 32) + (ba_mid >> 32) + carry_bit; - answer.low = ab_lo + (ab_mid & lo32) + (ba_mid & lo32); -#else // if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64) || defined(__aarch64__) || defined(_M_ARM64) +#ifdef _M_ARM64 + // ARM64 has native support for 64-bit multiplications, no need to emulate + answer.high = __umulh(a, b); + answer.low = a * b; +#elif defined(FASTFLOAT_32BIT) + answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64 +#elif defined(FASTFLOAT_64BIT) __uint128_t r = ((__uint128_t)a) * b; answer.low = uint64_t(r); answer.high = uint64_t(r >> 64); +#else + #error Not implemented #endif return answer; } -#endif struct adjusted_mantissa { uint64_t mantissa{0}; From f7b13da349abf03c4f2b091f7d13ec78a650fcc6 Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Fri, 20 Nov 2020 00:16:10 +0000 Subject: [PATCH 33/47] fix: readjust full_multiplication() and leading_zeroes() on windows --- include/fast_float/float_common.h | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 7e84b01..df16ec3 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -96,16 +96,17 @@ fastfloat_really_inline int leading_zeroes(uint64_t input_num) { unsigned long leading_zero = 0; // Search the mask data from most significant bit (MSB) // to least significant bit (LSB) for a set bit (1). - return (int)(_BitScanReverse64(&leading_zero, input_num) - leading_zero); + _BitScanReverse64(&leading_zero, input_num); + return (int)(63 - leading_zero); #else - int n = 0; - if(input_num & uint64_t(0xffffffff00000000)) input_num >>= 32, n |= 32; - if(input_num & uint64_t( 0xffff0000)) input_num >>= 16, n |= 16; - if(input_num & uint64_t( 0xff00)) input_num >>= 8, n |= 8; - if(input_num & uint64_t( 0xf0)) input_num >>= 4, n |= 4; - if(input_num & uint64_t( 0xc)) input_num >>= 2, n |= 2; - if(input_num & uint64_t( 0x2)) input_num >>= 1, n |= 1; - return 63 - n; + int last_bit = 0; + if(input_num & uint64_t(0xffffffff00000000)) input_num >>= 32, last_bit |= 32; + if(input_num & uint64_t( 0xffff0000)) input_num >>= 16, last_bit |= 16; + if(input_num & uint64_t( 0xff00)) input_num >>= 8, last_bit |= 8; + if(input_num & uint64_t( 0xf0)) input_num >>= 4, last_bit |= 4; + if(input_num & uint64_t( 0xc)) input_num >>= 2, last_bit |= 2; + if(input_num & uint64_t( 0x2)) input_num >>= 1, last_bit |= 1; + return 63 - last_bit; #endif #else return __builtin_clzll(input_num); @@ -124,15 +125,18 @@ fastfloat_really_inline int leading_zeroes(uint64_t input_num) { #endif -#ifdef FASTFLOAT_32BIT -#if (defined(_WIN32) && !defined(__clang__)) +#if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__)) #include #endif +#ifdef FASTFLOAT_32BIT + +#if !defined(_WIN32) // slow emulation routine for 32-bit fastfloat_really_inline uint64_t __emulu(uint32_t x, uint32_t y) { return x * (uint64_t)y; } +#endif // slow emulation routine for 32-bit fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd, @@ -157,7 +161,7 @@ fastfloat_really_inline value128 full_multiplication(uint64_t a, // ARM64 has native support for 64-bit multiplications, no need to emulate answer.high = __umulh(a, b); answer.low = a * b; -#elif defined(FASTFLOAT_32BIT) +#elif defined(FASTFLOAT_32BIT) || (defined(_WIN64)) answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64 #elif defined(FASTFLOAT_64BIT) __uint128_t r = ((__uint128_t)a) * b; From bfa33b3ed16dc831d215983e87551120197abf67 Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Fri, 20 Nov 2020 00:26:33 +0000 Subject: [PATCH 34/47] fix mingw compile errors --- include/fast_float/float_common.h | 5 ++++- tests/basictest.cpp | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index df16ec3..d604dec 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -131,7 +131,7 @@ fastfloat_really_inline int leading_zeroes(uint64_t input_num) { #ifdef FASTFLOAT_32BIT -#if !defined(_WIN32) +#if (!defined(_WIN32)) || defined(__MINGW32__) // slow emulation routine for 32-bit fastfloat_really_inline uint64_t __emulu(uint32_t x, uint32_t y) { return x * (uint64_t)y; @@ -139,6 +139,7 @@ fastfloat_really_inline uint64_t __emulu(uint32_t x, uint32_t y) { #endif // slow emulation routine for 32-bit +#if !defined(__MINGW64__) fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); @@ -150,6 +151,8 @@ fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd, (adbc_carry << 32) + !!(lo < bd); return lo; } +#endif // !__MINGW64__ + #endif // FASTFLOAT_32BIT diff --git a/tests/basictest.cpp b/tests/basictest.cpp index a2f3110..d0a7d48 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -1,9 +1,10 @@ -#include "fast_float/fast_float.h" -#include #define DOCTEST_CONFIG_SUPER_FAST_ASSERTS #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include +#include "fast_float/fast_float.h" +#include + TEST_CASE("leading_zeroes") { constexpr const uint64_t bit = 1; From 33b64cf0d3824d2a28e5558a7d9d1786322bfb1f Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Fri, 20 Nov 2020 01:08:53 +0000 Subject: [PATCH 35/47] fix: FetchContent_MakeAvailable() was introduced only in cmake 3.14 --- .travis.yml | 5 +++++ CMakeLists.txt | 2 +- tests/CMakeLists.txt | 4 ++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 616ab64..6f1b5bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,11 @@ language: cpp dist: bionic +addons: + snaps: + - name: cmake + confinement: classic + channel: 3.16/stable arch: - amd64 diff --git a/CMakeLists.txt b/CMakeLists.txt index 4be7a45..2d10096 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.11 FATAL_ERROR) +cmake_minimum_required(VERSION 3.11) project(fast_float VERSION 0.1.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 11) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cce9438..04129d0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,3 +1,7 @@ +# FetchContent_MakeAvailable() was only introduced in 3.14 +# https://cmake.org/cmake/help/v3.14/release/3.14.html#modules +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + include(FetchContent) FetchContent_Declare(doctest From ad22e20e4ccfde1ad40f0c25085158b244dfaf3c Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Thu, 19 Nov 2020 21:37:10 -0500 Subject: [PATCH 36/47] Completing. --- tests/exhaustive32_midpoint.cpp | 6 +++--- tests/powersoffive_hardround.cpp | 23 ++++++++++++++++++++++- tests/random_string.cpp | 8 ++++---- tests/short_random_string.cpp | 6 +++--- tests/string_test.cpp | 6 +++--- 5 files changed, 35 insertions(+), 14 deletions(-) diff --git a/tests/exhaustive32_midpoint.cpp b/tests/exhaustive32_midpoint.cpp index 7c2080f..72469cd 100644 --- a/tests/exhaustive32_midpoint.cpp +++ b/tests/exhaustive32_midpoint.cpp @@ -31,7 +31,7 @@ template char *to_string(T d, char *buffer) { void strtod_from_string(const char * st, float& d) { char *pr = (char *)st; -#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) d = cygwin_strtod_l(st, &pr); #elif defined(_WIN32) static _locale_t c_locale = _create_locale(LC_ALL, "C"); @@ -112,8 +112,8 @@ void allvalues() { } int main() { -#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) - std::cout << "Warning: msys/cygwin detected. This particular test is likely to generate false failures due to our reliance on the underlying runtime library." << std::endl; +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) + std::cout << "Warning: msys/cygwin or solaris detected. This particular test is likely to generate false failures due to our reliance on the underlying runtime library." << std::endl; #endif allvalues(); std::cout << std::endl; diff --git a/tests/powersoffive_hardround.cpp b/tests/powersoffive_hardround.cpp index eea82bf..9288beb 100644 --- a/tests/powersoffive_hardround.cpp +++ b/tests/powersoffive_hardround.cpp @@ -5,6 +5,27 @@ #include #include + +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) +// Anything at all that is related to cygwin, msys and so forth will +// always use this fallback because we cannot rely on it behaving as normal +// gcc. +#include +#include +// workaround for CYGWIN +double cygwin_strtod_l(const char* start, char** end) { + double d; + std::stringstream ss; + ss.imbue(std::locale::classic()); + ss << start; + ss >> d; + size_t nread = ss.tellg(); + *end = const_cast(start) + nread; + return d; +} +#endif + + std::pair strtod_from_string(const char *st) { double d; char *pr; @@ -25,7 +46,7 @@ std::pair strtod_from_string(const char *st) { std::pair strtof_from_string(char *st) { float d; char *pr; -#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) d = cygwin_strtod_l(st, &pr); #elif defined(_WIN32) static _locale_t c_locale = _create_locale(LC_ALL, "C"); diff --git a/tests/random_string.cpp b/tests/random_string.cpp index 8663691..a58a092 100644 --- a/tests/random_string.cpp +++ b/tests/random_string.cpp @@ -2,7 +2,7 @@ #include #include -#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) // Anything at all that is related to cygwin, msys and so forth will // always use this fallback because we cannot rely on it behaving as normal // gcc. @@ -126,7 +126,7 @@ std::pair strtod_from_string(char *st) { std::pair strtof_from_string(char *st) { float d; char *pr; -#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) d = cygwin_strtod_l(st, &pr); #elif defined(_WIN32) static _locale_t c_locale = _create_locale(LC_ALL, "C"); @@ -203,8 +203,8 @@ bool tester(uint64_t seed, size_t volume) { } int main() { -#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) - std::cout << "Warning: msys/cygwin detected. This particular test is likely to generate false failures due to our reliance on the underlying runtime library." << std::endl; +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) + std::cout << "Warning: msys/cygwin or solaris detected. This particular test is likely to generate false failures due to our reliance on the underlying runtime library." << std::endl; #endif if (tester(1234344, 100000000)) { std::cout << "All tests ok." << std::endl; diff --git a/tests/short_random_string.cpp b/tests/short_random_string.cpp index 50503cc..0d317b6 100644 --- a/tests/short_random_string.cpp +++ b/tests/short_random_string.cpp @@ -2,7 +2,7 @@ #include #include -#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) // Anything at all that is related to cygwin, msys and so forth will // always use this fallback because we cannot rely on it behaving as normal // gcc. @@ -122,7 +122,7 @@ std::pair strtod_from_string(char *st) { std::pair strtof_from_string(char *st) { float d; char *pr; -#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) d = cygwin_strtod_l(st, &pr); #elif defined(_WIN32) static _locale_t c_locale = _create_locale(LC_ALL, "C"); @@ -199,7 +199,7 @@ bool tester(uint64_t seed, size_t volume) { } int main() { -#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) std::cout << "Warning: msys/cygwin detected. This particular test is likely to generate false failures due to our reliance on the underlying runtime library." << std::endl; #endif if (tester(1234344, 100000000)) { diff --git a/tests/string_test.cpp b/tests/string_test.cpp index 7da2f7b..a6f47e0 100644 --- a/tests/string_test.cpp +++ b/tests/string_test.cpp @@ -2,7 +2,7 @@ #include -#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) // Anything at all that is related to cygwin, msys and so forth will // always use this fallback because we cannot rely on it behaving as normal // gcc. @@ -85,7 +85,7 @@ void strtod_from_string(const std::string &st, double& d) { template <> void strtod_from_string(const std::string &st, float& d) { char *pr = (char *)st.c_str(); -#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) d = cygwin_strtod_l(st.c_str(), &pr); #elif defined(_WIN32) static _locale_t c_locale = _create_locale(LC_ALL, "C"); @@ -236,7 +236,7 @@ bool partow_test() { int main() { -#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) std::cout << "Warning: msys/cygwin detected. This particular test is likely to generate false failures due to our reliance on the underlying runtime library." << std::endl; #endif std::cout << "32 bits checks" << std::endl; From dad8c84c388702089a68f6aec463f5012f8a30ee Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Thu, 19 Nov 2020 21:41:23 -0500 Subject: [PATCH 37/47] Upgrading. --- .github/workflows/ubuntu20.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ubuntu20.yml b/.github/workflows/ubuntu20.yml index 68aa1ee..33c38db 100644 --- a/.github/workflows/ubuntu20.yml +++ b/.github/workflows/ubuntu20.yml @@ -14,7 +14,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Setup cmake - uses: jwlawson/actions-setup-cmake@v1.0 + uses: jwlawson/actions-setup-cmake@v1.4 with: cmake-version: '3.9.x' - name: install older compilers From 1283ea199be3dd5b14612f20a9f1ddd86b456e89 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Thu, 19 Nov 2020 21:45:55 -0500 Subject: [PATCH 38/47] Forgot this. --- .github/workflows/ubuntu18.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ubuntu18.yml b/.github/workflows/ubuntu18.yml index 23850cc..5ee88ff 100644 --- a/.github/workflows/ubuntu18.yml +++ b/.github/workflows/ubuntu18.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Setup cmake - uses: jwlawson/actions-setup-cmake@v1.0 + uses: jwlawson/actions-setup-cmake@v1.4 with: cmake-version: '3.9.x' - name: Install older compilers From d617b97c7ce4c190a25ffb6fb574b4f7cabe8fd1 Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Fri, 20 Nov 2020 09:43:27 +0000 Subject: [PATCH 39/47] update authors --- AUTHORS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 77cd15b..60c9425 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1 +1,2 @@ -Daniel Lemire \ No newline at end of file +Daniel Lemire +João Paulo Magalhaes From 9afc814fb67460d064855a5734b763a2b470c167 Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Fri, 20 Nov 2020 09:44:27 +0000 Subject: [PATCH 40/47] tidy float_common.h: put feature test macros at the top --- include/fast_float/float_common.h | 33 ++++++++++++++----------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index d604dec..8791709 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -5,12 +5,25 @@ #include #include +#if (defined(__i386) || defined(__i386__) || defined(_M_IX86) \ + || defined(__arm__) || defined(__MINGW32__)) +#define FASTFLOAT_32BIT +#elif (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \ + || defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \ + || defined(__MINGW64__)) +#define FASTFLOAT_64BIT +#else +#error Unknown platform +#endif + +#if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__)) +#include +#endif + #if defined(_MSC_VER) && !defined(__clang__) #define FASTFLOAT_VISUAL_STUDIO 1 #endif - - #ifdef _WIN32 #define FASTFLOAT_IS_BIG_ENDIAN 0 #else @@ -113,22 +126,6 @@ fastfloat_really_inline int leading_zeroes(uint64_t input_num) { #endif } - -#if (defined(__i386) || defined(__i386__) || defined(_M_IX86) \ - || defined(__arm__) || defined(__MINGW32__)) -#define FASTFLOAT_32BIT -#elif (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \ - || defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64)) -#define FASTFLOAT_64BIT -#else -#error Unknown platform -#endif - - -#if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__)) -#include -#endif - #ifdef FASTFLOAT_32BIT #if (!defined(_WIN32)) || defined(__MINGW32__) From abd3c2f81e076bbf3c3376655422da5629e12d35 Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Fri, 20 Nov 2020 09:45:36 +0000 Subject: [PATCH 41/47] powers of ten test: do not fail when pow() is broken - merely warn --- tests/basictest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index d0a7d48..2df1b60 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -192,7 +192,7 @@ static const double testing_power_of_ten[] = { TEST_CASE("powers_of_ten") { char buf[1024]; - CHECK_MESSAGE(1e-308 == std::pow(10, -308), "On your system, the pow function is busted. Sorry about that."); + WARN_MESSAGE(1e-308 == std::pow(10, -308), "On your system, the pow function is busted. Sorry about that."); bool is_pow_correct{1e-308 == std::pow(10,-308)}; // large negative values should be zero. int start_point = is_pow_correct ? -1000 : -307; From df6e785d5c416c8f1d3f9144918fb84b78b636c2 Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Fri, 20 Nov 2020 10:01:38 +0000 Subject: [PATCH 42/47] travis: use a recent cmake --- .travis.yml | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6f1b5bf..134149a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,6 @@ language: cpp dist: bionic -addons: - snaps: - - name: cmake - confinement: classic - channel: 3.16/stable arch: - amd64 @@ -168,7 +163,37 @@ before_install: - eval "${COMPILER}" install: - - sudo apt-get -qq update + # make sure we have a recent cmake version. + # fastfloat itself does not require it, but the tests require >= 3.14. + + # these architectures have a snap: + # https://gitlab.kitware.com/cmake/cmake/-/issues/20122 + - sudo -E apt-get -y update + - sudo -E apt-get -y install snapd + - sudo -E snap install cmake --beta --classic + - export PATH=/snap/cmake/current/bin:$PATH + + # ALTERNATIVE: does not work on s390 and ppc64le + ## https://github.com/kahypar/kahypar/blob/master/.travis.yml + #- echo ${TRAVIS_OS_NAME} + #- | + # if [ "${TRAVIS_OS_NAME}" == "linux" ]; then + # pwd ; + # export DEPS_DIR="${HOME}/deps" ; + # mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR} ; + # export CMAKE_URL="https://github.com/Kitware/CMake/releases/download/v3.17.0/cmake-3.17.0-Linux-x86_64.tar.gz"; + # mkdir cmake; + # travis_retry wget --no-check-certificate -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake; + # export PATH=${DEPS_DIR}/cmake/bin:${PATH}; + # cd - ; + # pwd + # else + # brew install cmake || brew upgrade cmake; + # fi + + - echo ${PATH} + - which cmake + - cmake --version script: - mkdir build From 8fde4bad4e9722905b5eb0f6d80d26c97de15021 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Fri, 20 Nov 2020 16:09:53 -0500 Subject: [PATCH 43/47] Adding a guard. --- include/fast_float/decimal_to_binary.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/fast_float/decimal_to_binary.h b/include/fast_float/decimal_to_binary.h index d5d1311..cdeb44a 100644 --- a/include/fast_float/decimal_to_binary.h +++ b/include/fast_float/decimal_to_binary.h @@ -101,8 +101,11 @@ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept { // In some very rare cases, this could happen, in which case we might need a more accurate // computation that what we can provide cheaply. This is very, very unlikely. // - answer.power2 = -1; // This (a negative value) indicates an error condition. - return answer; + const bool inside_safe_exponent = (q >= 0) && (q <= 55); // always good because 5**q <2**128. + if(!inside_safe_exponent) { + answer.power2 = -1; // This (a negative value) indicates an error condition. + return answer; + } } // The "compute_product_approximation" function can be slightly slower than a branchless approach: // value128 product = compute_product(q, w); From 7bf5db7216c31e10cbddd5ab3a80c718b50fb03b Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Fri, 20 Nov 2020 17:05:06 -0500 Subject: [PATCH 44/47] Tuning. --- include/fast_float/decimal_to_binary.h | 8 ++++---- include/fast_float/float_common.h | 23 ++++++++++++++++++++++- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/include/fast_float/decimal_to_binary.h b/include/fast_float/decimal_to_binary.h index cdeb44a..e85f550 100644 --- a/include/fast_float/decimal_to_binary.h +++ b/include/fast_float/decimal_to_binary.h @@ -63,7 +63,6 @@ namespace { } // namespace - // w * 10 ** q // The returned value should be a valid ieee64 number that simply need to be packed. // However, in some very rare cases, the computation will fail. In such cases, we @@ -73,13 +72,13 @@ template fastfloat_really_inline adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept { adjusted_mantissa answer; - if ((w == 0) || (q < smallest_power_of_five)) { + if ((w == 0) || (q < binary::smallest_power_of_ten())) { answer.power2 = 0; answer.mantissa = 0; // result should be zero return answer; } - if (q > largest_power_of_five) { + if (q > binary::largest_power_of_ten()) { // we want to get infinity: answer.power2 = binary::infinite_power(); answer.mantissa = 0; @@ -101,7 +100,8 @@ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept { // In some very rare cases, this could happen, in which case we might need a more accurate // computation that what we can provide cheaply. This is very, very unlikely. // - const bool inside_safe_exponent = (q >= 0) && (q <= 55); // always good because 5**q <2**128. + const bool inside_safe_exponent = (q >= -27) && (q <= 55); // always good because 5**q <2**128 when q>=0, + // and otherwise, for q<0, we have 5**-q<2**64 and the 128-bit reciprocal allows for exact computation. if(!inside_safe_exponent) { answer.power2 = -1; // This (a negative value) indicates an error condition. return answer; diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 60e5f6e..299fb5a 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -233,6 +233,8 @@ template struct binary_format { static constexpr int max_exponent_round_to_even(); static constexpr int min_exponent_round_to_even(); static constexpr uint64_t max_mantissa_fast_path(); + static constexpr int largest_power_of_ten(); + static constexpr int smallest_power_of_ten(); static constexpr T exact_power_of_ten(int64_t power); }; @@ -315,6 +317,25 @@ constexpr float binary_format::exact_power_of_ten(int64_t power) { return powers_of_ten_float[power]; } + +template <> +constexpr int binary_format::largest_power_of_ten() { + return 308; +} +template <> +constexpr int binary_format::largest_power_of_ten() { + return 38; +} + +template <> +constexpr int binary_format::smallest_power_of_ten() { + return -342; +} +template <> +constexpr int binary_format::smallest_power_of_ten() { + return -65; +} + } // namespace fast_float // for convenience: @@ -328,4 +349,4 @@ inline std::ostream &operator<<(std::ostream &out, const fast_float::decimal &d) return out; } -#endif +#endif \ No newline at end of file From ed6664d93e284e2adabcf23143c1b99dc817865e Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Sat, 21 Nov 2020 01:12:53 +0100 Subject: [PATCH 45/47] add bitness for s390 --- include/fast_float/float_common.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 8791709..bcff7e1 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -6,11 +6,13 @@ #include #if (defined(__i386) || defined(__i386__) || defined(_M_IX86) \ - || defined(__arm__) || defined(__MINGW32__)) + || defined(__arm__) \ + || defined(__MINGW32__)) #define FASTFLOAT_32BIT #elif (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \ || defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \ - || defined(__MINGW64__)) + || defined(__MINGW64__) \ + || defined(__s390x__)) #define FASTFLOAT_64BIT #else #error Unknown platform From 8ba7d2e850a80ef9971b6a8b0a3cd3d1c7f66a6f Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Sat, 21 Nov 2020 00:59:13 +0100 Subject: [PATCH 46/47] travis vs FetchContent_MakeAvailable(): give up on a recent cmake ... Avoid using the modern cmake function, and go back to an earlier approach. The ppc64le travis images was not dealing well with the attempt to hoist a modern cmake into it: https://travis-ci.com/github/lemire/fast_float/jobs/444278392 --- .travis.yml | 119 +++++++++++++++++++++++++------------------ CMakeLists.txt | 2 +- tests/CMakeLists.txt | 19 +++++-- 3 files changed, 85 insertions(+), 55 deletions(-) diff --git a/.travis.yml b/.travis.yml index 134149a..c8dc04a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,11 +2,6 @@ language: cpp dist: bionic -arch: - - amd64 - - ppc64le - - s390x - cache: directories: - $HOME/.dep_cache @@ -15,9 +10,30 @@ env: global: - fastfloat_DEPENDENCY_CACHE_DIR=$HOME/.dep_cache + +# the ppc64le and s390x images use cmake 3.10, but fast_float requires 3.11. +# so we compile cmake from source in those images. +# - tried the kitware ppa but that is using 3.10 as well +# - tried also using snap to get a more recent version but that failed with +# udev errors. + matrix: include: - - os: linux + - arch: ppc64le + os: linux + env: + - CMAKE_SRC="https://github.com/Kitware/CMake/releases/download/v3.11.4/cmake-3.11.4.tar.gz" + + - arch: s390x + os: linux + env: + - CMAKE_SRC="https://github.com/Kitware/CMake/releases/download/v3.11.4/cmake-3.11.4.tar.gz" + + - arch: amd64 + os: linux + + - arch: amd64 + os: linux addons: apt: sources: @@ -28,7 +44,8 @@ matrix: - COMPILER="CC=gcc-8 && CXX=g++-8" compiler: gcc-8 - - os: linux + - arch: amd64 + os: linux addons: apt: sources: @@ -39,7 +56,8 @@ matrix: - COMPILER="CC=gcc-9 && CXX=g++-9" compiler: gcc-9 - - os: linux + - arch: amd64 + os: linux addons: apt: sources: @@ -50,7 +68,8 @@ matrix: - COMPILER="CC=gcc-10 && CXX=g++-10" compiler: gcc-10 - - os: linux + - arch: amd64 + os: linux addons: apt: sources: @@ -62,7 +81,8 @@ matrix: - SANITIZE="on" compiler: gcc-10-sanitize - - os: linux + - arch: amd64 + os: linux addons: apt: sources: @@ -72,9 +92,10 @@ matrix: env: - COMPILER="CC=gcc-10 && CXX=g++-10" - STATIC="on" - compiler: gcc-10-static + acompiler: gcc-10-static - - os: linux + - arch: amd64 + os: linux addons: apt: sources: @@ -85,7 +106,8 @@ matrix: - COMPILER="CC=clang-6.0 && CXX=clang++-6.0" compiler: clang-6 - - os: linux + - arch: amd64 + os: linux addons: apt: sources: @@ -96,7 +118,8 @@ matrix: - COMPILER="CC=clang-7 && CXX=clang++-7" compiler: clang-7 - - os: linux + - arch: amd64 + os: linux addons: apt: sources: @@ -107,7 +130,8 @@ matrix: - COMPILER="CC=clang-8 && CXX=clang++-8" compiler: clang-8 - - os: linux + - arch: amd64 + os: linux addons: apt: sources: @@ -118,7 +142,8 @@ matrix: - COMPILER="CC=clang-9 && CXX=clang++-9" compiler: clang-9 - - os: linux + - arch: amd64 + os: linux addons: apt: packages: @@ -131,7 +156,8 @@ matrix: - COMPILER="CC=clang-10 && CXX=clang++-10" compiler: clang-10 - - os: linux + - arch: amd64 + os: linux addons: apt: packages: @@ -145,7 +171,8 @@ matrix: - STATIC="on" compiler: clang-10-static - - os: linux + - arch: amd64 + os: linux addons: apt: packages: @@ -163,42 +190,36 @@ before_install: - eval "${COMPILER}" install: - # make sure we have a recent cmake version. - # fastfloat itself does not require it, but the tests require >= 3.14. - - # these architectures have a snap: - # https://gitlab.kitware.com/cmake/cmake/-/issues/20122 - - sudo -E apt-get -y update - - sudo -E apt-get -y install snapd - - sudo -E snap install cmake --beta --classic - - export PATH=/snap/cmake/current/bin:$PATH - - # ALTERNATIVE: does not work on s390 and ppc64le - ## https://github.com/kahypar/kahypar/blob/master/.travis.yml - #- echo ${TRAVIS_OS_NAME} - #- | - # if [ "${TRAVIS_OS_NAME}" == "linux" ]; then - # pwd ; - # export DEPS_DIR="${HOME}/deps" ; - # mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR} ; - # export CMAKE_URL="https://github.com/Kitware/CMake/releases/download/v3.17.0/cmake-3.17.0-Linux-x86_64.tar.gz"; - # mkdir cmake; - # travis_retry wget --no-check-certificate -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake; - # export PATH=${DEPS_DIR}/cmake/bin:${PATH}; - # cd - ; - # pwd - # else - # brew install cmake || brew upgrade cmake; - # fi - + - | + if [ "$CMAKE_SRC" != "" ] ; then + set -x + set -e + sudo -E apt remove --purge cmake + sudo -E apt-get update + sudo -E apt-get install -y build-essential libssl-dev + mkdir cmake_src + pushd cmake_src + wget "$CMAKE_SRC" + tar xfz $(basename "$CMAKE_SRC") + pushd $(basename "$CMAKE_SRC" | sed "s:.tar.gz::") + ./bootstrap + make -j2 + sudo make install + popd + popd + set +x + fi - echo ${PATH} - which cmake - cmake --version + - which ${CC} + - ${CC} --version + - which ${CXX} + - ${CXX} --version script: - mkdir build - cd build - - cmake -DFASTFLOAT_TEST=ON .. - - make + - cmake -DFASTFLOAT_TEST=ON .. + - make -j2 - ctest --output-on-failure -R basictest - diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d10096..acc0901 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.11) +cmake_minimum_required(VERSION 3.9) project(fast_float VERSION 0.1.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 11) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 04129d0..3a4b724 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,13 +1,22 @@ -# FetchContent_MakeAvailable() was only introduced in 3.14 -# https://cmake.org/cmake/help/v3.14/release/3.14.html#modules -cmake_minimum_required(VERSION 3.14 FATAL_ERROR) +# FetchContent requires cmake >=3.11 +# see https://cmake.org/cmake/help/v3.11/module/FetchContent.html +cmake_minimum_required(VERSION 3.11 FATAL_ERROR) include(FetchContent) FetchContent_Declare(doctest GIT_REPOSITORY https://github.com/onqtam/doctest.git - GIT_TAG master) -FetchContent_MakeAvailable(doctest) + GIT_TAG 2.4.1) + +# FetchContent_MakeAvailable() was only introduced in 3.14 +# https://cmake.org/cmake/help/v3.14/release/3.14.html#modules +# FetchContent_MakeAvailable(doctest) +FetchContent_GetProperties(doctest) +if(NOT doctest_POPULATED) + FetchContent_Populate(doctest) + add_subdirectory(${doctest_SOURCE_DIR} ${doctest_BINARY_DIR}) +endif() + function(fast_float_add_cpp_test TEST_NAME) add_executable(${TEST_NAME} ${TEST_NAME}.cpp) From 037136a96623dfe2b49a9781729705c766b12f2c Mon Sep 17 00:00:00 2001 From: Joao Paulo Magalhaes Date: Sat, 21 Nov 2020 19:01:26 +0000 Subject: [PATCH 47/47] fix: add bitness for ppc64le --- include/fast_float/float_common.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index bcff7e1..6679c64 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -12,7 +12,8 @@ #elif (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \ || defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \ || defined(__MINGW64__) \ - || defined(__s390x__)) + || defined(__s390x__) \ + || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__))) #define FASTFLOAT_64BIT #else #error Unknown platform