Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Specification error for Math.round

84 views
Skip to first unread message

RobG

unread,
Oct 10, 2012, 10:52:09 PM10/10/12
to
The largest contiguous integer (i.e. the largest of the continuous sequence that can be represented exactly) in Javascript is 2^53, so it might be expected that integers below this value should not exhibit precision errors.

The ES5 specification for Math.round(x)[1] says that:

"If x is already an integer, the result is x"

and that:

"The value of Math.round(x) is the same as the
value of Math.floor(x+0.5)"

So one might expect Math.round(x) to simply return x for all integers up to 2^53. But if the Math.floor criterion is applied, the limit is 2^52 (4503599627370496), so these criteria conflict for odd numbers between 2^52 and 2^53.

Browsers must choose which criterion they will respect, naturally some of the more popular ones have chosen differently.

e.g. in Firefox and Chrome:

Math.round(4503599627370496) -> 4503599627370496
Math.round(4503599627370497) -> 4503599627370497
Math.floor(4503599627370497 + 0.5) -> 4503599627370498

which satisfies the first criterion, but fails the second.

In IE and Opera:

Math.round(4503599627370496) -> 4503599627370496
Math.round(4503599627370497) -> 4503599627370498
Math.floor(4503599627370497 + 0.5) -> 4503599627370498

which satisfies the second criterion but not the first.

There are arguments to support both cases but there is no conclusive agrument one way or the other. It seems to me that this can't be called a bug in implementations, but is a specification error for providing incompatible requirements.

Is it possible to meet both criteria within the bounds of javascript numbers?

Is there some overwhelmingly good argument to support one criterion and not the other?


1. http://ecma-international.org/ecma-262/5.1/#sec-15.8.2.15
2. http://ecma-international.org/ecma-262/5.1/#sec-15.8.2.6

--
Rob

Patricia Shanahan

unread,
Oct 11, 2012, 2:29:07 AM10/11/12
to
RobG wrote:
...
> The ES5 specification for Math.round(x)[1] says that:
>
> "If x is already an integer, the result is x"
>
> and that:
>
> "The value of Math.round(x) is the same as the
> value of Math.floor(x+0.5)"
...

The second rule introduces another oddity.

Most rounding is being done according to IEEE 754 round to nearest, in
which ties go to the even number. This particular rounding is sending
ties to the larger number. For example, Firefox gives "9" as the result
of "Math.round(8.5)".

That means that the inconsistency cannot be fixed by just saying that
the rounding is being done according to the normal round to nearest
rules, the way the corresponding function in java.lang.Math, rint(), is
specified. The second specification, or something equivalent to it, has
to be kept at least for non-integer inputs.

Patricia

Danny

unread,
Oct 11, 2012, 4:16:03 PM10/11/12
to
I'd say that'd be an implementation bug in IE/Opera, I use your number and gave it to Opera 11.62, and gave me the answer you got, then

Math.round(4503599627370499) -> 4503599627370500
Math.round(4503599627370495) -> 4503599627370495
Math.round(4503599627370494) -> 4503599627370494

Math wise, usually x+0.5 should give x+1 as a rounded figure :), so I see nothing wrong with the implementation in FF/chrome, both good engines btw, however I can work with Math.floor(x+0.5) too.

Web standards are practical guidelines to go by, but in the end, the vendors will implement as best they can within those guidelines. Bearing in mind that all major browser vendors have membership in the ES board.

Patricia Shanahan

unread,
Oct 11, 2012, 7:14:40 PM10/11/12
to
I think a standard should be designed so that it is at least possible to
implement, even if some implementations deviate from it. This particular
one is not.

Patricia

RobG

unread,
Oct 12, 2012, 12:46:33 AM10/12/12
to
On Friday, 12 October 2012 06:16:03 UTC+10, Danny wrote:
> I'd say that'd be an implementation bug in IE/Opera, I use your number and gave it to Opera 11.62, and gave me the answer you got, then
>
> Math.round(4503599627370499) -> 4503599627370500

Which confirms that Opera is consistent with IE and inconsistent with Firefox and Chrome.

> Math.round(4503599627370495) -> 4503599627370495
>
> Math.round(4503599627370494) -> 4503599627370494

Those numbers are less than 2^52, so don't have the issue.


> Math wise, usually x+0.5 should give x+1 as a rounded figure

Not at all. If x is a decimal, then:

2.75 rounds to 3
2.75 + 0.5 = 3.25 which also rounds to 3

If x is an integer, then for simple rounding you are correct. But as Patricia said, a common rounding algorithm is to round to the nearest even number (sometimes called "bankers rounding") to attenuate rounding inaccuracy.

A peculiarity of javascript numbers is that 4503599627370498.5 is rounded to 4503599627370498 when converted to a number, so there is a kind of natural bankers rounding for such numbers.

> so I see nothing wrong with the implementation in FF/chrome, both good engines btw, however I can work with Math.floor(x+0.5) too.

The problem is that it's impossible to comply with the standard within the bounds of ECMAScript numbers. One fix is to do something like:

