0.2 + 0.1 = 0.30000000000000004

This post is part of a series about cross-compiling ActionScript to JavaScript. In order to illustrate some fundamental problems of cross-compiling ActionScript to JavaScript I would like to start with this simple JavaScript expression:

alert( 0.2 + 0.1 );

It might surprise you that Chrome, Safari, Firefox, and IE8 all report 0.30000000000000004. The bad news is that the result is incorrect, the good news is that we get an incorrect result consistently across all most popular browsers.

This astonishing result should raise some questions:

  • Is this a bug?
  • If so, why don’t they fix it in the browsers?
  • If not, is this defined in the JavaScript language specification, ECMA-262?

But even worse:

  • What if ActionScript correctly returned 0.3 for “0.2 + 0.1″?

Then we would be in serious trouble. Let me walk you through this…

 

Should the cross-compiler leave the expression “0.2 + 0.1″ unchanged when translating to JavaScript?

Then the result would not be 0.3 when run in the browser and the internal consistency of the generated JavaScript code might be compromised, because our ActionScript code assumed 0.2 + 0.1 to be 0.3.

 

Should the cross-compiler inject code that makes sure that the result will be 0.3 in JavaScript?

How? For example like this:

alert((0.2*10 + 0.1*10) / 10);

That doesn’t sound like a good idea. Any modification like the code above, or injecting Math.round() etc. would introduce tons of unwanted side-effects.

 

Should the cross-compiler try to evaluate values to constants at compile time, thus avoiding the problem altogether?

Like this:

alert( 0.3 );

Be warned, I am taking you for a ride. The code above is not a good solution for several reasons. For one, while constant folding may avoid the problem in this case, you could easily construct cases where the cross-compiler cannot evaluate values to constants at compile-time:

function addNumbers( left, right )
{
   return left + right;
}
alert( addNumbers(0.2, 0.1) );

The main reason why injecting 0.3 is not a good idea is that it is not necessary. In fact, constant folding would create unwanted results in this case. If you think this is crazy-talk, read on…

 

Happy End

So, are we in trouble with cross-compiling “0.2 + 0.1″ from ActionScript to JavaScript, or not? What does ActionScript return?

0.30000000000000004

Lucky me! That’s all we need to know: We consistently get the same wrong result in ActionScript and JavaScript. In other words the cross-compiler should leave numeric expressions like “0.2 + 0.1″ unchanged.

 

Epilog

The cross-compiler’s most important goal is preserving the internal consistency of the input code in the target code. ActionScript code should ideally yield the same results when compiled to SWF and run in the FlashPlayer as code that is cross-compiled to JavaScript and run in the browser. Whether the results are correct, or not, does not matter. Whether the results differ at runtime between source and target code does matter.

In conclusion,

  1. A cross-compiler needs to preserve the internal consistency of the input code in the generated code.
  2. A cross-compiler needs to remove ambiguities that may result in generated code that yields different runtime behavior.
  3. Even a well specified language like JavaScript may contain areas that are underspecified.
  4. Sometimes it is better to be lucky than smart.

 

(BTW, if you are interested in JavaScript oddities like “0.2 + 0.1″ I recommend watching the Crockford on JavaScript talks and reading wtfjs.com).