Improve Math.round(). Fix the bug in r4146. Further improve performance by checking...
authoroleg@chromium.org <oleg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 18 Mar 2010 13:00:57 +0000 (13:00 +0000)
committeroleg@chromium.org <oleg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 18 Mar 2010 13:00:57 +0000 (13:00 +0000)
Review URL: http://codereview.chromium.org/1008004

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4180 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/math.js
src/objects-inl.h
src/objects.h
src/runtime.cc
src/runtime.h
test/mjsunit/math-round.js

index 0623cb82ddcb1f1ce0b19c5278b6c3c0b379a804..d17a6fdf5b0d7a992110bad7b81680cdcefe9b7b 100644 (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
index 4cc9b9f8340238549a12136218672eb205771007..e7daa2d68c87eea5a3202beaa905e21c162d8d67 100644 (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)
 
 
index b3fe448463aa425c4b6b2cf2d1516f2798254bff..d6965702015d51976ad59e34c46c9e00c3f51736 100644 (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
index 713ddc8d313550c0a4e25805aa9f72bd83875bed..446a614c82f9af0ec8e2147c8b0878aa3f2580da 100644 (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));
 }
 
 
index 05f09f6f3ef87e3d4c8ea798c39f538096549ac6..4175902c450eac3a266e6ff157be159e0f4ecb59 100644 (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) \
index d80a1036fb495a51d51a5afc07fdc08fb003c61b..3b060885f4d2ddeda08f167def11a98d1352590a 100644 (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));