Improve Math.round(). Fix the bug in r4146. Further improve performance by checking the exponent instead of comparing doubles. Add several tests for numbers near the limits of SMI and several tests from WebKit.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4180 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
oleg@chromium.org 2010-03-18 13:00:57 +00:00
parent c8a2735762
commit 825a5d741f
6 changed files with 93 additions and 8 deletions

View File

@ -171,7 +171,7 @@ function MathRandom() {
// ECMA 262 - 15.8.2.15
function MathRound(x) {
if (!IS_NUMBER(x)) x = ToNumber(x);
return %Math_round(x);
return %RoundNumber(x);
}
// ECMA 262 - 15.8.2.16

View File

@ -1121,6 +1121,17 @@ void HeapNumber::set_value(double value) {
}
int HeapNumber::get_exponent() {
return ((READ_INT_FIELD(this, kExponentOffset) & kExponentMask) >>
kExponentShift) - kExponentBias;
}
int HeapNumber::get_sign() {
return READ_INT_FIELD(this, kExponentOffset) & kSignMask;
}
ACCESSORS(JSObject, properties, FixedArray, kPropertiesOffset)

View File

@ -1094,6 +1094,9 @@ class HeapNumber: public HeapObject {
void HeapNumberVerify();
#endif
inline int get_exponent();
inline int get_sign();
// Layout description.
static const int kValueOffset = HeapObject::kHeaderSize;
// IEEE doubles are two 32 bit words. The first is just mantissa, the second

View File

@ -5350,16 +5350,38 @@ static Object* Runtime_Math_pow_cfunction(Arguments args) {
}
static Object* Runtime_Math_round(Arguments args) {
static Object* Runtime_RoundNumber(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
Counters::math_round.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
double integer = ceil(x);
if (integer - x > 0.5) { integer -= 1.0; }
return Heap::NumberFromDouble(integer);
if (!args[0]->IsHeapNumber()) {
// Must be smi. Return the argument unchanged for all the other types
// to make fuzz-natives test happy.
return args[0];
}
HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
double value = number->value();
int exponent = number->get_exponent();
int sign = number->get_sign();
// We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
// should be rounded to 2^30, which is not smi.
if (!sign && exponent <= kSmiValueSize - 3) {
return Smi::FromInt(static_cast<int>(value + 0.5));
}
// If the magnitude is big enough, there's no place for fraction part. If we
// try to add 0.5 to this number, 1.0 will be added instead.
if (exponent >= 52) {
return number;
}
if (sign && value >= -0.5) return Heap::minus_zero_value();
return Heap::NumberFromDouble(floor(value + 0.5));
}

View File

@ -145,7 +145,7 @@ namespace internal {
F(Math_log, 1, 1) \
F(Math_pow, 2, 1) \
F(Math_pow_cfunction, 2, 1) \
F(Math_round, 1, 1) \
F(RoundNumber, 1, 1) \
F(Math_sin, 1, 1) \
F(Math_sqrt, 1, 1) \
F(Math_tan, 1, 1) \

View File

@ -50,3 +50,52 @@ assertEquals(-9007199254740990, Math.round(-9007199254740990));
assertEquals(-9007199254740991, Math.round(-9007199254740991));
assertEquals(Number.MAX_VALUE, Math.round(Number.MAX_VALUE));
assertEquals(-Number.MAX_VALUE, Math.round(-Number.MAX_VALUE));
assertEquals(536870911, Math.round(536870910.5));
assertEquals(536870911, Math.round(536870911));
assertEquals(536870911, Math.round(536870911.4));
assertEquals(536870912, Math.round(536870911.5));
assertEquals(536870912, Math.round(536870912));
assertEquals(536870912, Math.round(536870912.4));
assertEquals(536870913, Math.round(536870912.5));
assertEquals(536870913, Math.round(536870913));
assertEquals(536870913, Math.round(536870913.4));
assertEquals(1073741823, Math.round(1073741822.5));
assertEquals(1073741823, Math.round(1073741823));
assertEquals(1073741823, Math.round(1073741823.4));
assertEquals(1073741824, Math.round(1073741823.5));
assertEquals(1073741824, Math.round(1073741824));
assertEquals(1073741824, Math.round(1073741824.4));
assertEquals(1073741825, Math.round(1073741824.5));
assertEquals(2147483647, Math.round(2147483646.5));
assertEquals(2147483647, Math.round(2147483647));
assertEquals(2147483647, Math.round(2147483647.4));
assertEquals(2147483648, Math.round(2147483647.5));
assertEquals(2147483648, Math.round(2147483648));
assertEquals(2147483648, Math.round(2147483648.4));
assertEquals(2147483649, Math.round(2147483648.5));
// Tests based on WebKit LayoutTests
assertEquals(0, Math.round(0.4));
assertEquals(-0, Math.round(-0.4));
assertEquals(-0, Math.round(-0.5));
assertEquals(1, Math.round(0.6));
assertEquals(-1, Math.round(-0.6));
assertEquals(2, Math.round(1.5));
assertEquals(2, Math.round(1.6));
assertEquals(-2, Math.round(-1.6));
assertEquals(8640000000000000, Math.round(8640000000000000));
assertEquals(8640000000000001, Math.round(8640000000000001));
assertEquals(8640000000000002, Math.round(8640000000000002));
assertEquals(9007199254740990, Math.round(9007199254740990));
assertEquals(9007199254740991, Math.round(9007199254740991));
assertEquals(1.7976931348623157e+308, Math.round(1.7976931348623157e+308));
assertEquals(-8640000000000000, Math.round(-8640000000000000));
assertEquals(-8640000000000001, Math.round(-8640000000000001));
assertEquals(-8640000000000002, Math.round(-8640000000000002));
assertEquals(-9007199254740990, Math.round(-9007199254740990));
assertEquals(-9007199254740991, Math.round(-9007199254740991));
assertEquals(-1.7976931348623157e+308, Math.round(-1.7976931348623157e+308));
assertEquals(Infinity, Math.round(Infinity));
assertEquals(-Infinity, Math.round(-Infinity));