mirror of
https://github.com/fastfloat/fast_float.git
synced 2025-12-06 16:56:57 +08:00
Merge pull request #39 from lemire/dlemire/extended_fast_path
Extending the fast path.
This commit is contained in:
commit
3dab88a245
7
.github/workflows/ubuntu18.yml
vendored
7
.github/workflows/ubuntu18.yml
vendored
@ -15,6 +15,13 @@ jobs:
|
||||
- {cxx: , arch: -DCMAKE_CXX_FLAGS="-m32"} # default=gcc7
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
<<<<<<< HEAD
|
||||
- name: Setup cmake
|
||||
uses: jwlawson/actions-setup-cmake@v1.4
|
||||
with:
|
||||
cmake-version: '3.9.x'
|
||||
=======
|
||||
>>>>>>> main
|
||||
- name: Install older compilers
|
||||
run: |
|
||||
sudo -E dpkg --add-architecture i386
|
||||
|
||||
7
.github/workflows/ubuntu20.yml
vendored
7
.github/workflows/ubuntu20.yml
vendored
@ -14,6 +14,13 @@ jobs:
|
||||
- {cxx: , arch: -DCMAKE_CXX_FLAGS="-m32"} # default=gcc9
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
<<<<<<< HEAD
|
||||
- name: Setup cmake
|
||||
uses: jwlawson/actions-setup-cmake@v1.4
|
||||
with:
|
||||
cmake-version: '3.9.x'
|
||||
=======
|
||||
>>>>>>> main
|
||||
- name: install older compilers
|
||||
run: |
|
||||
sudo -E dpkg --add-architecture i386
|
||||
|
||||
@ -62,6 +62,7 @@ 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
|
||||
@ -71,13 +72,13 @@ template <typename binary>
|
||||
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;
|
||||
@ -93,12 +94,18 @@ 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<binary::mantissa_explicit_bits() + 3>(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;
|
||||
//
|
||||
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;
|
||||
}
|
||||
}
|
||||
// The "compute_product_approximation" function can be slightly slower than a branchless approach:
|
||||
// value128 product = compute_product(q, w);
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -257,6 +257,8 @@ template <typename T> 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);
|
||||
};
|
||||
|
||||
@ -339,6 +341,25 @@ constexpr float binary_format<float>::exact_power_of_ten(int64_t power) {
|
||||
return powers_of_ten_float[power];
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
constexpr int binary_format<double>::largest_power_of_ten() {
|
||||
return 308;
|
||||
}
|
||||
template <>
|
||||
constexpr int binary_format<float>::largest_power_of_ten() {
|
||||
return 38;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr int binary_format<double>::smallest_power_of_ten() {
|
||||
return -342;
|
||||
}
|
||||
template <>
|
||||
constexpr int binary_format<float>::smallest_power_of_ten() {
|
||||
return -65;
|
||||
}
|
||||
|
||||
} // namespace fast_float
|
||||
|
||||
// for convenience:
|
||||
@ -352,4 +373,4 @@ inline std::ostream &operator<<(std::ostream &out, const fast_float::decimal &d)
|
||||
return out;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@ -8,7 +8,7 @@ for q in range(-342,0):
|
||||
z = 0
|
||||
while( (1<<z) < power5) :
|
||||
z += 1
|
||||
if(q >= -17):
|
||||
if(q >= -27):
|
||||
b = z + 127
|
||||
c = 2 ** b // power5 + 1
|
||||
format(c)
|
||||
|
||||
@ -27,6 +27,7 @@ function(fast_float_add_cpp_test TEST_NAME)
|
||||
endif()
|
||||
target_link_libraries(${TEST_NAME} PUBLIC fast_float doctest)
|
||||
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)
|
||||
|
||||
@ -31,7 +31,7 @@ template <typename T> 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;
|
||||
|
||||
115
tests/powersoffive_hardround.cpp
Normal file
115
tests/powersoffive_hardround.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
|
||||
#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 <locale>
|
||||
#include <sstream>
|
||||
// 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<char*>(start) + nread;
|
||||
return d;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
std::pair<double, bool> 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<float, bool> strtof_from_string(char *st) {
|
||||
float d;
|
||||
char *pr;
|
||||
#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");
|
||||
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<uint64_t> 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<double, bool> 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;
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
#include <cstdint>
|
||||
#include <random>
|
||||
|
||||
#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<double, bool> strtod_from_string(char *st) {
|
||||
std::pair<float, bool> 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;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#include <cstdint>
|
||||
#include <random>
|
||||
|
||||
#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<double, bool> strtod_from_string(char *st) {
|
||||
std::pair<float, bool> 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)) {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user