function specialRound(n) {
var num = String(n);

// If n is an integer, return it
if (/^[0-9]+$/.test(num)) {
return n;
}

// Otherwise, round it
return Math.round(n);
}


which does what the ES5 specification says. It can be useful:

var a = 4503599627370498;
var b = 4503599627370497;
specialRound(a) - specialRound(b); // 1 in all browsers
Math.round(a) - Math.round(b); // 0 in IE, 1 in Firefox


Patricia Shanahan

unread,
Oct 12, 2012, 3:28:19 AM10/12/12
to
RobG wrote:
...
> If x is an integer, then for simple rounding you are correct. But as
> Patricia said, a common rounding algorithm is to round to the nearest
> even number (sometimes called "bankers rounding") to attenuate
> rounding inaccuracy.
...

It's perhaps worth emphasizing the fact that the ties go to nearest even
style of rounding is the normal rounding mode in JavaScript arithmetic.
It is what you get if you add two numbers, and the exact sum has more
than 53 bits, ignoring leading and trailing zeros.

Patricia

Dr J R Stockton

unread,
Oct 12, 2012, 5:48:32 PM10/12/12
to
In comp.lang.javascript message <6dc8cb55-c29c-4bb2-ab6d-d9a9447cffd4@go
oglegroups.com>, Wed, 10 Oct 2012 19:52:09, RobG <rg...@iinet.net.au>
posted:

Adherence to the 72-character convention would be appreciated.

>The largest contiguous integer (i.e. the largest of the continuous
>sequence that can be represented exactly) in Javascript is 2^53, so it
>might be expected that integers below this value should not exhibit
>precision errors.
>
>The ES5 specification for Math.round(x)[1] says that:
>
> "If x is already an integer, the result is x"

Agreed.

>and that:
>
> "The value of Math.round(x) is the same as the
> value of Math.floor(x+0.5)"

Not really agreed. That is part of a Note, which should be explicative
and is not normative. The second part of the Note also seems wrong; +0
and -0 are distinguishable, but have the same value.


>So one might expect Math.round(x) to simply return x for all integers
>up to 2^53. But if the Math.floor criterion is applied, the limit is
>2^52 (4503599627370496), so these criteria conflict for odd numbers
>between 2^52 and 2^53.
>
>Browsers must choose which criterion they will respect, naturally some
>of the more popular ones have chosen differently.
>
>e.g. in Firefox and Chrome:
>
> Math.round(4503599627370496) -> 4503599627370496
> Math.round(4503599627370497) -> 4503599627370497
> Math.floor(4503599627370497 + 0.5) -> 4503599627370498
>
>which satisfies the first criterion, but fails the second.
>
>In IE and Opera:
>
> Math.round(4503599627370496) -> 4503599627370496
> Math.round(4503599627370497) -> 4503599627370498
> Math.floor(4503599627370497 + 0.5) -> 4503599627370498
>
>which satisfies the second criterion but not the first.
>
>There are arguments to support both cases but there is no conclusive
>agrument one way or the other. It seems to me that this can't be called
>a bug in implementations, but is a specification error for providing
>incompatible requirements.

Only the first case is right; it agrees with the normative part.
Math.round of an integer should certainly return the identical integer
(and not just an integer of the same value).

>Is it possible to meet both criteria within the bounds of javascript numbers?
>
>Is there some overwhelmingly good argument to support one criterion and
>not the other?

The normative way makes sense, the other does not.
15.8.2.9 ???


Entered into <http://www.merlyn.demon.co.uk/js-flaws.htm#Auto>. Safari
5.1.7 correctly matches Firefox & Chrome.

--
(c) John Stockton, nr London, UK. E-mail, see Home Page. Turnpike v6.05.
Website <http://www.merlyn.demon.co.uk/> - w. FAQish topics, links, acronyms
PAS EXE etc. : <http://www.merlyn.demon.co.uk/programs/> - see in 00index.htm
Dates - miscdate.htm estrdate.htm js-dates.htm pas-time.htm critdate.htm etc.

Dr J R Stockton

unread,
Oct 12, 2012, 6:00:41 PM10/12/12
to
In comp.lang.javascript message <sMudna--d5iv-uvNnZ2dnUVZ_tydnZ2d@earthl
ink.com>, Thu, 11 Oct 2012 07:29:07, Patricia Shanahan <pa...@acm.org>
posted:

>Most rounding is being done according to IEEE 754 round to nearest, in
>which ties go to the even number. This particular rounding is sending
>ties to the larger number. For example, Firefox gives "9" as the result
>of "Math.round(8.5)".

Bankers' Rounding is good, I suppose, for Bankers. It is bad for
technical work. The ECMA spec should keep Math.round as it is, and add
Math.roundBankers.

<http://www.merlyn.demon.co.uk/js-rndg3.htm#CT> purports to provide
Bankers' Rounding, of two types.

Since JavaScript non-integer arithmetic is commonly inexact, it may be
desirable to round values which are almost half-integer to half-integer
before doing Bankers; my page provides that.

To get the ECMA standard debugged,
"omnes labores adhuc exantlati (exatlanti?) omni fructu caruissent"
(Enestrom 304).

