Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add optimized (smaller) lookup table for float type parsing #103

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions include/fast_float/decimal_to_binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,19 @@ namespace fast_float {
template <int bit_precision>
CXX20_CONSTEXPR fastfloat_really_inline
value128 compute_product_approximation(int64_t q, uint64_t w) {
const int index = 2 * int(q - powers::smallest_power_of_five);
using parsing_type = typename std::conditional<bit_precision <= 26, float, double>::type;
const int index = 2 * int(q - powers_of_five_count<parsing_type>::smallest_power_of_five);
// For small values of q, e.g., q in [0,27], the answer is always exact because
// The line value128 firstproduct = full_multiplication(w, power_of_five_128[index]);
// gives the exact answer.
value128 firstproduct = full_multiplication(w, powers::power_of_five_128[index]);
value128 firstproduct = full_multiplication(w, powers<parsing_type>::power_of_five_128[index]);
static_assert((bit_precision >= 0) && (bit_precision <= 64), " precision should be in (0,64]");
constexpr uint64_t precision_mask = (bit_precision < 64) ?
(uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision)
: uint64_t(0xFFFFFFFFFFFFFFFF);
if((firstproduct.high & precision_mask) == precision_mask) { // could further guard with (lower + w < lower)
// regarding the second product, we only need secondproduct.high, but our expectation is that the compiler will optimize this extra work away if needed.
value128 secondproduct = full_multiplication(w, powers::power_of_five_128[index + 1]);
value128 secondproduct = full_multiplication(w, powers<parsing_type>::power_of_five_128[index + 1]);
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) {
firstproduct.high++;
Expand Down
142 changes: 134 additions & 8 deletions include/fast_float/fast_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,141 @@ namespace fast_float {
* infinite in binary64 so we never need to worry about powers
* of 5 greater than 308.
*/
template <class unused = void>

template<typename T>
struct powers_of_five_count {
constexpr static int smallest_power_of_five = binary_format<T>::smallest_power_of_ten();
constexpr static int largest_power_of_five = binary_format<T>::largest_power_of_ten();
constexpr static size_t number_of_entries = 2 * (largest_power_of_five - smallest_power_of_five + 1);
};

template <class unused, typename T>
struct powers_template {
static const uint64_t power_of_five_128[powers_of_five_count<T>::number_of_entries];
};

constexpr static int smallest_power_of_five = binary_format<double>::smallest_power_of_ten();
constexpr static int largest_power_of_five = binary_format<double>::largest_power_of_ten();
constexpr static int number_of_entries = 2 * (largest_power_of_five - smallest_power_of_five + 1);
template <class unused>
struct powers_template<unused, double> {
// Powers of five from 5^-342 all the way to 5^308 rounded toward one.
static const uint64_t power_of_five_128[number_of_entries];
static const uint64_t power_of_five_128[powers_of_five_count<double>::number_of_entries];
};

template <class unused>
struct powers_template<unused, float> {
// Powers of five from 5^-65 all the way to 5^38 rounded toward one.
static const uint64_t power_of_five_128[powers_of_five_count<float>::number_of_entries];
};

template <class unused>
const uint64_t powers_template<unused>::power_of_five_128[number_of_entries] = {
const uint64_t powers_template<unused, float>::power_of_five_128[] = {
0x86ccbb52ea94baeaL,0x98e947129fc2b4e9L,
0xa87fea27a539e9a5L,0x3f2398d747b36224L,
0xd29fe4b18e88640eL,0x8eec7f0d19a03aadL,
0x83a3eeeef9153e89L,0x1953cf68300424acL,
0xa48ceaaab75a8e2bL,0x5fa8c3423c052dd7L,
0xcdb02555653131b6L,0x3792f412cb06794dL,
0x808e17555f3ebf11L,0xe2bbd88bbee40bd0L,
0xa0b19d2ab70e6ed6L,0x5b6aceaeae9d0ec4L,
0xc8de047564d20a8bL,0xf245825a5a445275L,
0xfb158592be068d2eL,0xeed6e2f0f0d56712L,
0x9ced737bb6c4183dL,0x55464dd69685606bL,
0xc428d05aa4751e4cL,0xaa97e14c3c26b886L,
0xf53304714d9265dfL,0xd53dd99f4b3066a8L,
0x993fe2c6d07b7fabL,0xe546a8038efe4029L,
0xbf8fdb78849a5f96L,0xde98520472bdd033L,
0xef73d256a5c0f77cL,0x963e66858f6d4440L,
0x95a8637627989aadL,0xdde7001379a44aa8L,
0xbb127c53b17ec159L,0x5560c018580d5d52L,
0xe9d71b689dde71afL,0xaab8f01e6e10b4a6L,
0x9226712162ab070dL,0xcab3961304ca70e8L,
0xb6b00d69bb55c8d1L,0x3d607b97c5fd0d22L,
0xe45c10c42a2b3b05L,0x8cb89a7db77c506aL,
0x8eb98a7a9a5b04e3L,0x77f3608e92adb242L,
0xb267ed1940f1c61cL,0x55f038b237591ed3L,
0xdf01e85f912e37a3L,0x6b6c46dec52f6688L,
0x8b61313bbabce2c6L,0x2323ac4b3b3da015L,
0xae397d8aa96c1b77L,0xabec975e0a0d081aL,
0xd9c7dced53c72255L,0x96e7bd358c904a21L,
0x881cea14545c7575L,0x7e50d64177da2e54L,
0xaa242499697392d2L,0xdde50bd1d5d0b9e9L,
0xd4ad2dbfc3d07787L,0x955e4ec64b44e864L,
0x84ec3c97da624ab4L,0xbd5af13bef0b113eL,
0xa6274bbdd0fadd61L,0xecb1ad8aeacdd58eL,
0xcfb11ead453994baL,0x67de18eda5814af2L,
0x81ceb32c4b43fcf4L,0x80eacf948770ced7L,
0xa2425ff75e14fc31L,0xa1258379a94d028dL,
0xcad2f7f5359a3b3eL,0x96ee45813a04330L,
0xfd87b5f28300ca0dL,0x8bca9d6e188853fcL,
0x9e74d1b791e07e48L,0x775ea264cf55347eL,
0xc612062576589ddaL,0x95364afe032a819eL,
0xf79687aed3eec551L,0x3a83ddbd83f52205L,
0x9abe14cd44753b52L,0xc4926a9672793543L,
0xc16d9a0095928a27L,0x75b7053c0f178294L,
0xf1c90080baf72cb1L,0x5324c68b12dd6339L,
0x971da05074da7beeL,0xd3f6fc16ebca5e04L,
0xbce5086492111aeaL,0x88f4bb1ca6bcf585L,
0xec1e4a7db69561a5L,0x2b31e9e3d06c32e6L,
0x9392ee8e921d5d07L,0x3aff322e62439fd0L,
0xb877aa3236a4b449L,0x9befeb9fad487c3L,
0xe69594bec44de15bL,0x4c2ebe687989a9b4L,
0x901d7cf73ab0acd9L,0xf9d37014bf60a11L,
0xb424dc35095cd80fL,0x538484c19ef38c95L,
0xe12e13424bb40e13L,0x2865a5f206b06fbaL,
0x8cbccc096f5088cbL,0xf93f87b7442e45d4L,
0xafebff0bcb24aafeL,0xf78f69a51539d749L,
0xdbe6fecebdedd5beL,0xb573440e5a884d1cL,
0x89705f4136b4a597L,0x31680a88f8953031L,
0xabcc77118461cefcL,0xfdc20d2b36ba7c3eL,
0xd6bf94d5e57a42bcL,0x3d32907604691b4dL,
0x8637bd05af6c69b5L,0xa63f9a49c2c1b110L,
0xa7c5ac471b478423L,0xfcf80dc33721d54L,
0xd1b71758e219652bL,0xd3c36113404ea4a9L,
0x83126e978d4fdf3bL,0x645a1cac083126eaL,
0xa3d70a3d70a3d70aL,0x3d70a3d70a3d70a4L,
0xccccccccccccccccL,0xcccccccccccccccdL,
0x8000000000000000L,0x0L,
0xa000000000000000L,0x0L,
0xc800000000000000L,0x0L,
0xfa00000000000000L,0x0L,
0x9c40000000000000L,0x0L,
0xc350000000000000L,0x0L,
0xf424000000000000L,0x0L,
0x9896800000000000L,0x0L,
0xbebc200000000000L,0x0L,
0xee6b280000000000L,0x0L,
0x9502f90000000000L,0x0L,
0xba43b74000000000L,0x0L,
0xe8d4a51000000000L,0x0L,
0x9184e72a00000000L,0x0L,
0xb5e620f480000000L,0x0L,
0xe35fa931a0000000L,0x0L,
0x8e1bc9bf04000000L,0x0L,
0xb1a2bc2ec5000000L,0x0L,
0xde0b6b3a76400000L,0x0L,
0x8ac7230489e80000L,0x0L,
0xad78ebc5ac620000L,0x0L,
0xd8d726b7177a8000L,0x0L,
0x878678326eac9000L,0x0L,
0xa968163f0a57b400L,0x0L,
0xd3c21bcecceda100L,0x0L,
0x84595161401484a0L,0x0L,
0xa56fa5b99019a5c8L,0x0L,
0xcecb8f27f4200f3aL,0x0L,
0x813f3978f8940984L,0x4000000000000000L,
0xa18f07d736b90be5L,0x5000000000000000L,
0xc9f2c9cd04674edeL,0xa400000000000000L,
0xfc6f7c4045812296L,0x4d00000000000000L,
0x9dc5ada82b70b59dL,0xf020000000000000L,
0xc5371912364ce305L,0x6c28000000000000L,
0xf684df56c3e01bc6L,0xc732000000000000L,
0x9a130b963a6c115cL,0x3c7f400000000000L,
0xc097ce7bc90715b3L,0x4b9f100000000000L,
0xf0bdc21abb48db20L,0x1e86d40000000000L,
0x96769950b50d88f4L,0x1314448000000000L,
};

template <class unused>
const uint64_t powers_template<unused, double>::power_of_five_128[] = {
0xeef453d6923bd65a,0x113faa2906a13b3f,
0x9558b4661b6565f8,0x4ac7ca59a424c507,
0xbaaee17fa23ebf76,0x5d79bcf00d2df649,
Expand Down Expand Up @@ -691,8 +814,11 @@ const uint64_t powers_template<unused>::power_of_five_128[number_of_entries] = {
0x91d28b7416cdd27e,0x4cdc331d57fa5441,
0xb6472e511c81471d,0xe0133fe4adf8e952,
0xe3d8f9e563a198e5,0x58180fddd97723a6,
0x8e679c2f5e44ff8f,0x570f09eaa7ea7648,};
using powers = powers_template<>;
0x8e679c2f5e44ff8f,0x570f09eaa7ea7648,
};

template<typename T>
using powers = powers_template<void, T>;

}

Expand Down
31 changes: 31 additions & 0 deletions script/table_generation_float.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
def format(number):
upper = number // (1<<64)
lower = number % (1<<64)
print(""+hex(upper)+","+hex(lower)+",")

for q in range(-65,0):
power5 = 5 ** -q
z = 0
while( (1<<z) < power5) :
z += 1
if(q >= -27):
b = z + 127
c = 2 ** b // power5 + 1
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,38+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)
2 changes: 2 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ endfunction(fast_float_add_cpp_test)


fast_float_add_cpp_test(example_test)
fast_float_add_cpp_test(example_test_float)
fast_float_add_cpp_test(example_test_mixed)
fast_float_add_cpp_test(example_comma_test)
fast_float_add_cpp_test(basictest)

Expand Down
12 changes: 12 additions & 0 deletions tests/example_test_float.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

#include "fast_float/fast_float.h"
#include <iostream>

int main() {
const std::string input = "3.1416 xyz ";
float result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
if((answer.ec != std::errc()) || ((result != 3.1416f))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}
23 changes: 23 additions & 0 deletions tests/example_test_mixed.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

#include "fast_float/fast_float.h"
#include <iostream>

int main() {
const std::string input = "3.1416 xyz ";

{
double result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
if((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
std::cout << "parsed the number " << result << std::endl;
}

{
float result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
if((answer.ec != std::errc()) || ((result != 3.1416f))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
std::cout << "parsed the number " << result << std::endl;
}

return EXIT_SUCCESS;
}