From 4f4a9cacd29af57970a4bf34569a2d39c87216e7 Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Sun, 25 Dec 2011 15:16:12 +0100 Subject: [PATCH] Bug 622348 - JavaScript Math.round incorrect for (2^53)-1. r=Waldo --HG-- extra : rebase_source : 51a780cf5ce9cbcc84c56d8a8dbe2c5d9867b36b --- js/src/jit-test/tests/basic/mathRoundBig.js | 10 +++++ js/src/jsmath.cpp | 43 ++++++++++++++------- js/src/jsmath.h | 3 -- js/src/jsnum.h | 17 ++++---- 4 files changed, 48 insertions(+), 25 deletions(-) create mode 100644 js/src/jit-test/tests/basic/mathRoundBig.js diff --git a/js/src/jit-test/tests/basic/mathRoundBig.js b/js/src/jit-test/tests/basic/mathRoundBig.js new file mode 100644 index 000000000000..e01ffa4f5457 --- /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); diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp index 10da6250e806..991baa270f8b 100644 --- a/js/src/jsmath.cpp +++ b/js/src/jsmath.cpp @@ -577,26 +577,39 @@ js_copysign(double x, double y) } #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 diff --git a/js/src/jsmath.h b/js/src/jsmath.h index 17b824315c26..9e2efa69fcf8 100644 --- a/js/src/jsmath.h +++ b/js/src/jsmath.h @@ -121,7 +121,4 @@ 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___ */ diff --git a/js/src/jsnum.h b/js/src/jsnum.h index d848428bf501..658e9aebcb2b 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -65,6 +65,16 @@ #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) @@ -77,13 +87,6 @@ typedef union jsdpun { 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) {