Let us try the long path.

This commit is contained in:
Daniel Lemire 2020-10-27 18:26:16 -04:00
parent 644b05989d
commit 05ad45dfb5
3 changed files with 57 additions and 15 deletions

View File

@ -160,6 +160,7 @@ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
return answer; return answer;
} }
} // namespace fast_float } // namespace fast_float
#endif #endif

View File

@ -126,7 +126,11 @@ value128 full_multiplication(uint64_t value1, uint64_t value2) {
struct adjusted_mantissa { struct adjusted_mantissa {
uint64_t mantissa; uint64_t mantissa;
int power2; int power2;
adjusted_mantissa() : mantissa(0), power2(0) {} adjusted_mantissa() = default;
//bool operator==(const adjusted_mantissa &o) const = default;
bool operator==(const adjusted_mantissa &o) const {
return mantissa == o.mantissa && power2 == o.power2;
}
}; };
struct decimal { struct decimal {
@ -135,6 +139,36 @@ struct decimal {
bool negative; bool negative;
bool truncated; bool truncated;
uint8_t digits[max_digits]; uint8_t digits[max_digits];
// generate a mantissa by truncating to 19 digits, this assumes
// that num_digits >= 19 (caller should check).
inline uint64_t to_truncated_mantissa() {
const int64_t max_digit_with_overflow = 19;
static_assert(9999999999999999999U < 0xffffffffffffffff, "cannot fit 19 digits in an uint64_t");
static_assert(max_digit_with_overflow < max_digits, "too few max_digits");
uint64_t val;
// 8 first digits
::memcpy(&val, digits, sizeof(uint64_t));
val = val * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
uint64_t mantissa = uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
// 8 more digits for a total of 16
::memcpy(&val, digits + sizeof(uint64_t), sizeof(uint64_t));
val = val * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
uint32_t eight_digits_value = uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
mantissa = 100000000 * mantissa + eight_digits_value;
for(size_t i = 2*sizeof(uint64_t); i < max_digit_with_overflow; i++) {
mantissa = mantissa * 10 + digits[i]; // can be accelerated
}
return mantissa;
}
// generate an exponent matching to_truncated_mantissa()
inline int64_t to_truncated_exponent() {
const int64_t max_digit_with_overflow = 19;
return decimal_point - max_digit_with_overflow;
}
}; };
constexpr static double powers_of_ten_double[] = { constexpr static double powers_of_ten_double[] = {
@ -265,4 +299,13 @@ constexpr float binary_format<float>::exact_power_of_ten(int64_t power) {
} // namespace fast_float } // namespace fast_float
// for convenience:
#include <ostream>
std::ostream& operator<<(std::ostream& out, const fast_float::decimal& d) {
out << "0.";
for(size_t i = 0; i < d.num_digits; i++) { out << int32_t(d.digits[i]); }
out << " * 10 ** " << d.decimal_point;
return out;
}
#endif #endif

View File

@ -28,20 +28,6 @@ inline void trim(decimal &h) {
} }
} }
/** If you ever want to see what is going on, the following function might prove handy:
* **/
void print(const decimal d, int32_t exp2 = 0) {
printf("0.");
for(size_t i = 0; i < d.num_digits; i++) {
printf("%d", int(d.digits[i]));
}
printf(" * 10 **%d ", d.decimal_point);
printf(" * 2 **%d ", exp2);
}
uint32_t number_of_digits_decimal_left_shift(decimal &h, uint32_t shift) { uint32_t number_of_digits_decimal_left_shift(decimal &h, uint32_t shift) {
@ -368,8 +354,20 @@ adjusted_mantissa compute_float(decimal &d) {
template <typename binary> template <typename binary>
adjusted_mantissa parse_long_mantissa(const char *first, const char* last) { adjusted_mantissa parse_long_mantissa(const char *first, const char* last) {
decimal d = parse_decimal(first, last); decimal d = parse_decimal(first, last);
const uint64_t mantissa = d.to_truncated_mantissa();
const int64_t exponent = d.to_truncated_exponent();
adjusted_mantissa am1 = compute_float<binary>(exponent, mantissa);
adjusted_mantissa am2 = compute_float<binary>(exponent, mantissa+1);
if( am1 == am2 ) { return am1; }
return compute_float<binary>(d); return compute_float<binary>(d);
} }
} // namespace fast_float } // namespace fast_float
#endif #endif
/*
uint32_t num_digits;
int32_t decimal_point;
bool negative;
bool truncated;
uint8_t digits[max_digits];*/