ES Decimal status

Brendan Eich brendan at
Wed Sep 24 08:53:07 PDT 2008

On Sep 24, 2008, at 8:28 AM, Sam Ruby wrote:

>>> My apologies.  That wasn't the question I was intending.
>>> Can you identify code that today depends on numeric binary 64  
>>> floating
>>> point which makes use of operations such as unrounded division and
>>> depends on trailing zeros being truncated to compute array indexes?
>>> I would think that such code would be more affected by factors  
>>> such as
>>> the increased precision and the fact that 1.2-1.1 produces
>>> 0.09999999999999987 than on the presence or absence of any trailing
>>> zeros.
>>> But given the continued use of words such as "broken" and  
>>> "unusable",
>>> I'm wondering if I'm missing something obvious.

The only thing that might be called obvious here is the invariant  
(modulo teeny tiny numbers) that people have pointed out (a === b => o 
[a] is o[b]). Such JS invariants tend to be required by web content.  
Postel's Law means you accept everything that flies in ES1, and have  
trouble being less liberal in ES2+. Web authors crawl the feature  
vector space and find all the edges, so at least what you did not  
accept in v1 becomes law. But these are generalizations from  
experience with invariants such as typeof x == "object" && !x => x  
=== null and typeof x == typeof y => (x == y <=> x === y).

Beyond this conservatism in breaking invariants based on experience,  
it turns out that % and / results do flow into array indexes. From  
SunSpider's 3d-raytrace.js (which came from some other benchmark  
suite, IIRC):

// Triangle intersection using barycentric coord method
function Triangle(p1, p2, p3) {
     var edge1 = sub(p3, p1);
     var edge2 = sub(p2, p1);
     var normal = cross(edge1, edge2);
     if (Math.abs(normal[0]) > Math.abs(normal[1]))
         if (Math.abs(normal[0]) > Math.abs(normal[2]))
             this.axis = 0;
             this.axis = 2;
         if (Math.abs(normal[1]) > Math.abs(normal[2]))
             this.axis = 1;
             this.axis = 2;
     var u = (this.axis + 1) % 3;
     var v = (this.axis + 2) % 3;
     var u1 = edge1[u];
     var v1 = edge1[v];
     . . .

Triangle.prototype.intersect = function(orig, dir, near, far) {
     var u = (this.axis + 1) % 3;
     var v = (this.axis + 2) % 3;
     var d = dir[this.axis] + * dir[u] + this.nv * dir[v];
     . . .

but the operands of % are integers here. So long as decimal and  
double don't change the results from being integral, these use-cases  
should be ok (if slower).

The concern remains, though. Not due to power-of-five problems that  
would lead to 0.09999999999999987 or the like, but from cases where a  
number was spelled with extra trailing zeros (in external data, e.g.  
a spreadsheet) but fits in an integer, or otherwise can be expressed  
exactly using powers of two. The burden of proof here is on the  
invariant breaker :-/.


More information about the Es-discuss mailing list