vendredi 26 juin 2015

ActionScript: Number.toExponential returns incorrect results for some values

I found at least two numbers for which ActionScript's Number.toExponential(20) returns incorrect results.

The most obvious value is 0.

trace(Number(0).toExponential(20)); // 0.00000000000000000000e-16
trace(Number(0).toExponential(2));  // 0.00e-16
trace(Number(0).toExponential(1));  // 0.0e-16
trace(Number(0).toExponential(0));  // 1e-15 - even worse!

I've made a class Double for playing around with Numbers (which are IEEE 754 double-precision binary floating point format numbers). It takes a Number and extracts all the bits into sign, bits and significand (the latter is printed including the implicit leading bit), or can generate a Number provided sign, bits and significand. There are methods to get the next and prior nearest representable numbers. Here's what 0 looks like internally:

[Double number=0.00000000000000000000e-16 sign=1 exponent=-1023 isZero
significand=00000000000000000000000000000000000000000000000000000]

The other incorrect results are the nearest representable Number greater than Number.MAX_VALUE / 2 and Number.MAX_VALUE / 4.

// Number.MAX_VALUE / 2.
trace(Number(8.98846567431157854072e+307).toExponential(20)); // 8.98846567431157854072e+307
// Nearest representable Number greater than Number.MAX_VALUE / 2. Printed incorrectly!
trace(Number(8.98846567431157953864e+307).toExponential(20)); // 0.e+327
// A Number two ULPs greater than Number.MAX_VALUE / 2.
trace(Number(8.98846567431158153448e+307).toExponential(20)); // 8.98846567431158153448e+307
// Nearest representable Number greater than Number.MAX_VALUE / 4. Printed incorrectly!
trace(Number(4.49423283715578976932e+307).toExponential(20)); // 0.e+327

Here's what these 4 numbers look like internally:

[Double number=8.98846567431157854072e+307 sign=1 exponent=1022 
significand=11111111111111111111111111111111111111111111111111111]
[Double number=0.e+327 sign=1 exponent=1023 
significand=10000000000000000000000000000000000000000000000000000]
[Double number=8.98846567431158153448e+307 sign=1 exponent=1023 
significand=10000000000000000000000000000000000000000000000000001]
[Double number=0.e+327 sign=1 exponent=1022 
significand=10000000000000000000000000000000000000000000000000000]

So far all three numbers that are printed incorrectly have a 0 significand (with implicit leading 1), and the exponent values of 1023 (Emax for normal numbers), 1022 (Emax - 1), or -1023 (special exponent value for ±zero and subnormal numbers, Emin - 1).

Looks like toExponent doesn't correctly handle at least these three cases . Are there any other numbers that would print incorrectly?

Trying to check if toExponential would produce correct results with other precision values.

trace(Number(8.98846567431157953864e+307).toExponential(2)); // 0.e+309
trace(Number(8.98846567431157953864e+307).toExponential(1)); // 0.e+308
trace(Number(8.98846567431157953864e+307).toExponential(0)); // 1e+308 - less bad!

If anyone interested in my Double class I will post it. I've also written faster implementations next(x:Number) and prior(x:Number) functions which work pretty fast without messing with internal bits of the numbers.

References: http://ift.tt/1pP815z

Aucun commentaire:

Enregistrer un commentaire