Add a function to get cached power of 10

This commit is contained in:
Victor Zverovich 2018-05-27 10:57:26 -07:00
parent 2f257b7291
commit 468c243ca8
3 changed files with 30 additions and 7 deletions

View File

@ -342,6 +342,19 @@ FMT_FUNC fp operator*(fp x, fp y) {
uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31); uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
return fp(ac + (ad >> 32) + (bc >> 32) + (mid >> 32), x.e + y.e + 64); return fp(ac + (ad >> 32) + (bc >> 32) + (mid >> 32), x.e + y.e + 64);
} }
FMT_FUNC fp get_cached_power(int min_exponent, int &pow10_exponent) {
const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10)
int index = static_cast<int>(std::ceil(
(min_exponent + fp::fp_significand_size - 1) * one_over_log2_10));
// Decimal exponent of the first (smallest) cached power of 10.
const int first_dec_exp = -348;
// Difference between two consecutive decimal exponents in cached powers of 10.
const int dec_exp_step = 8;
index = (index - first_dec_exp - 1) / dec_exp_step + 1;
pow10_exponent = first_dec_exp + index * dec_exp_step;
return fp(data::POW10_SIGNIFICANDS[index], data::POW10_EXPONENTS[index]);
}
} // namespace internal } // namespace internal
#if FMT_USE_WINDOWS_H #if FMT_USE_WINDOWS_H

View File

@ -272,6 +272,8 @@ class fp {
uint64_t f; uint64_t f;
int e; int e;
static constexpr int fp_significand_size = sizeof(f) * char_size;
fp(uint64_t f, int e): f(f), e(e) {} fp(uint64_t f, int e): f(f), e(e) {}
// Constructs fp from an IEEE754 double. It is a template to prevent compile // Constructs fp from an IEEE754 double. It is a template to prevent compile
@ -305,7 +307,6 @@ class fp {
f <<= 1; f <<= 1;
--e; --e;
} }
auto fp_significand_size = sizeof(f) * char_size;
// Subtract 1 to account for hidden bit. // Subtract 1 to account for hidden bit.
auto offset = fp_significand_size - double_significand_size - SHIFT - 1; auto offset = fp_significand_size - double_significand_size - SHIFT - 1;
f <<= offset; f <<= offset;
@ -323,12 +324,9 @@ inline fp operator-(fp x, fp y) {
// with half-up tie breaking, r.e = x.e + y.e + 32. Result may not be normalized. // with half-up tie breaking, r.e = x.e + y.e + 32. Result may not be normalized.
fp operator*(fp x, fp y); fp operator*(fp x, fp y);
// Compute k such that its cached power c_k = c_k.f * pow(2, c_k.e) satisfies // Returns cached power (of 10) c_k = c_k.f * pow(2, c_k.e) such that its
// min_exponent <= c_k.e + e <= min_exponent + 3. // (binary) exponent satisfies min_exponent <= c_k.e <= min_exponent + 3.
inline int compute_cached_power_index(int e, int min_exponent) { fp get_cached_power(int min_exponent, int &pow10_exponent);
const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10)
return static_cast<int>(std::ceil((min_exponent - e + 63) * one_over_log2_10));
}
template <typename Allocator> template <typename Allocator>
typename Allocator::value_type *allocate(Allocator& alloc, std::size_t n) { typename Allocator::value_type *allocate(Allocator& alloc, std::size_t n) {

View File

@ -918,3 +918,15 @@ TEST(FPTest, Multiply) {
EXPECT_EQ(v.f, (123 * 567 + 1u) / 2); EXPECT_EQ(v.f, (123 * 567 + 1u) / 2);
EXPECT_EQ(v.e, 4 + 8 + 64); EXPECT_EQ(v.e, 4 + 8 + 64);
} }
TEST(FPTest, GetCachedPower) {
typedef std::numeric_limits<double> limits;
for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) {
int dec_exp = 0;
auto fp = fmt::internal::get_cached_power(exp, dec_exp);
EXPECT_LE(exp, fp.e);
int dec_exp_step = 8;
EXPECT_LE(fp.e, exp + dec_exp_step * log2(10));
EXPECT_EQ(pow(10, dec_exp), ldexp(fp.f, fp.e));
}
}