Commit 29ae2f08 authored by floitschV8@gmail.com's avatar floitschV8@gmail.com

Strtod fast-case that uses DiyFps and cached powers of ten.

This is a fixed version of r5677.
Review URL: http://codereview.chromium.org/3898007

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5686 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 598de609
......@@ -42,6 +42,11 @@ struct CachedPower {
};
static const CachedPower kCachedPowers[] = {
{V8_2PART_UINT64_C(0xfa8fd5a0, 081c0288), -1220, -348},
{V8_2PART_UINT64_C(0xbaaee17f, a23ebf76), -1193, -340},
{V8_2PART_UINT64_C(0x8b16fb20, 3055ac76), -1166, -332},
{V8_2PART_UINT64_C(0xcf42894a, 5dce35ea), -1140, -324},
{V8_2PART_UINT64_C(0x9a6bb0aa, 55653b2d), -1113, -316},
{V8_2PART_UINT64_C(0xe61acf03, 3d1a45df), -1087, -308},
{V8_2PART_UINT64_C(0xab70fe17, c79ac6ca), -1060, -300},
{V8_2PART_UINT64_C(0xff77b1fc, bebcdc4f), -1034, -292},
......@@ -129,24 +134,44 @@ static const CachedPower kCachedPowers[] = {
static const int kCachedPowersLength = ARRAY_SIZE(kCachedPowers);
static const int kCachedPowersOffset = -kCachedPowers[0].decimal_exponent;
static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10)
static const int kCachedPowersDecimalDistance =
const int PowersOfTenCache::kDecimalExponentDistance =
kCachedPowers[1].decimal_exponent - kCachedPowers[0].decimal_exponent;
const int PowersOfTenCache::kMinDecimalExponent =
kCachedPowers[0].decimal_exponent;
const int PowersOfTenCache::kMaxDecimalExponent =
kCachedPowers[kCachedPowersLength - 1].decimal_exponent;
void GetCachedPowerForBinaryExponentRange(int min_exponent,
int max_exponent,
DiyFp* power,
int* decimal_exponent) {
int kQ = DiyFp::kSignificandSize;
double k = ceiling((min_exponent + kQ - 1) * kD_1_LOG2_10);
int foo = kCachedPowersOffset;
int index =
(foo + static_cast<int>(k) - 1) / kCachedPowersDecimalDistance + 1;
ASSERT(0 <= index && index < kCachedPowersLength);
CachedPower cached_power = kCachedPowers[index];
ASSERT(min_exponent <= cached_power.binary_exponent);
ASSERT(cached_power.binary_exponent <= max_exponent);
*decimal_exponent = cached_power.decimal_exponent;
*power = DiyFp(cached_power.significand, cached_power.binary_exponent);
void PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
int min_exponent,
int max_exponent,
DiyFp* power,
int* decimal_exponent) {
int kQ = DiyFp::kSignificandSize;
double k = ceiling((min_exponent + kQ - 1) * kD_1_LOG2_10);
int foo = kCachedPowersOffset;
int index =
(foo + static_cast<int>(k) - 1) / kDecimalExponentDistance + 1;
ASSERT(0 <= index && index < kCachedPowersLength);
CachedPower cached_power = kCachedPowers[index];
ASSERT(min_exponent <= cached_power.binary_exponent);
ASSERT(cached_power.binary_exponent <= max_exponent);
*decimal_exponent = cached_power.decimal_exponent;
*power = DiyFp(cached_power.significand, cached_power.binary_exponent);
}
void PowersOfTenCache::GetCachedPowerForDecimalExponent(int requested_exponent,
DiyFp* power,
int* found_exponent) {
ASSERT(kMinDecimalExponent <= requested_exponent);
ASSERT(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance);
int index =
(requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance;
CachedPower cached_power = kCachedPowers[index];
*power = DiyFp(cached_power.significand, cached_power.binary_exponent);
*found_exponent = cached_power.decimal_exponent;
ASSERT(*found_exponent <= requested_exponent);
ASSERT(requested_exponent < *found_exponent + kDecimalExponentDistance);
}
} } // namespace v8::internal
......@@ -33,10 +33,32 @@
namespace v8 {
namespace internal {
void GetCachedPowerForBinaryExponentRange(int min_exponent,
int max_exponent,
DiyFp* power,
int* decimal_exponent);
class PowersOfTenCache {
public:
// Not all powers of ten are cached. The decimal exponent of two neighboring
// cached numbers will differ by kDecimalExponentDistance.
static const int kDecimalExponentDistance;
static const int kMinDecimalExponent;
static const int kMaxDecimalExponent;
// Returns a cached power-of-ten with a binary exponent in the range
// [min_exponent; max_exponent] (boundaries included).
static void GetCachedPowerForBinaryExponentRange(int min_exponent,
int max_exponent,
DiyFp* power,
int* decimal_exponent);
// Returns a cached power of ten x ~= 10^k such that
// k <= decimal_exponent < k + kCachedPowersDecimalDistance.
// The given decimal_exponent must satisfy
// kMinDecimalExponent <= requested_exponent, and
// requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance.
static void GetCachedPowerForDecimalExponent(int requested_exponent,
DiyFp* power,
int* found_exponent);
};
} } // namespace v8::internal
......
......@@ -45,10 +45,14 @@ class Double {
static const uint64_t kSignificandMask =
V8_2PART_UINT64_C(0x000FFFFF, FFFFFFFF);
static const uint64_t kHiddenBit = V8_2PART_UINT64_C(0x00100000, 00000000);
static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit.
static const int kSignificandSize = 53;
Double() : d64_(0) {}
explicit Double(double d) : d64_(double_to_uint64(d)) {}
explicit Double(uint64_t d64) : d64_(d64) {}
explicit Double(DiyFp diy_fp)
: d64_(DiyFpToUint64(diy_fp)) {}
DiyFp AsDiyFp() const {
ASSERT(!IsSpecial());
......@@ -67,9 +71,9 @@ class Double {
f <<= 1;
e--;
}
// Do the final shifts in one go. Don't forget the hidden bit (the '-1').
f <<= DiyFp::kSignificandSize - kSignificandSize - 1;
e -= DiyFp::kSignificandSize - kSignificandSize - 1;
// Do the final shifts in one go.
f <<= DiyFp::kSignificandSize - kSignificandSize;
e -= DiyFp::kSignificandSize - kSignificandSize;
return DiyFp(f, e);
}
......@@ -82,7 +86,8 @@ class Double {
if (IsDenormal()) return kDenormalExponent;
uint64_t d64 = AsUint64();
int biased_e = static_cast<int>((d64 & kExponentMask) >> kSignificandSize);
int biased_e =
static_cast<int>((d64 & kExponentMask) >> kPhysicalSignificandSize);
return biased_e - kExponentBias;
}
......@@ -156,12 +161,54 @@ class Double {
double value() const { return uint64_to_double(d64_); }
// Returns the significand size for a given order of magnitude.
// If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude.
// This function returns the number of significant binary digits v will have
// once its encoded into a double. In almost all cases this is equal to
// kSignificandSize. The only exception are denormals. They start with leading
// zeroes and their effective significand-size is hence smaller.
static int SignificandSizeForOrderOfMagnitude(int order) {
if (order >= (kDenormalExponent + kSignificandSize)) {
return kSignificandSize;
}
if (order <= kDenormalExponent) return 0;
return order - kDenormalExponent;
}
private:
static const int kSignificandSize = 52; // Excludes the hidden bit.
static const int kExponentBias = 0x3FF + kSignificandSize;
static const int kExponentBias = 0x3FF + kPhysicalSignificandSize;
static const int kDenormalExponent = -kExponentBias + 1;
static const int kMaxExponent = 0x7FF - kExponentBias;
static const uint64_t kInfinity = V8_2PART_UINT64_C(0x7FF00000, 00000000);
uint64_t d64_;
const uint64_t d64_;
static uint64_t DiyFpToUint64(DiyFp diy_fp) {
uint64_t significand = diy_fp.f();
int exponent = diy_fp.e();
while (significand > kHiddenBit + kSignificandMask) {
significand >>= 1;
exponent++;
}
if (exponent >= kMaxExponent) {
return kInfinity;
}
if (exponent < kDenormalExponent) {
return 0;
}
while (exponent > kDenormalExponent && (significand & kHiddenBit) == 0) {
significand <<= 1;
exponent--;
}
uint64_t biased_exponent;
if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0) {
biased_exponent = 0;
} else {
biased_exponent = static_cast<uint64_t>(exponent + kExponentBias);
}
return (significand & kSignificandMask) |
(biased_exponent << kPhysicalSignificandSize);
}
};
} } // namespace v8::internal
......
......@@ -613,9 +613,10 @@ static bool Grisu3(double v,
kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize);
int ten_mk_maximal_binary_exponent =
kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize);
GetCachedPowerForBinaryExponentRange(ten_mk_minimal_binary_exponent,
ten_mk_maximal_binary_exponent,
&ten_mk, &mk);
PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
ten_mk_minimal_binary_exponent,
ten_mk_maximal_binary_exponent,
&ten_mk, &mk);
ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() +
DiyFp::kSignificandSize) &&
(kMaximalTargetExponent >= w.e() + ten_mk.e() +
......@@ -671,9 +672,10 @@ static bool Grisu3Counted(double v,
kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize);
int ten_mk_maximal_binary_exponent =
kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize);
GetCachedPowerForBinaryExponentRange(ten_mk_minimal_binary_exponent,
ten_mk_maximal_binary_exponent,
&ten_mk, &mk);
PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
ten_mk_minimal_binary_exponent,
ten_mk_maximal_binary_exponent,
&ten_mk, &mk);
ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() +
DiyFp::kSignificandSize) &&
(kMaximalTargetExponent >= w.e() + ten_mk.e() +
......
This diff is collapsed.
......@@ -198,10 +198,58 @@ TEST(Strtod) {
CHECK_EQ(1234e304, StrtodChar("0000000123400000", 299));
CHECK_EQ(V8_INFINITY, StrtodChar("00000000180000000", 300));
CHECK_EQ(17e307, StrtodChar("00000000170000000", 300));
CHECK_EQ(1.7976931348623157E+308, StrtodChar("17976931348623157", 292));
CHECK_EQ(1.7976931348623158E+308, StrtodChar("17976931348623158", 292));
CHECK_EQ(V8_INFINITY, StrtodChar("17976931348623159", 292));
// The following number is the result of 89255.0/1e-22. Both floating-point
// numbers can be accurately represented with doubles. However on Linux,x86
// the floating-point stack is set to 80bits and the double-rounding
// introduces an error.
CHECK_EQ(89255e-22, StrtodChar("89255", -22));
CHECK_EQ(104110013277974872254e-225,
StrtodChar("104110013277974872254", -225));
CHECK_EQ(123456789e108, StrtodChar("123456789", 108));
CHECK_EQ(123456789e109, StrtodChar("123456789", 109));
CHECK_EQ(123456789e110, StrtodChar("123456789", 110));
CHECK_EQ(123456789e111, StrtodChar("123456789", 111));
CHECK_EQ(123456789e112, StrtodChar("123456789", 112));
CHECK_EQ(123456789e113, StrtodChar("123456789", 113));
CHECK_EQ(123456789e114, StrtodChar("123456789", 114));
CHECK_EQ(123456789e115, StrtodChar("123456789", 115));
CHECK_EQ(1234567890123456789012345e108,
StrtodChar("1234567890123456789012345", 108));
CHECK_EQ(1234567890123456789012345e109,
StrtodChar("1234567890123456789012345", 109));
CHECK_EQ(1234567890123456789012345e110,
StrtodChar("1234567890123456789012345", 110));
CHECK_EQ(1234567890123456789012345e111,
StrtodChar("1234567890123456789012345", 111));
CHECK_EQ(1234567890123456789012345e112,
StrtodChar("1234567890123456789012345", 112));
CHECK_EQ(1234567890123456789012345e113,
StrtodChar("1234567890123456789012345", 113));
CHECK_EQ(1234567890123456789012345e114,
StrtodChar("1234567890123456789012345", 114));
CHECK_EQ(1234567890123456789012345e115,
StrtodChar("1234567890123456789012345", 115));
CHECK_EQ(1234567890123456789052345e108,
StrtodChar("1234567890123456789052345", 108));
CHECK_EQ(1234567890123456789052345e109,
StrtodChar("1234567890123456789052345", 109));
CHECK_EQ(1234567890123456789052345e110,
StrtodChar("1234567890123456789052345", 110));
CHECK_EQ(1234567890123456789052345e111,
StrtodChar("1234567890123456789052345", 111));
CHECK_EQ(1234567890123456789052345e112,
StrtodChar("1234567890123456789052345", 112));
CHECK_EQ(1234567890123456789052345e113,
StrtodChar("1234567890123456789052345", 113));
CHECK_EQ(1234567890123456789052345e114,
StrtodChar("1234567890123456789052345", 114));
CHECK_EQ(1234567890123456789052345e115,
StrtodChar("1234567890123456789052345", 115));
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment