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 { ...@@ -42,6 +42,11 @@ struct CachedPower {
}; };
static const CachedPower kCachedPowers[] = { 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(0xe61acf03, 3d1a45df), -1087, -308},
{V8_2PART_UINT64_C(0xab70fe17, c79ac6ca), -1060, -300}, {V8_2PART_UINT64_C(0xab70fe17, c79ac6ca), -1060, -300},
{V8_2PART_UINT64_C(0xff77b1fc, bebcdc4f), -1034, -292}, {V8_2PART_UINT64_C(0xff77b1fc, bebcdc4f), -1034, -292},
...@@ -129,24 +134,44 @@ static const CachedPower kCachedPowers[] = { ...@@ -129,24 +134,44 @@ static const CachedPower kCachedPowers[] = {
static const int kCachedPowersLength = ARRAY_SIZE(kCachedPowers); static const int kCachedPowersLength = ARRAY_SIZE(kCachedPowers);
static const int kCachedPowersOffset = -kCachedPowers[0].decimal_exponent; static const int kCachedPowersOffset = -kCachedPowers[0].decimal_exponent;
static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10) 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; 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, void PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
int max_exponent, int min_exponent,
DiyFp* power, int max_exponent,
int* decimal_exponent) { DiyFp* power,
int kQ = DiyFp::kSignificandSize; int* decimal_exponent) {
double k = ceiling((min_exponent + kQ - 1) * kD_1_LOG2_10); int kQ = DiyFp::kSignificandSize;
int foo = kCachedPowersOffset; double k = ceiling((min_exponent + kQ - 1) * kD_1_LOG2_10);
int index = int foo = kCachedPowersOffset;
(foo + static_cast<int>(k) - 1) / kCachedPowersDecimalDistance + 1; int index =
ASSERT(0 <= index && index < kCachedPowersLength); (foo + static_cast<int>(k) - 1) / kDecimalExponentDistance + 1;
CachedPower cached_power = kCachedPowers[index]; ASSERT(0 <= index && index < kCachedPowersLength);
ASSERT(min_exponent <= cached_power.binary_exponent); CachedPower cached_power = kCachedPowers[index];
ASSERT(cached_power.binary_exponent <= max_exponent); ASSERT(min_exponent <= cached_power.binary_exponent);
*decimal_exponent = cached_power.decimal_exponent; ASSERT(cached_power.binary_exponent <= max_exponent);
*power = DiyFp(cached_power.significand, cached_power.binary_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 } } // namespace v8::internal
...@@ -33,10 +33,32 @@ ...@@ -33,10 +33,32 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
void GetCachedPowerForBinaryExponentRange(int min_exponent, class PowersOfTenCache {
int max_exponent, public:
DiyFp* power,
int* decimal_exponent); // 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 } } // namespace v8::internal
......
...@@ -45,10 +45,14 @@ class Double { ...@@ -45,10 +45,14 @@ class Double {
static const uint64_t kSignificandMask = static const uint64_t kSignificandMask =
V8_2PART_UINT64_C(0x000FFFFF, FFFFFFFF); V8_2PART_UINT64_C(0x000FFFFF, FFFFFFFF);
static const uint64_t kHiddenBit = V8_2PART_UINT64_C(0x00100000, 00000000); 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) {} Double() : d64_(0) {}
explicit Double(double d) : d64_(double_to_uint64(d)) {} explicit Double(double d) : d64_(double_to_uint64(d)) {}
explicit Double(uint64_t d64) : d64_(d64) {} explicit Double(uint64_t d64) : d64_(d64) {}
explicit Double(DiyFp diy_fp)
: d64_(DiyFpToUint64(diy_fp)) {}
DiyFp AsDiyFp() const { DiyFp AsDiyFp() const {
ASSERT(!IsSpecial()); ASSERT(!IsSpecial());
...@@ -67,9 +71,9 @@ class Double { ...@@ -67,9 +71,9 @@ class Double {
f <<= 1; f <<= 1;
e--; e--;
} }
// Do the final shifts in one go. Don't forget the hidden bit (the '-1'). // Do the final shifts in one go.
f <<= DiyFp::kSignificandSize - kSignificandSize - 1; f <<= DiyFp::kSignificandSize - kSignificandSize;
e -= DiyFp::kSignificandSize - kSignificandSize - 1; e -= DiyFp::kSignificandSize - kSignificandSize;
return DiyFp(f, e); return DiyFp(f, e);
} }
...@@ -82,7 +86,8 @@ class Double { ...@@ -82,7 +86,8 @@ class Double {
if (IsDenormal()) return kDenormalExponent; if (IsDenormal()) return kDenormalExponent;
uint64_t d64 = AsUint64(); 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; return biased_e - kExponentBias;
} }
...@@ -156,12 +161,54 @@ class Double { ...@@ -156,12 +161,54 @@ class Double {
double value() const { return uint64_to_double(d64_); } 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: private:
static const int kSignificandSize = 52; // Excludes the hidden bit. static const int kExponentBias = 0x3FF + kPhysicalSignificandSize;
static const int kExponentBias = 0x3FF + kSignificandSize;
static const int kDenormalExponent = -kExponentBias + 1; 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 } } // namespace v8::internal
......
...@@ -613,9 +613,10 @@ static bool Grisu3(double v, ...@@ -613,9 +613,10 @@ static bool Grisu3(double v,
kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize);
int ten_mk_maximal_binary_exponent = int ten_mk_maximal_binary_exponent =
kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize);
GetCachedPowerForBinaryExponentRange(ten_mk_minimal_binary_exponent, PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
ten_mk_maximal_binary_exponent, ten_mk_minimal_binary_exponent,
&ten_mk, &mk); ten_mk_maximal_binary_exponent,
&ten_mk, &mk);
ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() +
DiyFp::kSignificandSize) && DiyFp::kSignificandSize) &&
(kMaximalTargetExponent >= w.e() + ten_mk.e() + (kMaximalTargetExponent >= w.e() + ten_mk.e() +
...@@ -671,9 +672,10 @@ static bool Grisu3Counted(double v, ...@@ -671,9 +672,10 @@ static bool Grisu3Counted(double v,
kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize);
int ten_mk_maximal_binary_exponent = int ten_mk_maximal_binary_exponent =
kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize);
GetCachedPowerForBinaryExponentRange(ten_mk_minimal_binary_exponent, PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
ten_mk_maximal_binary_exponent, ten_mk_minimal_binary_exponent,
&ten_mk, &mk); ten_mk_maximal_binary_exponent,
&ten_mk, &mk);
ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() +
DiyFp::kSignificandSize) && DiyFp::kSignificandSize) &&
(kMaximalTargetExponent >= w.e() + ten_mk.e() + (kMaximalTargetExponent >= w.e() + ten_mk.e() +
......
This diff is collapsed.
...@@ -198,10 +198,58 @@ TEST(Strtod) { ...@@ -198,10 +198,58 @@ TEST(Strtod) {
CHECK_EQ(1234e304, StrtodChar("0000000123400000", 299)); CHECK_EQ(1234e304, StrtodChar("0000000123400000", 299));
CHECK_EQ(V8_INFINITY, StrtodChar("00000000180000000", 300)); CHECK_EQ(V8_INFINITY, StrtodChar("00000000180000000", 300));
CHECK_EQ(17e307, StrtodChar("00000000170000000", 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 // 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 // numbers can be accurately represented with doubles. However on Linux,x86
// the floating-point stack is set to 80bits and the double-rounding // the floating-point stack is set to 80bits and the double-rounding
// introduces an error. // introduces an error.
CHECK_EQ(89255e-22, StrtodChar("89255", -22)); 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