--
(c) John Stockton, nr London UK. E-addr via Homepage. Turnpike v6.05 MIME.
<http://www.merlyn.demon.co.uk/> TP/BP/Delphi/&c., FAQqy topics & links;
<http://www.merlyn.demon.co.uk/clpb-faq.txt> RAH Prins : c.l.p.b mFAQ;
<ftp://garbo.uwasa.fi/pc/link/tsfaqp.zip> Timo Salmi's Turbo Pascal FAQ.

Thomas 'PointedEars' Lahn

unread,
Oct 13, 2012, 4:14:13 PM10/13/12
to
Patricia Shanahan wrote:

> RobG wrote:
>> If x is an integer, then for simple rounding you are correct. But as
>> Patricia said, a common rounding algorithm is to round to the nearest
>> even number (sometimes called "bankers rounding") to attenuate
>> rounding inaccuracy.
>
> It's perhaps worth emphasizing the fact that the ties go to nearest even
> style of rounding is the normal rounding mode in JavaScript arithmetic.
^^^^^^^^^^
That would be _ECMAScript_.¹ But you are oversimplifying, see ES 5.1
§9.8.1. One additional caveat here is that the numbers you *see* are only
string representations of Number values.

BTW, your quoting style is suboptimal. Omissions, if any, should be
indicated at the same quotation level as the text from which text has been
omitted. `[...]' or (typographically correct) `[…]' should be used to
indicate omission, to distinguish it from an ellipsis in the quoted text.


PointedEars
___________
¹ But perhaps one should be grateful already that you are not writing
"Javascript" or "javascript" instead. Sigh. [psf 10.1]
--
Anyone who slaps a 'this page is best viewed with Browser X' label on
a Web page appears to be yearning for the bad old days, before the Web,
when you had very little chance of reading a document written on another
computer, another word processor, or another network. -- Tim Berners-Lee

Patricia Shanahan

unread,
Oct 13, 2012, 4:45:33 PM10/13/12
to
Thomas 'PointedEars' Lahn wrote:
...
> BTW, your quoting style is suboptimal. Omissions, if any, should be
> indicated at the same quotation level as the text from which text has been
> omitted. `[...]' or (typographically correct) `[…]' should be used to
> indicate omission, to distinguish it from an ellipsis in the quoted text.

Interesting. I've been posting, using similar quoting style, in various
USENET newsgroups, for over 20 years. You are the very first person to
comment on this.

Patricia

Evertjan.

unread,
Oct 13, 2012, 5:59:42 PM10/13/12
to
Patricia Shanahan wrote on 13 okt 2012 in comp.lang.javascript:

> Thomas 'PointedEars' Lahn wrote:
> ...
>> BTW, your quoting style is suboptimal. Omissions, if any, should be
>> indicated at the same quotation level as the text from which text has
>> been omitted. `[...]' or (typographically correct) `[ƒ Ý]' should be
>> used to indicate omission, to distinguish it from an ellipsis in the
>> quoted text.
>
> Interesting. I've been posting, using similar quoting style, in
> various USENET newsgroups, for over 20 years. You are the very first
> person to comment on this.

Could be that most of us are oldfassionly polite to you?

Even so, 20 years of dejavue is a long stretch even for an active
usenetter.

--
Evertjan.
The Netherlands.
(Please change the x'es to dots in my emailaddress)

Dr J R Stockton

unread,
Oct 13, 2012, 2:31:40 PM10/13/12
to
In comp.lang.javascript message <CbGdnbfKl-UJW-rNnZ2dnUVZ_vydnZ2d@earthl
ink.com>, Fri, 12 Oct 2012 08:28:19, Patricia Shanahan <pa...@acm.org>
posted:
Forced rounding because 53 bits is insufficient and voluntary rounding
by explicitly calling a rounding routine are two quite distinct cases,
and neither should influence the other.

--
(c) John Stockton, nr London, UK. Mail via homepage. Turnpike v6.05 MIME.
Web <http://www.merlyn.demon.co.uk/> - FAQqish topics, acronyms and links;
Astro stuff via astron-1.htm, gravity0.htm ; quotings.htm, pascal.htm, etc.

Denis McMahon

unread,
Oct 14, 2012, 7:09:29 AM10/14/12
to
On Thu, 11 Oct 2012 21:46:33 -0700, RobG wrote:

> On Friday, 12 October 2012 06:16:03 UTC+10, Danny wrote:

>> Math wise, usually x+0.5 should give x+1 as a rounded figure
>
> Not at all. If x is a decimal, then:
>
> 2.75 rounds to 3 2.75 + 0.5 = 3.25 which also rounds to 3
>
> If x is an integer, then for simple rounding you are correct.

I suspect that was the case that Danny was referring to.

Normally, for any "whole number" x.0, I would expect decimal fractions in
the range (x.0 - 0.5) .. (x.0 + 0.49999999999999999999999999..) to round
to x.

I think this is what you get if your rounding function f(x) is
{return Math.floor(x+0.5);}

I think (1) it's probably better to keep your numbers away from the cases
where errors may occur, and (2) if you want to process numbers with that
much precision, don't use javascript.

Rgds

Denis McMahon


0 new messages