diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index 59cff6261d20..2fbb02bb9ecf 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -965,9 +965,92 @@ js_ValueToECMAInt32(JSContext *cx, jsval *vp) return js_DoubleToECMAInt32(d); } +/* + * From the ES3 spec, 9.5 + * 2. If Result(1) is NaN, +0, -0, +Inf, or -Inf, return +0. + * 3. Compute sign(Result(1)) * floor(abs(Result(1))). + * 4. Compute Result(3) modulo 2^32; that is, a finite integer value k of Number + * type with positive sign and less than 2^32 in magnitude such the mathematical + * difference of Result(3) and k is mathematically an integer multiple of 2^32. + * 5. If Result(4) is greater than or equal to 2^31, return Result(4)- 2^32, + * otherwise return Result(4). + */ int32 js_DoubleToECMAInt32(jsdouble d) { +#ifdef __i386__ + jsdpun du, duh, two32; + uint32 di_h, u_tmp, expon, shift_amount; + int32 mask32; + + /* + * Algorithm Outline + * Step 1. If d is NaN, +/-Inf or |d|>=2^84 or |d|<1, then return 0 + * All of this is implemented based on an exponent comparison. + * Step 2. If |d|<2^31, then return (int)d + * The cast to integer (conversion in RZ mode) returns the correct result. + * Step 3. If |d|>=2^32, d:=fmod(d, 2^32) is taken -- but without a call + * Step 4. If |d|>=2^31, then the fractional bits are cleared before + * applying the correction by 2^32: d - sign(d)*2^32 + * Step 5. Return (int)d + */ + + du.d = d; + di_h = du.s.hi; + + u_tmp = (di_h & 0x7ff00000) - 0x3ff00000; + if (u_tmp >= (0x45300000-0x3ff00000)) { + // d is Nan, +/-Inf or +/-0, or |d|>=2^(32+52) or |d|<1, in which case result=0 + return 0; + } + + if (u_tmp < 0x01f00000) { + // |d|<2^31 + return int32_t(d); + } + + if (u_tmp > 0x01f00000) { + // |d|>=2^32 + expon = u_tmp >> 20; + shift_amount = expon - 21; + duh.u64 = du.u64; + mask32 = 0x80000000; + if (shift_amount < 32) { + mask32 >>= shift_amount; + duh.s.hi = du.s.hi & mask32; + duh.s.lo = 0; + } else { + mask32 >>= (shift_amount-32); + duh.s.hi = du.s.hi; + duh.s.lo = du.s.lo & mask32; + } + du.d -= duh.d; + } + + di_h = du.s.hi; + + // eliminate fractional bits + u_tmp = (di_h & 0x7ff00000); + if (u_tmp >= 0x41e00000) { + // |d|>=2^31 + expon = u_tmp >> 20; + shift_amount = expon - (0x3ff - 11); + mask32 = 0x80000000; + if (shift_amount < 32) { + mask32 >>= shift_amount; + du.s.hi &= mask32; + du.s.lo = 0; + } else { + mask32 >>= (shift_amount-32); + du.s.lo &= mask32; + } + two32.s.hi = 0x41f00000 ^ (du.s.hi & 0x80000000); + two32.s.lo = 0; + du.d -= two32.d; + } + + return int32(du.d); +#else int32 i; jsdouble two32, two31; @@ -983,6 +1066,7 @@ js_DoubleToECMAInt32(jsdouble d) d = fmod(d, two32); d = (d >= 0) ? floor(d) : ceil(d) + two32; return (int32) (d >= two31 ? d - two32 : d); +#endif } uint32