A directive to solve the JavaScript arithmetic precision issue
A directive would have the same benefits than use strict which is to not break existing code in platform that do not support this directive.
It would also have the same limitation that "use strict"; does, which is that it doesn't play well with (the quite common pattern of) concat'ing minified scripts together in build environment deployments, because your "use stict" declaration in one of your files bleeds over (probably unintentionally) to affecting other files when they are all combined into one file.
Maybe something like:
use "sensible arithmetic" { // .. };
Perhaps? In any case, I'm definitely not a fan of continuing the frustration that "use strict" gives us in concat-js environments.
Le 15/08/2011 20:45, Kyle Simpson a écrit :
A directive would have the same benefits than use strict which is to not break existing code in platform that do not support this directive.
It would also have the same limitation that "use strict"; does, which is that it doesn't play well with (the quite common pattern of) concat'ing minified scripts together in build environment deployments, because your "use stict" declaration in one of your files bleeds over (probably unintentionally) to affecting other files when they are all combined into one file.
I intuit that the consequences are less harmful. Strict mode can trigger some syntax errors or throw runtime errors that wouldn't happen in non-strict code. Different arithmetic is less likely to cause this kind of problem. I can't think of syntax errors (at worst, the directive can be designed to avoid additional syntax errors). But runtime would definitly be affected, that's true. Do you have an idea of a current running program that would behave significantly differently with an accidental bleed of arithmetic mode?
Maybe something like:
use "sensible arithmetic" { // .. };
Are you refering to the pragma proposal [1]? I am not used to it, but if it's applicable, that's an idea too.
David
[1] harmony:pragmas
On Mon, Aug 15, 2011 at 1:33 PM, David Bruant <david.bruant at labri.fr> wrote:
I have been watching JSConf talks recently and two of them [1] [2] mentionned the issue that JavaScript has with numbers. Something than prevent JS to be a good language to interact with a database.
The lack of 64-bits integers is also mentionned.
[snip]
var a = 0.1 + 0.2; // 0.3 // FINALLY!
It's important to note that these are two very different problems, and they almost certainly have very different solutions. In particular, adding any sort of integers won't make 0.1 change behavior. Similarly, one possible solution to the 0.1 issue is decimal floating point, which doesn't give you a 64-bit integer range.
Numbers are a very big problem space, and the choices made in JS have multiple issues (as well as some benefits). It's important to be clear on what we want to fix before we start trying to fix it.
I intuit that the consequences are less harmful. Strict mode can trigger some syntax errors or throw runtime errors that wouldn't happen in non-strict code. Different arithmetic is less likely to cause this kind of problem.
Sure, it might not cause syntax errors, but it would cause subtle arithmetic bugs, that would be nigh impossible to find. Part of the good thing about "use strict" is that syntax errors "fail early". What you're talking about is a "fail really late and subtlely" type of error introduction, which makes me nervous.
Do you have an idea of a current running program that would behave significantly differently with an accidental bleed of arithmetic mode?
Seems like any JS which is doing math like for animations, bounds checking, etc, may be affected by such things. But I don't have any current code I could point to that I know for sure would die.
Are you refering to the pragma proposal [1]? I am not used to it, but if it's applicable, that's an idea too. [1] harmony:pragmas
I wasn't specifically aware of/referencing that proposal. I was more getting at the idea that some kind of pragma/control command that could be "scoped" with { .. } would be useful, as opposed to the sort of "flag it on" type behavior of the "use strict" command. What makes "use strict" specifically frustrating is that there's no counter-part to say "use lazy" or "use legacy", so one it's encountered, there's no way to tell the interpreter to switch back out of strict mode, other than to get to a new file, which makes it harder to use concat for build performance optimizations.
On Aug 15, 2011, at 10:33 AM, David Bruant wrote:
Hi,
I have been watching JSConf talks recently and two of them [1] [2] mentionned the issue that JavaScript has with numbers. Something than prevent JS to be a good language to interact with a database. The lack of 64-bits integers is also mentioned.
The last is a sore point that I hope binary_data (in ES6) will address, on the storage-type side. As far as 64-bit arithmetic, without value types we would have to use a module of functions to call by awkward names: add, sub, etc. if not add64, sub64 to be explicit.
I don't know if it has been proposed already, but what about a directive? Code could look like:
"use precise arithmetic"; // choose any other name you'd like here
var a = 0.1 + 0.2; // 0.3 // FINALLY!
What number format should be used? I have no expertise whatsoever in that domain, so i'll let people who do talk. In his talks, Douglas Crockford mentions that the fact that JS has one number type is a good thing, so, maybe that choosing a unique replacement should be the best thing. Alternatively, the directive could be "parametrized" with number formats ("use number IEEE XYZ").
You will not get premature standardization past TC39 on this front. Our agreement when decimal missed ES3.1 in 2008 fall (the Kona meeting) was to work on value types so that new numeric types could be implemented in-language.
Today, that means module-encapsulated implementations, and (for usability) value types for operator syntax. But value types as you note are not in ES6.
Sketching a directive solves nothing. Would Math.sin change to use the new numeric type? If not, why not? If so, how?
Worse, what about calls from within the extent of the "use precise arithmetic" pragma to outside of that scope? Of course we can't have dynamic scope, but what types of parameters pass across the barrier? Do precise numbers coerce with loss of precision to IEEE double?
Rather than bikeshed directive syntax, which solves nothing, we need more design and implementation work on value types, specifically new numeric types.
Worse, we may need the "big red switch" on the side of a clique of addressable global objects, so one can opt everything into the precise numeric type, including Math, Number, canvas, WebGL, etc. And doing so may break (or at least terribly slow down) canvas, WebGL, etc. So the big red switch may be a pipe dream.
If so, then the problem becomes how to use different numeric types conveniently. Per our TC39 agreement, this must mean value types, prototype modules implementing numeric value types, user testing, and experiments with coercion rules. This is hard, it takes time.
If we had this effort further along, we could haggle over directive syntax. Right now, doing so is putting cart before horse.
Let's work on the hard parts first.
Le 15/08/2011 21:33, Brendan Eich a écrit :
On Aug 15, 2011, at 10:33 AM, David Bruant wrote:
I don't know if it has been proposed already, but what about a directive? Code could look like:
"use precise arithmetic"; // choose any other name you'd like here
var a = 0.1 + 0.2; // 0.3 // FINALLY!
What number format should be used? I have no expertise whatsoever in that domain, so i'll let people who do talk. In his talks, Douglas Crockford mentions that the fact that JS has one number type is a good thing, so, maybe that choosing a unique replacement should be the best thing. Alternatively, the directive could be "parametrized" with number formats ("use number IEEE XYZ"). You will not get premature standardization past TC39 on this front. Our agreement when decimal missed ES3.1 in 2008 fall (the Kona meeting) was to work on value types so that new numeric types could be implemented in-language.
That's an interesting piece of information. If I understand it correctly, it would imply that the expression "0.1+0.2" will never be equal to 0.3 in a JavaScript program (even with any sort of opt-in (@type versionning, directive, pragma...)). The best thing i can think of is wrapping my numbers to create values out of them. So, "wrap(0.1)+wrap(0.2)" can be equal to 0.3. (Tell me if there is a part i misunderstand or forget)
Consequently, in order to have accurate arithmetic, i can:
- wrap by hand my numbers (which doesn't scale well if i want to apply it to current JS files)
- use a compiler/transpiler to wrap numbers automatically. But it would be hard to tell automatically which numbers should be wrapped and which should not. Wrapping all numbers could have annoying performance issues.
- use some combining approach (interactive tool, heuristics...)
Also, from what i understand, typeof( wrap(0.1)+wrap(0.2) ) could not be "number".
A directive may not solve every problem caused by numeric types, but it would solve the problem of making current code work with precise arithmetic at a small cost (from the developer side).
Today, that means module-encapsulated implementations, and (for usability) value types for operator syntax. But value types as you note are not in ES6.
Sketching a directive solves nothing. Would Math.sin change to use the new numeric type? If not, why not? If so, how?
In ES5, Math.sin and most other Math functions are specified as "Returns an implementation-dependent approximation to the sine of x.", so I do not consider this as a concern. Regardless of underlying number representation, Math.sin returns a approximation of the sine of the argument.
I guess I should return the question: how would Math.sin behave with a value proxy? This question stands for any in-language representation. Since the numeric type is defined in-language, there is not the "regardless of underlying numeric representation" trap that i used above since the representation is not /under/lying. Will in-language values have to define a behavior for each Math function? What when new Math functions are added? Will in-language values have to provide a "default IEEE double value" to work with native Math functions?
Worse, what about calls from within the extent of the "use precise arithmetic" pragma to outside of that scope? Of course we can't have dynamic scope, but what types of parameters pass across the barrier? Do precise numbers coerce with loss of precision to IEEE double?
I agree that this is an issue. But a first answer would be that if you really care about precision, you will be careful of using only precise arithmetic scripts to not be bothered by the dual-mode and issues that could come with it (loss of precision). If you do not care very much about precision, either you do not use the directive or being coerced to non-precise numbers is not an issue for you, so coercion sounds like a good compromise
Rather than bikeshed directive syntax, which solves nothing, we need more design and implementation work on value types, specifically new numeric types.
Worse, we may need the "big red switch" on the side of a clique of addressable global objects, so one can opt everything into the precise numeric type, including Math, Number, canvas, WebGL, etc. And doing so may break (or at least terribly slow down) canvas, WebGL, etc. So the big red switch may be a pipe dream.
If so, then the problem becomes how to use different numeric types conveniently. Per our TC39 agreement
Are there notes of the 2008 Kona meeting? What were the arguments leading to this agreement?
On Aug 15, 2011, at 1:58 PM, David Bruant wrote:
Le 15/08/2011 21:33, Brendan Eich a écrit :
On Aug 15, 2011, at 10:33 AM, David Bruant wrote:
I don't know if it has been proposed already, but what about a directive? Code could look like:
"use precise arithmetic"; // choose any other name you'd like here
var a = 0.1 + 0.2; // 0.3 // FINALLY!
What number format should be used? I have no expertise whatsoever in that domain, so i'll let people who do talk. In his talks, Douglas Crockford mentions that the fact that JS has one number type is a good thing, so, maybe that choosing a unique replacement should be the best thing. Alternatively, the directive could be "parametrized" with number formats ("use number IEEE XYZ"). You will not get premature standardization past TC39 on this front. Our agreement when decimal missed ES3.1 in 2008 fall (the Kona meeting) was to work on value types so that new numeric types could be implemented in-language. That's an interesting piece of information. If I understand it correctly, it would imply that the expression "0.1+0.2" will never be equal to 0.3 in a JavaScript program (even with any sort of opt-in (@type versionning, directive, pragma...)).
That last "even with" parenthetical does not follow from what I wrote. Clearly one could opt in via
<script type="application/ecmascript;version=decimal_nirvana_arrived"> alert(0.1 + 0.2); </script>
and have world peace, cats and dogs living together, etc.
Seriously, my point was simply that TC39 will not decide on one new and more precise number type, rewrite the spec to use it (forking the old spec for compatibility with downrev scripts), and throw the "big red switch" by committee fiat.
The best thing i can think of is wrapping my numbers to create values out of them. So, "wrap(0.1)+wrap(0.2)" can be equal to 0.3. (Tell me if there is a part i misunderstand or forget)
No, you're overreacting still ;-).
Even without a widely-scoped version selector or pragma, we want value types to support usable literals. For decimal, this means the 'm' suffix, after C# (m for "money", I'm told):
alert(0.1m + 0.2m);
and of course + would work with the decimal value type's appropriate operator methods.
Consequently, in order to have accurate arithmetic, i can:
- wrap by hand my numbers (which doesn't scale well if i want to apply it to current JS files)
- use a compiler/transpiler to wrap numbers automatically. But it would be hard to tell automatically which numbers should be wrapped and which should not. Wrapping all numbers could have annoying performance issues.
- use some combining approach (interactive tool, heuristics...)
Also, from what i understand, typeof( wrap(0.1)+wrap(0.2) ) could not be "number".
That's right, value types could introduce new typeof-types, e.g., "decimal", "complex", "rational".
A directive may not solve every problem caused by numeric types, but it would solve the problem of making current code work with precise arithmetic at a small cost (from the developer side).
How? You didn't say anything about the runtime semantics, even if we assume a more precise floating point format (say, IEEE754r decimal).
Today, that means module-encapsulated implementations, and (for usability) value types for operator syntax. But value types as you note are not in ES6.
Sketching a directive solves nothing. Would Math.sin change to use the new numeric type? If not, why not? If so, how? In ES5, Math.sin and most other Math functions are specified as "Returns an implementation-dependent approximation to the sine of x.", so I do not consider this as a concern. Regardless of underlying number representation, Math.sin returns a approximation of the sine of the argument.
That doesn't say how the one Math.sin function object shared by scripts that |use precise arithmetic| with scripts that use IEEE double decides which representation to use: decimal or double.
Also, the approximation allowed by the spec there does indeed vary among implementations, but the domain of the result is IEEE754 binary double. Not extended 80-bit binary double, not 754r decimal, not any wider format.
I guess I should return the question: how would Math.sin behave with a value proxy?
One idea that we've discussed: reverse-delegate to the argument if it is a value proxy, otherwise convert to double as today.
This question stands for any in-language representation. Since the numeric type is defined in-language, there is not the "regardless of underlying numeric representation" trap that i used above since the representation is not /under/lying. Will in-language values have to define a behavior for each Math function?
No, but they might for value types. Math.sin(x) for a value type instance x might delegate to x.reverse_sin() or equivalent.
What when new Math functions are added?
The reverse delegation could funnel through a single method taking the name of the Math method in question, e.g. Details to be decided, but we definitely talked about this in past value types meetings, and ES4 went so far as to consider multi-methods for operators on new (value-ish) types.
Will in-language values have to provide a "default IEEE double value" to work with native Math functions?
No, that seems neither necessary nor a good idea.
Worse, what about calls from within the extent of the "use precise arithmetic" pragma to outside of that scope? Of course we can't have dynamic scope, but what types of parameters pass across the barrier? Do precise numbers coerce with loss of precision to IEEE double? I agree that this is an issue. But a first answer would be that if you really care about precision, you will be careful of using only precise arithmetic scripts to not be bothered by the dual-mode and issues that could come with it (loss of precision).
Programmers will mess this up. All the time.
And it's not just about small step-wise precision differences. Decimal and double are not just imprecisely different, the different radixes result in different rounding even for common "dollars and cents" additions and subtractions. Finally, performance will differ in ways that matter, and which cannot be concealed.
If you do not care very much about precision, either you do not use the directive or being coerced to non-precise numbers is not an issue for you, so coercion sounds like a good compromise
Implicit coercion in JS is one of the big remaining warts we can't easily fix in Harmony. Let's not add more.
Rather than bikeshed directive syntax, which solves nothing, we need more design and implementation work on value types, specifically new numeric types.
Worse, we may need the "big red switch" on the side of a clique of addressable global objects, so one can opt everything into the precise numeric type, including Math, Number, canvas, WebGL, etc. And doing so may break (or at least terribly slow down) canvas, WebGL, etc. So the big red switch may be a pipe dream.
If so, then the problem becomes how to use different numeric types conveniently. Per our TC39 agreement Are there notes of the 2008 Kona meeting? What were the arguments leading to this agreement?
On es-discuss.
esdiscuss/2008-November/thread.html#8105
and look for Kona.
I have been watching JSConf talks recently and two of them [1] [2] mentionned the issue that JavaScript has with numbers. Something than prevent JS to be a good language to interact with a database. The lack of 64-bits integers is also mentionned.
Based on the proposal page, ES.next does not address this issue for the moment while it seems to be a strong concern from the JS community. Soon, with JS being used for Windows 8 apps, the concern will become even stronger.
As far as I know, the only strawman that could be used to address the arithmetic precision issue is value proxies [3]. As a personal opinion, despite my enthusiasm for object proxies, value proxies scare me a bit.
I don't know if it has been proposed already, but what about a directive? Code could look like:
"use precise arithmetic"; // choose any other name you'd like here
var a = 0.1 + 0.2; // 0.3 // FINALLY!
What number format should be used? I have no expertise whatsoever in that domain, so i'll let people who do talk. In his talks, Douglas Crockford mentions that the fact that JS has one number type is a good thing, so, maybe that choosing a unique replacement should be the best thing. Alternatively, the directive could be "parametrized" with number formats ("use number IEEE XYZ").
A directive would have the same benefits than use strict which is to not break existing code in platform that do not support this directive. With a relevant arithmetic test, feature testing will be doable, enabling to determine whether or not a given platform can execute accurate arithmetic (and taking action if not).
It would also have the benefit to fix arithmetic for existing script (at the cost of adding a directive on top).
Thoughts?
David
[1] blip.tv/jsconf/nodeconf-2011-nodecommit-panel-edited-5420208 [2] blip.tv/jsconf/jsconf2011-andrew-paprocki-5455215 [3] strawman:value_proxies