Bug 622348 - JavaScript Math.round incorrect for (2^53)-1. r=Waldo
authorTom Schuster <evilpies@gmail.com>
Sun, 25 Dec 2011 15:16:12 +0100
changeset 84837 37348c68ba0b7bdfd6affeebd30cd7d8cd68a987
parent 84836 0341759ffb4cd88254b45b09bf3ecaf35a34acf1
child 84838 196f5b34b6e3979195a2cf5068eccaa96dc1133d
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs622348
milestone12.0a1
Bug 622348 - JavaScript Math.round incorrect for (2^53)-1. r=Waldo
js/src/jit-test/tests/basic/mathRoundBig.js
js/src/jsmath.cpp
js/src/jsmath.h
js/src/jsnum.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/mathRoundBig.js
@@ -0,0 +1,10 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+assertEq(Math.round(9007199254740991), 9007199254740991);
+assertEq(Math.round(-19007199254740990), -19007199254740990);
+
+assertEq(Math.round("9007199254740991"), 9007199254740991);
+assertEq(Math.round("-19007199254740990"), -19007199254740990);
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -572,36 +572,49 @@ js_copysign(double x, double y)
     xu.d = x;
     yu.d = y;
     xu.s.hi &= ~JSDOUBLE_HI32_SIGNBIT;
     xu.s.hi |= yu.s.hi & JSDOUBLE_HI32_SIGNBIT;
     return xu.d;
 }
 #endif
 
-jsdouble
-js_math_round_impl(jsdouble x)
-{
-    return js_copysign(floor(x + 0.5), x);
-}
 
-JSBool
+JSBool /* ES5 15.8.2.15. */
 js_math_round(JSContext *cx, uintN argc, Value *vp)
 {
-    jsdouble x, z;
+    CallArgs args = CallArgsFromVp(argc, vp);
 
-    if (argc == 0) {
-        vp->setDouble(js_NaN);
-        return JS_TRUE;
+    if (args.length() == 0) {
+        args.rval().setDouble(js_NaN);
+        return true;
     }
-    if (!ToNumber(cx, vp[2], &x))
-        return JS_FALSE;
-    z = js_copysign(floor(x + 0.5), x);
-    vp->setNumber(z);
-    return JS_TRUE;
+
+    double x;
+    if (!ToNumber(cx, args[0], &x))
+        return false;
+
+    int32_t i;
+    if (JSDOUBLE_IS_INT32(x, &i)) { 
+        args.rval().setInt32(i);
+        return true;
+    }
+
+    jsdpun u;
+    u.d = x;
+
+    /* Some numbers are so big that adding 0.5 would give the wrong number */
+    int exponent = ((u.s.hi & JSDOUBLE_HI32_EXPMASK) >> JSDOUBLE_HI32_EXPSHIFT) - JSDOUBLE_EXPBIAS;
+    if (exponent >= 52) {
+        args.rval().setNumber(x);
+        return true;
+    }
+
+    args.rval().setNumber(js_copysign(floor(x + 0.5), x));
+    return true;
 }
 
 static JSBool
 math_sin(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble x, z;
 
     if (argc == 0) {
--- a/js/src/jsmath.h
+++ b/js/src/jsmath.h
@@ -116,12 +116,9 @@ extern JSBool
 js_math_pow(JSContext *cx, uintN argc, js::Value *vp);
 
 extern jsdouble
 js_math_ceil_impl(jsdouble x);
 
 extern jsdouble
 js_math_floor_impl(jsdouble x);
 
-extern jsdouble
-js_math_round_impl(jsdouble x);
-
 #endif /* jsmath_h___ */
--- a/js/src/jsnum.h
+++ b/js/src/jsnum.h
@@ -60,35 +60,38 @@
  * the high and low words are in big endian order).
  */
 #if defined(__arm) || defined(__arm32__) || defined(__arm26__) || defined(__arm__)
 #if !defined(__VFP_FP__)
 #define FPU_IS_ARM_FPA
 #endif
 #endif
 
+/* Low-level floating-point predicates. See bug 640494. */
+#define JSDOUBLE_HI32_SIGNBIT   0x80000000
+#define JSDOUBLE_HI32_EXPMASK   0x7ff00000
+#define JSDOUBLE_HI32_MANTMASK  0x000fffff
+#define JSDOUBLE_HI32_NAN       0x7ff80000
+#define JSDOUBLE_LO32_NAN       0x00000000
+
+#define JSDOUBLE_HI32_EXPSHIFT  20
+#define JSDOUBLE_EXPBIAS        1023
+
 typedef union jsdpun {
     struct {
 #if defined(IS_LITTLE_ENDIAN) && !defined(FPU_IS_ARM_FPA)
         uint32_t lo, hi;
 #else
         uint32_t hi, lo;
 #endif
     } s;
     uint64_t u64;
     jsdouble d;
 } jsdpun;
 
-/* Low-level floating-point predicates. See bug 640494. */
-#define JSDOUBLE_HI32_SIGNBIT   0x80000000
-#define JSDOUBLE_HI32_EXPMASK   0x7ff00000
-#define JSDOUBLE_HI32_MANTMASK  0x000fffff
-#define JSDOUBLE_HI32_NAN       0x7ff80000
-#define JSDOUBLE_LO32_NAN       0x00000000
-
 static inline int
 JSDOUBLE_IS_NaN(jsdouble d)
 {
     jsdpun u;
     u.d = d;
     return (u.s.hi & JSDOUBLE_HI32_NAN) == JSDOUBLE_HI32_NAN;
 }