Commit 808d00f8 authored by floitschV8@gmail.com's avatar floitschV8@gmail.com

Bignum implementation of Strtod.

This removes the dependency on Gay's strtod.

Review URL: http://codereview.chromium.org/4060001

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5778 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 17ac8903
......@@ -40,6 +40,7 @@ SOURCES = {
api.cc
assembler.cc
ast.cc
bignum.cc
bootstrapper.cc
builtins.cc
cached-powers.cc
......
This diff is collapsed.
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_BIGNUM_H_
#define V8_BIGNUM_H_
namespace v8 {
namespace internal {
class Bignum {
public:
// 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately.
// This bignum can encode much bigger numbers, since it contains an
// exponent.
static const int kMaxSignificantBits = 3584;
Bignum();
void AssignUInt16(uint16_t value);
void AssignUInt64(uint64_t value);
void AssignBignum(const Bignum& other);
void AssignDecimalString(Vector<const char> value);
void AssignHexString(Vector<const char> value);
void AssignPowerUInt16(uint16_t base, int exponent);
void AddUInt16(uint16_t operand);
void AddUInt64(uint64_t operand);
void AddBignum(const Bignum& other);
// Precondition: this >= other.
void SubtractBignum(const Bignum& other);
void Square();
void ShiftLeft(int shift_amount);
void MultiplyByUInt32(uint32_t factor);
void MultiplyByUInt64(uint64_t factor);
void MultiplyByPowerOfTen(int exponent);
void Times10() { return MultiplyByUInt32(10); }
// Pseudocode:
// int result = this / other;
// this = this % other;
// In the worst case this function is in O(this/other).
uint16_t DivideModuloIntBignum(const Bignum& other);
bool ToHexString(char* buffer, int buffer_size) const;
static int Compare(const Bignum& a, const Bignum& b);
static bool Equal(const Bignum& a, const Bignum& b) {
return Compare(a, b) == 0;
}
static bool LessEqual(const Bignum& a, const Bignum& b) {
return Compare(a, b) <= 0;
}
static bool Less(const Bignum& a, const Bignum& b) {
return Compare(a, b) < 0;
}
// Returns Compare(a + b, c);
static int PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c);
// Returns a + b == c
static bool PlusEqual(const Bignum& a, const Bignum& b, const Bignum& c) {
return PlusCompare(a, b, c) == 0;
}
// Returns a + b <= c
static bool PlusLessEqual(const Bignum& a, const Bignum& b, const Bignum& c) {
return PlusCompare(a, b, c) <= 0;
}
// Returns a + b < c
static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) {
return PlusCompare(a, b, c) < 0;
}
private:
typedef uint32_t Chunk;
typedef uint64_t DoubleChunk;
static const int kChunkSize = sizeof(Chunk) * 8;
static const int kDoubleChunkSize = sizeof(DoubleChunk) * 8;
// With bigit size of 28 we loose some bits, but a double still fits easily
// into two chunks, and more importantly we can use the Comba multiplication.
static const int kBigitSize = 28;
static const Chunk kBigitMask = (1 << kBigitSize) - 1;
// Every instance allocates kBigitLength chunks on the stack. Bignums cannot
// grow. There are no checks if the stack-allocated space is sufficient.
static const int kBigitCapacity = kMaxSignificantBits / kBigitSize;
void EnsureCapacity(int size) {
if (size > kBigitCapacity) {
UNREACHABLE();
}
}
void Align(const Bignum& other);
void Clamp();
bool IsClamped() const;
void Zero();
// Requires this to have enough capacity (no tests done).
// Updates used_digits_ if necessary.
// by must be < kBigitSize.
void BigitsShiftLeft(int shift_amount);
// BigitLength includes the "hidden" digits encoded in the exponent.
int BigitLength() const { return used_digits_ + exponent_; }
Chunk BigitAt(int index) const;
void SubtractTimes(const Bignum& other, int factor);
Chunk bigits_buffer_[kBigitCapacity];
// A vector backed by bigits_buffer_. This way accesses to the array are
// checked for out-of-bounds errors.
Vector<Chunk> bigits_;
int used_digits_;
// The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize).
int exponent_;
DISALLOW_COPY_AND_ASSIGN(Bignum);
};
} } // namespace v8::internal
#endif // V8_BIGNUM_H_
......@@ -82,6 +82,11 @@ class Double {
return d64_;
}
double NextDouble() const {
if (d64_ == kInfinity) return kInfinity;
return Double(d64_ + 1).value();
}
int Exponent() const {
if (IsDenormal()) return kDenormalExponent;
......@@ -120,19 +125,20 @@ class Double {
((d64 & kSignificandMask) != 0);
}
bool IsInfinite() const {
uint64_t d64 = AsUint64();
return ((d64 & kExponentMask) == kExponentMask) &&
((d64 & kSignificandMask) == 0);
}
int Sign() const {
uint64_t d64 = AsUint64();
return (d64 & kSignMask) == 0? 1: -1;
}
DiyFp UpperBoundary() const {
return DiyFp(Significand() * 2 + 1, Exponent() - 1);
}
// Returns the two boundaries of this.
// The bigger boundary (m_plus) is normalized. The lower boundary has the same
......
......@@ -31,6 +31,7 @@
#include "v8.h"
#include "strtod.h"
#include "bignum.h"
#include "cached-powers.h"
#include "double.h"
......@@ -83,44 +84,12 @@ static const double exact_powers_of_ten[] = {
// 10^22 = 0x21e19e0c9bab2400000 = 0x878678326eac9 * 2^22
10000000000000000000000.0
};
static const int kExactPowersOfTenSize = ARRAY_SIZE(exact_powers_of_ten);
extern "C" double gay_strtod(const char* s00, const char** se);
static double old_strtod(Vector<const char> buffer, int exponent) {
// gay_strtod is broken on Linux,x86. For numbers with few decimal digits
// the computation is done using floating-point operations which (on Linux)
// are prone to double-rounding errors.
// By adding several zeroes to the buffer gay_strtod falls back to a slower
// (but correct) algorithm.
const int kInsertedZeroesCount = 20;
char gay_buffer[1024];
Vector<char> gay_buffer_vector(gay_buffer, sizeof(gay_buffer));
int pos = 0;
for (int i = 0; i < buffer.length(); ++i) {
gay_buffer_vector[pos++] = buffer[i];
}
for (int i = 0; i < kInsertedZeroesCount; ++i) {
gay_buffer_vector[pos++] = '0';
}
exponent -= kInsertedZeroesCount;
gay_buffer_vector[pos++] = 'e';
if (exponent < 0) {
gay_buffer_vector[pos++] = '-';
exponent = -exponent;
}
const int kNumberOfExponentDigits = 5;
for (int i = kNumberOfExponentDigits - 1; i >= 0; i--) {
gay_buffer_vector[pos + i] = exponent % 10 + '0';
exponent /= 10;
}
pos += kNumberOfExponentDigits;
gay_buffer_vector[pos] = '\0';
return gay_strtod(gay_buffer, NULL);
}
// Maximum number of significant digits in the decimal representation.
// In fact the value is 772 (see conversions.cc), but to give us some margin
// we round up to 780.
static const int kMaxSignificantDecimalDigits = 780;
static Vector<const char> TrimLeadingZeros(Vector<const char> buffer) {
for (int i = 0; i < buffer.length(); i++) {
......@@ -142,6 +111,23 @@ static Vector<const char> TrimTrailingZeros(Vector<const char> buffer) {
}
static void TrimToMaxSignificantDigits(Vector<const char> buffer,
int exponent,
char* significant_buffer,
int* significant_exponent) {
for (int i = 0; i < kMaxSignificantDecimalDigits - 1; ++i) {
significant_buffer[i] = buffer[i];
}
// The input buffer has been trimmed. Therefore the last digit must be
// different from '0'.
ASSERT(buffer[buffer.length() - 1] != '0');
// Set the last digit to be non-zero. This is sufficient to guarantee
// correct rounding.
significant_buffer[kMaxSignificantDecimalDigits - 1] = '1';
*significant_exponent =
exponent + (buffer.length() - kMaxSignificantDecimalDigits);
}
// Reads digits from the buffer and converts them to a uint64.
// Reads in as many digits as fit into a uint64.
// When the string starts with "1844674407370955161" no further digit is read.
......@@ -374,20 +360,81 @@ static bool DiyFpStrtod(Vector<const char> buffer,
}
// Returns the correct double for the buffer*10^exponent.
// The variable guess should be a close guess that is either the correct double
// or its lower neighbor (the nearest double less than the correct one).
// Preconditions:
// buffer.length() + exponent <= kMaxDecimalPower + 1
// buffer.length() + exponent > kMinDecimalPower
// buffer.length() <= kMaxDecimalSignificantDigits
static double BignumStrtod(Vector<const char> buffer,
int exponent,
double guess) {
if (guess == V8_INFINITY) {
return guess;
}
DiyFp upper_boundary = Double(guess).UpperBoundary();
ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1);
ASSERT(buffer.length() + exponent > kMinDecimalPower);
ASSERT(buffer.length() <= kMaxSignificantDecimalDigits);
// Make sure that the Bignum will be able to hold all our numbers.
// Our Bignum implementation has a separate field for exponents. Shifts will
// consume at most one bigit (< 64 bits).
// ln(10) == 3.3219...
ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits);
Bignum input;
Bignum boundary;
input.AssignDecimalString(buffer);
boundary.AssignUInt64(upper_boundary.f());
if (exponent >= 0) {
input.MultiplyByPowerOfTen(exponent);
} else {
boundary.MultiplyByPowerOfTen(-exponent);
}
if (upper_boundary.e() > 0) {
boundary.ShiftLeft(upper_boundary.e());
} else {
input.ShiftLeft(-upper_boundary.e());
}
int comparison = Bignum::Compare(input, boundary);
if (comparison < 0) {
return guess;
} else if (comparison > 0) {
return Double(guess).NextDouble();
} else if ((Double(guess).Significand() & 1) == 0) {
// Round towards even.
return guess;
} else {
return Double(guess).NextDouble();
}
}
double Strtod(Vector<const char> buffer, int exponent) {
Vector<const char> left_trimmed = TrimLeadingZeros(buffer);
Vector<const char> trimmed = TrimTrailingZeros(left_trimmed);
exponent += left_trimmed.length() - trimmed.length();
if (trimmed.length() == 0) return 0.0;
if (trimmed.length() > kMaxSignificantDecimalDigits) {
char significant_buffer[kMaxSignificantDecimalDigits];
int significant_exponent;
TrimToMaxSignificantDigits(trimmed, exponent,
significant_buffer, &significant_exponent);
trimmed =
Vector<const char>(significant_buffer, kMaxSignificantDecimalDigits);
exponent = significant_exponent;
}
if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) return V8_INFINITY;
if (exponent + trimmed.length() <= kMinDecimalPower) return 0.0;
double result;
if (DoubleStrtod(trimmed, exponent, &result) ||
DiyFpStrtod(trimmed, exponent, &result)) {
return result;
double guess;
if (DoubleStrtod(trimmed, exponent, &guess) ||
DiyFpStrtod(trimmed, exponent, &guess)) {
return guess;
}
return old_strtod(trimmed, exponent);
return BignumStrtod(trimmed, exponent, guess);
}
} } // namespace v8::internal
......@@ -41,6 +41,7 @@ SOURCES = {
'test-alloc.cc',
'test-api.cc',
'test-ast.cc',
'test-bignum.cc',
'test-circular-queue.cc',
'test-compiler.cc',
'test-conversions.cc',
......
This diff is collapsed.
......@@ -4,7 +4,10 @@
#include "v8.h"
#include "bignum.h"
#include "cctest.h"
#include "diy-fp.h"
#include "double.h"
#include "strtod.h"
using namespace v8::internal;
......@@ -202,11 +205,14 @@ TEST(Strtod) {
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/1e22. 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));
// Some random values.
CHECK_EQ(358416272e-33, StrtodChar("358416272", -33));
CHECK_EQ(104110013277974872254e-225,
StrtodChar("104110013277974872254", -225));
......@@ -252,4 +258,158 @@ TEST(Strtod) {
StrtodChar("1234567890123456789052345", 114));
CHECK_EQ(1234567890123456789052345e115,
StrtodChar("1234567890123456789052345", 115));
// Boundary cases.
// 0x1FFFFFFFFFFFF * 2^3 = 72057594037927928
// next: 72057594037927936
// boundary: 72057594037927932
CHECK_EQ(72057594037927928.0, StrtodChar("72057594037927928", 0));
CHECK_EQ(72057594037927936.0, StrtodChar("72057594037927936", 0));
CHECK_EQ(72057594037927936.0, StrtodChar("72057594037927932", 0));
CHECK_EQ(72057594037927928.0, StrtodChar("7205759403792793199999", -5));
CHECK_EQ(72057594037927936.0, StrtodChar("7205759403792793200001", -5));
// 0x1FFFFFFFFFFFF * 2^10 = 9223372036854774784
// next: 9223372036854775808
// boundary: 9223372036854775296
CHECK_EQ(9223372036854774784.0, StrtodChar("9223372036854774784", 0));
CHECK_EQ(9223372036854775808.0, StrtodChar("9223372036854775808", 0));
CHECK_EQ(9223372036854775296.0, StrtodChar("9223372036854775296", 0));
CHECK_EQ(9223372036854774784.0, StrtodChar("922337203685477529599999", -5));
CHECK_EQ(9223372036854775808.0, StrtodChar("922337203685477529600001", -5));
// 0x1FFFFFFFFFFFF * 2^50 = 10141204801825834086073718800384
// next: 10141204801825835211973625643008
// boundary: 10141204801825834649023672221696
CHECK_EQ(10141204801825834086073718800384.0,
StrtodChar("10141204801825834086073718800384", 0));
CHECK_EQ(10141204801825835211973625643008.0,
StrtodChar("10141204801825835211973625643008", 0));
CHECK_EQ(10141204801825834649023672221696.0,
StrtodChar("10141204801825834649023672221696", 0));
CHECK_EQ(10141204801825834086073718800384.0,
StrtodChar("1014120480182583464902367222169599999", -5));
CHECK_EQ(10141204801825835211973625643008.0,
StrtodChar("1014120480182583464902367222169600001", -5));
// 0x1FFFFFFFFFFFF * 2^99 = 5708990770823838890407843763683279797179383808
// next: 5708990770823839524233143877797980545530986496
// boundary: 5708990770823839207320493820740630171355185152
CHECK_EQ(5708990770823838890407843763683279797179383808.0,
StrtodChar("5708990770823838890407843763683279797179383808", 0));
CHECK_EQ(5708990770823839524233143877797980545530986496.0,
StrtodChar("5708990770823839524233143877797980545530986496", 0));
CHECK_EQ(5708990770823839207320493820740630171355185152.0,
StrtodChar("5708990770823839207320493820740630171355185152", 0));
CHECK_EQ(5708990770823838890407843763683279797179383808.0,
StrtodChar("5708990770823839207320493820740630171355185151999", -3));
CHECK_EQ(5708990770823839524233143877797980545530986496.0,
StrtodChar("5708990770823839207320493820740630171355185152001", -3));
}
static int CompareBignumToDiyFp(const Bignum& bignum_digits,
int bignum_exponent,
DiyFp diy_fp) {
Bignum bignum;
bignum.AssignBignum(bignum_digits);
Bignum other;
other.AssignUInt64(diy_fp.f());
if (bignum_exponent >= 0) {
bignum.MultiplyByPowerOfTen(bignum_exponent);
} else {
other.MultiplyByPowerOfTen(-bignum_exponent);
}
if (diy_fp.e() >= 0) {
other.ShiftLeft(diy_fp.e());
} else {
bignum.ShiftLeft(-diy_fp.e());
}
return Bignum::Compare(bignum, other);
}
static bool CheckDouble(Vector<const char> buffer,
int exponent,
double to_check) {
DiyFp lower_boundary;
DiyFp upper_boundary;
Bignum input_digits;
input_digits.AssignDecimalString(buffer);
if (to_check == 0.0) {
const double kMinDouble = 4e-324;
// Check that the buffer*10^exponent < (0 + kMinDouble)/2.
Double d(kMinDouble);
d.NormalizedBoundaries(&lower_boundary, &upper_boundary);
return CompareBignumToDiyFp(input_digits, exponent, lower_boundary) <= 0;
}
if (to_check == V8_INFINITY) {
const double kMaxDouble = 1.7976931348623157e308;
// Check that the buffer*10^exponent >= boundary between kMaxDouble and inf.
Double d(kMaxDouble);
d.NormalizedBoundaries(&lower_boundary, &upper_boundary);
return CompareBignumToDiyFp(input_digits, exponent, upper_boundary) >= 0;
}
Double d(to_check);
d.NormalizedBoundaries(&lower_boundary, &upper_boundary);
if ((d.Significand() & 1) == 0) {
return CompareBignumToDiyFp(input_digits, exponent, lower_boundary) >= 0 &&
CompareBignumToDiyFp(input_digits, exponent, upper_boundary) <= 0;
} else {
return CompareBignumToDiyFp(input_digits, exponent, lower_boundary) > 0 &&
CompareBignumToDiyFp(input_digits, exponent, upper_boundary) < 0;
}
}
// Copied from v8.cc and adapted to make the function deterministic.
static uint32_t DeterministicRandom() {
// Random number generator using George Marsaglia's MWC algorithm.
static uint32_t hi = 0;
static uint32_t lo = 0;
// Initialization values don't have any special meaning. (They are the result
// of two calls to random().)
if (hi == 0) hi = 0xbfe166e7;
if (lo == 0) lo = 0x64d1c3c9;
// Mix the bits.
hi = 36969 * (hi & 0xFFFF) + (hi >> 16);
lo = 18273 * (lo & 0xFFFF) + (lo >> 16);
return (hi << 16) + (lo & 0xFFFF);
}
static const int kBufferSize = 1024;
static const int kShortStrtodRandomCount = 2;
static const int kLargeStrtodRandomCount = 2;
TEST(RandomStrtod) {
char buffer[kBufferSize];
for (int length = 1; length < 15; length++) {
for (int i = 0; i < kShortStrtodRandomCount; ++i) {
int pos = 0;
for (int j = 0; j < length; ++j) {
buffer[pos++] = random() % 10 + '0';
}
int exponent = DeterministicRandom() % (25*2 + 1) - 25 - length;
buffer[pos] = '\0';
Vector<const char> vector(buffer, pos);
double strtod_result = Strtod(vector, exponent);
CHECK(CheckDouble(vector, exponent, strtod_result));
}
}
for (int length = 15; length < 800; length += 2) {
for (int i = 0; i < kLargeStrtodRandomCount; ++i) {
int pos = 0;
for (int j = 0; j < length; ++j) {
buffer[pos++] = random() % 10 + '0';
}
int exponent = DeterministicRandom() % (308*2 + 1) - 308 - length;
buffer[pos] = '\0';
Vector<const char> vector(buffer, pos);
double strtod_result = Strtod(vector, exponent);
CHECK(CheckDouble(vector, exponent, strtod_result));
}
}
}
......@@ -280,6 +280,10 @@
'../../src/ast.cc',
'../../src/ast-inl.h',
'../../src/ast.h',
'../../src/bignum.cc',
'../../src/bignum.h',
'../../src/bignum-dtoa.cc',
'../../src/bignum-dtoa.h',
'../../src/bootstrapper.cc',
'../../src/bootstrapper.h',
'../../src/builtins.cc',
......
......@@ -144,6 +144,22 @@
/>
</FileConfiguration>
</File>
<File
RelativePath="..\..\src\bignum.cc"
>
</File>
<File
RelativePath="..\..\src\bignum.h"
>
</File>
<File
RelativePath="..\..\src\bignum-dtoa.cc"
>
</File>
<File
RelativePath="..\..\src\bignum-dtoa.h"
>
</File>
<File
RelativePath="..\..\src\dtoa.cc"
>
......@@ -240,6 +256,22 @@
RelativePath="..\..\src\ast.h"
>
</File>
<File
RelativePath="..\..\src\bignum.cc"
>
</File>
<File
RelativePath="..\..\src\bignum.h"
>
</File>
<File
RelativePath="..\..\src\bignum-dtoa.cc"
>
</File>
<File
RelativePath="..\..\src\bignum-dtoa.h"
>
</File>
<File
RelativePath="..\..\src\bootstrapper.cc"
>
......
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