Round off

J

John G Harris

******
The OP asked about rounding a number to 2 decimals - not about converting it
to a string.
***
<snip>

The most reliable way to do decimal rounding is to operate on the
decimal representation of the number. In javascript this means you have
to start by translating the internal binary representation into a
string. (At present. Microsoft wanted numbers to be decimal-coded. I
don't know if they still do.)

This is probably what JohnS meant. Then again, perhaps not. It's not
always easy to work out what he means when he's operating in sarcastic
teacher mode.

Actually, the OP didn't say if he wants decimal rounding, or hexadecimal
rounding, or duodecimal rounding, or ternary rounding, or binary
rounding to two places. We are all guessing he meant decimal as that's
what most people want to display to their end users.

John
 
D

Dr John Stockton

JRS: In article <[email protected]>, dated Thu, 6
Apr 2006 22:59:22 remote, seen in Hal Rosser
******
The OP asked about rounding a number to 2 decimals - not about converting it
to a string.
***

"Two decimal places" implies a string. Number 3.5 is a multiple of
0.01, but it has only one decimal place. String "3.50" represents a
multiple of 0.01, and has two decimal places. They have the same
numerical value.

===========

In my system, somewhat to my surprise, Math.pow(10, N) is faster than
Number("1e"+N), and is sufficiently accurate[*] ; unless I hear that the
contrary is generally true, I'll change my pages.

[*] for (j=0; j<40; j++) document.writeln(j, " ", +("1E"+j), " ",
t=Math.pow(10, j), " ", t%1, "<br>")
My results for 23 & 29 need some more thought.

FAQ 4.6 can be correspondingly changed.

I'm sure that code is very clever and correct.
It is, however just so mush gibberish to someone who's trying to learn
something.

Don't quote what you're not commenting on. It confuses and annoys your
readers.

The FAQ author evidently did not feel that the comment presented in and
for StrU and its friends in js-round.htm needed to be repeated in the
FAQ, given that he was providing a link to js-round.htm. There, StrU
currently starts
function StrU(X, M, N) { // X>=0.0 ; gives M digits point N digits

Remember that the FAQ is posted twice a week, and that some users will
be on dial-up or wireless links. That is why (IMHO) the FAQ rightly
gives brief answers with links to further information.
 
J

joe.eas

The OP asked about rounding a number to 2 decimals

got a method i want you to judge for reliability, might work but i have
a feeling its not as reliable as the way in the faq.

the Math.round trick gives 1.03 and 2.04 when you put in 1.035 and
2.035 but what if you carry on incrementing the number until the same
numbers after the decimal point appear a few times in a row.

using the math.round thingie in a loop and repeating until a consistent
answer is found seems to work where math.round on its own doesnt work
but have i missed some other math errors?

methinks its ok for just a few decimal places but more than that and it
is unreliable

the line with all the math.round calls just does the same as Math.round
in earlier examples but it does it so the result is between 0 and 1 for
the comparison.

function around( num, places ) {
i=0;
sign = (num >= 0) ? +1 : -1;
prev = Array(-1,-2,-1);
scale = Math.pow(10,places);
while( prev[0] != prev[1] || prev[1] != prev[2] )
prev[ Math.abs(++i) % 3 ] = ( Math.round( scale * (num +
sign*i) ) - ( Math.round(num + sign * i) * scale ) ) / scale;

return (Math.floor(Math.abs(num))*sign) + "." +
Math.abs(prev[0]).toString().substr(2,places);
}

ta all

joe
 
H

Hal Rosser

Phat G5 (G3) said:
Has anyone found a reliable way to force JS to round to a specific number of
places? Every time I try I get different results. For example, I'd need to
round 3.4589 to 2 places. What is the most reliable way to do it?

Thanks

Found it!
Adding several zeroes after the decimal makes it behave.
Tested on over 300 combinations of random numbers and places..
Assumes you round UP anything >=5 and that -90.075 < -90.07
Let me know if you break it - and how.
I'm still refining this function.
*** The code ***
//the args: nnn = number to be rounded
// yyy = number of places
function roundy(nnn,yyy){
multy = Math.pow(10,yyy); // ** the multiplier to use
nnn+=""; //** coerce nnn to a String
//make sure there's a dot and something after it
if (nnn.indexOf(".") < 0){
nnn += ".0";
}
//** adding zeroes after the dot makes it behave
nnn += "00000";
nnn *=1; //** coerce it back to a number
//** now multiply by 10^yyy, round it, then divide by 10^yyy -
works!
return Math.round(nnn * multy) / multy;
}
 
H

Hal Rosser

function around( num, places ) {
i=0;
sign = (num >= 0) ? +1 : -1;
prev = Array(-1,-2,-1);
scale = Math.pow(10,places);
while( prev[0] != prev[1] || prev[1] != prev[2] )
prev[ Math.abs(++i) % 3 ] = ( Math.round( scale * (num +
sign*i) ) - ( Math.round(num + sign * i) * scale ) ) / scale;

return (Math.floor(Math.abs(num))*sign) + "." +
Math.abs(prev[0]).toString().substr(2,places);
}

ta all

joe

Hi Joe,
I tested your function with a few numbers that they tested mine with.
Your code seems to crash (or go into an endless loop) with negative numbers.
and these runs give these results:
around (0.0, 2) returns .01
around (11.34, 5) returns 11.351
around( -90.07, 2) *** crashed ** (I think an infinite loop)
.... The other tests did ok . I used Firefox.
Hope this helps
I think I've come upon an answer, also. see previous posts.
 
D

Dr John Stockton

JRS: In article <[email protected]>, dated Sat,
8 Apr 2006 16:47:32 remote, seen in Hal
Rosser said:
Let me know if you break it - and how.

Your *first* problem is that you have not yet understood what rounding
to two decimal places actually means. It is not equivalent to rounding
a number to a multiple of 0.01. The result *must* be a String.

Before attempting to offer code here as an answer to a specific question
or for general use, you should *read* and *understand* the newsgroup
FAQ.
 
H

Hal Rosser

Phat G5 (G3) said:
Has anyone found a reliable way to force JS to round to a specific number of
places? Every time I try I get different results. For example, I'd need to
round 3.4589 to 2 places. What is the most reliable way to do it?

Thanks

-S
Dear OP,
The *** insists on returning a String, so here's an understandable piece
of code that will do just that(unlike FAQ 4.6 which is not understandable).
I will persist to resist FAQ 4.6 against all odds because of the unneeded
complexity of that code. I mean *really* look at it.

// args->nnn is the number to round,
// and yyy the number of places
function roundyv2(nnn,yyy){
multy = Math.pow(10,yyy); //** the multiplier to use
nnn+=""; //** coerce nnn to a String
//make sure there's a dot and something after it
if (nnn.indexOf(".") < 0){
nnn += ".0";
}
//** adding zeroes after the dot makes it behave
nnn += "00000";
nnn *=1; //** coerce it back to a number
//** now rounding works ***
var strOut = Math.round(nnn * multy) / multy;
//return strOut;//would return a number here
strOut = "" + strOut;//**coerce to String
var places = strOut.length - strOut.indexOf(".") - 1;
var zeroesNeeded = yyy - places;
var strZeroes = Math.pow(10, zeroesNeeded) + "";
if (zeroesNeeded > 0) {
strOut += strZeroes.substr(1);
}
return strOut;
}
 
H

Hal Rosser

Phat G5 (G3) said:
Has anyone found a reliable way to force JS to round to a specific number of
places? Every time I try I get different results. For example, I'd need to
round 3.4589 to 2 places. What is the most reliable way to do it?

Thanks

-S
but in the latest Firefox, this works ok:
return 3.4589.toFixed(2);
 
D

Dr John Stockton

JRS: In article <[email protected]>, dated Sun,
9 Apr 2006 15:08:45 remote, seen in Hal
Rosser said:
The *** insists on returning a String, so here's an understandable piece
of code that will do just that(unlike FAQ 4.6 which is not understandable).

FAQ 4.6 deals with cases that your code does not attempt to deal with;
so you may not see the need for some of it.

Your code provides no way to choose the representation of the sign; 4.6
does, by modifying Sign.

The code in the FAQ provides a routine, Stretch, potentially useful
elsewhere. If it's not used elsewhere, that part of the job can easily
be done within StrU.

The code in the FAQ adds leading zeroes if required.

The code in the FAQ also shows how to implement a .toFixed for Number.

By the way, although I used to use the code in the FAQ, I now use code
developed from it.

I will persist to resist FAQ 4.6 against all odds because of the unneeded
complexity of that code. I mean *really* look at it.

// args->nnn is the number to round,
// and yyy the number of places
function roundyv2(nnn,yyy){
multy = Math.pow(10,yyy); //** the multiplier to use //
nnn+=""; //** coerce nnn to a String
//make sure there's a dot and something after it
if (nnn.indexOf(".") < 0){
nnn += ".0";
}
//** adding zeroes after the dot makes it behave
nnn += "00000";
nnn *=1; //** coerce it back to a number
//** now rounding works *** //
var strOut = Math.round(nnn * multy) / multy;
//return strOut;//would return a number here
strOut = "" + strOut;//**coerce to String
var places = strOut.length - strOut.indexOf(".") - 1;
var zeroesNeeded = yyy - places;
var strZeroes = Math.pow(10, zeroesNeeded) + "";
if (zeroesNeeded > 0) {
strOut += strZeroes.substr(1);
}
return strOut;
}

If the input nnn is a small integer, your code extends it, changing its
value. Try, for example, roundyv2(1, 2), which gives "10".

For what conditions do you think that your lines between my added //
marks actually do something useful, and what do they then achieve?

Is using Math.pow an *efficient* way of generating trailing zeroes, in
comparison with others, averaged over probable usage?
 
D

Dr John Stockton

JRS: In article <[email protected]>, dated Sun,
9 Apr 2006 15:12:05 remote, seen in Hal
but in the latest Firefox, this works ok:
return 3.4589.toFixed(2);


Phat G5 (G3) wants a *reliable* way.

On the World-Wide Web, one cannot reliably assume that the end user has
the latest Firefox.

In at least one version of at least one popular browser, the native
toFixed is known to give correct results in some cases.
 
B

BootNic

RobG said:
[snip]

Read FAQ 4.6, it is rather concise but if studied it will all become
apparent. Also read 4.7, which helps to explain why the *100/100
method doesn't work consistently.

<URL:http://www.jibbering.com/FAQ/#FAQ4_6>
[snip]

0 - 100 test numbers, 3 decimals rounded to 2

May I be missing something? The script posted in FAQ4_6 seems to
generate 572 Anomalies.


http://files.photojerk.com/BootNic/Anomalies.png

IE6 toFixed 5 Anomalies
Mozilla 1.7.12 toFixed 4800 Anomalies
Opera 8.54 toFixed 5000 Anomalies

While the native toFixed results surprise me only by the fact that IE6 only
returned 5 Anomalies.

What would be a good way to test the results of a script like this?

--
BootNic Monday, April 10, 2006 2:54 PM

People grow through experience if they meet life honestly and
courageously. This is how character is built.
*Eleanor Roosevelt*
 
H

Hal Rosser

Phat G5 (G3) said:
Has anyone found a reliable way to force JS to round to a specific number of
places? Every time I try I get different results. For example, I'd need to
round 3.4589 to 2 places. What is the most reliable way to do it?

Thanks

-S

** Here's latest version thanks to John's code-testing services
** removed some superfluous code and fixed the 1-digit bug

//**args ->nnn = the number to round and yyy= the number of places
function roundyv3(nnn,yyy){
//** the multiplier to use
var multy = "1e"+yyy;//*same as Math.pow(10, yyy) ***
nnn+=""; //** coerce nnn to a String
//make sure there's a dot and something after it
if (nnn.indexOf(".") < 0){
nnn += ".0";
}
//** adding zeroes after the dot makes it behave when rounding
nnn = nnn + "00000";
//** now rounding works - but removes the dot if a whole number ***
var strOut = String(Math.round(nnn * multy) / multy);
//add back the decimal if rounding removed it and yyy > 0
if (strOut.indexOf(".") < 0 && yyy > 0) {
strOut+= ".0";
}
var placesInStrOut = strOut.length - strOut.indexOf(".") - 1;
var zeroesNeeded = yyy - placesInStrOut;
var strZeroes = String(Math.pow(10, zeroesNeeded));
if (zeroesNeeded > 0) {
strOut += strZeroes.substr(1);
}
return strOut;
}
 
H

Hal Rosser

Dr John Stockton said:
JRS: In article <[email protected]>, dated Sun,
9 Apr 2006 15:08:45 remote, seen in Hal
Rosser <[email protected]> posted :


FAQ 4.6 deals with cases that your code does not attempt to deal with;
so you may not see the need for some of it.
***
OK... right - but its not documented and its confusing code
Your code provides no way to choose the representation of the sign; 4.6
does, by modifying Sign.
***
Don't see a need for it negatives have a minus sign, (You want to add
parentheses, or what, for negatives?)
The code in the FAQ provides a routine, Stretch, potentially useful
elsewhere. If it's not used elsewhere, that part of the job can easily
be done within StrU.
*******
but it is uncommented ,obfuscated code
You blasted my code (in a previous post) for not indenting-- but look at the
code in FAQ4.6 hehe
The code in the FAQ adds leading zeroes if required.
***
(For rounding backwards ??) (When would that be required?)
The code in the FAQ also shows how to implement a .toFixed for Number.
**
I disagree - it confuses the reader unless he doesn't need to read the FAQ
**
By the way, although I used to use the code in the FAQ, I now use code
developed from it.
***
I would be interested in seeing *documented* code you're now using.
****
If the input nnn is a small integer, your code extends it, changing its
value. Try, for example, roundyv2(1, 2), which gives "10".
***
Fixed that - good catch - I forgot to reinsert the dot after rounding (whole
numbers).
For what conditions do you think that your lines between my added //
marks actually do something useful, and what do they then achieve?
***
Removed that - another decent (and constructive) catch - thanks.
Is using Math.pow an *efficient* way of generating trailing zeroes, in
comparison with others, averaged over probable usage?
***
The second hand on my clock didn't move much when that code executed, but I
changed one instance to the "1e" + x method. Purity of code is a good cause
I guess though.

I fixed it and reposted. Thanks for testing.
I took a 'software testing' course - but you have a real knack for it.
Hal
 
D

Dr John Stockton

JRS: In article <h%[email protected]>, dated
Tue, 11 Apr 2006 00:14:15 remote, seen in Hal
Rosser said:
** Here's latest version thanks to John's code-testing services
** removed some superfluous code and fixed the 1-digit bug

//**args ->nnn = the number to round and yyy= the number of places
function roundyv3(nnn,yyy){

BTW, it's a good convention IMHO to use, as in Fortran, names such as X
for floats and such as N for integers. Javascript does not make the
distinction, but it can help readability.
//** the multiplier to use
var multy = "1e"+yyy;//*same as Math.pow(10, yyy) ***

But "1e"+yyy gives a String; Math.pow(10, yyy) gives a Number.
nnn+=""; //** coerce nnn to a String
//make sure there's a dot and something after it
if (nnn.indexOf(".") < 0){
nnn += ".0";
}
//** adding zeroes after the dot makes it behave when rounding
nnn = nnn + "00000";

When is that part beneficial, and how?
//** now rounding works - but removes the dot if a whole number ***
var strOut = String(Math.round(nnn * multy) / multy);
//add back the decimal if rounding removed it and yyy > 0
if (strOut.indexOf(".") < 0 && yyy > 0) {
strOut+= ".0";
}
var placesInStrOut = strOut.length - strOut.indexOf(".") - 1;
var zeroesNeeded = yyy - placesInStrOut;
var strZeroes = String(Math.pow(10, zeroesNeeded));

Why do that if zeroesNeeded is zero ? Also, it seems slower than using
a string constant of ten or twenty zeroes with substr.
if (zeroesNeeded > 0) {
strOut += strZeroes.substr(1);
}
return strOut;
}

Your code gives funny results with large numbers and NaN .
Your code rounds 3.965 to 3.97 but -3.965 to -3.96 .
 
J

John G Harris

** Here's latest version thanks to John's code-testing services
** removed some superfluous code and fixed the 1-digit bug

//**args ->nnn = the number to round and yyy= the number of places
function roundyv3(nnn,yyy)

/* Preconditions :
abs(nnn) < 2^52 or thereabouts (you don't test for 'e' or 'E')
abs(yyy) < 20 or so (any more is pointless)
*/
{
//** the multiplier to use
var multy = "1e"+yyy;//*same as Math.pow(10, yyy) ***
nnn+=""; //** coerce nnn to a String
//make sure there's a dot and something after it
if (nnn.indexOf(".") < 0){
nnn += ".0";
}
//** adding zeroes after the dot makes it behave when rounding
nnn = nnn + "00000";
//** now rounding works - but removes the dot if a whole number ***

I don't see why you converted nnn into a string when the next thing you
do is convert it back into the original number.
var strOut = String(Math.round(nnn * multy) / multy);
//add back the decimal if rounding removed it and yyy > 0
if (strOut.indexOf(".") < 0 && yyy > 0) {
strOut+= ".0";
}
var placesInStrOut = strOut.length - strOut.indexOf(".") - 1;
var zeroesNeeded = yyy - placesInStrOut;
var strZeroes = String(Math.pow(10, zeroesNeeded));
if (zeroesNeeded > 0) {
strOut += strZeroes.substr(1);
}
return strOut;
}

John
 
H

Hal Rosser

John G Harris said:
/* Preconditions :
abs(nnn) < 2^52 or thereabouts (you don't test for 'e' or 'E')
abs(yyy) < 20 or so (any more is pointless)
*/ -----ok---

I don't see why you converted nnn into a string when the next thing you
do is convert it back into the original number.
****************************
*** to make sure a decimal point and zeroes are there
*** else rounding is inconsistent
 
D

Dr John Stockton

JRS: In article <[email protected]>, dated Tue, 11
Apr 2006 21:16:17 remote, seen in John G
Harris said:
/* Preconditions :
abs(nnn) < 2^52 or thereabouts (you don't test for 'e' or 'E')
abs(yyy) < 20 or so (any more is pointless)
*/

You mean, I think, 2^53, the first integer that cannot be incremented
with ++. But the default conversion from Number to String does not use
e-format below almost 10^21.

Neither FAQ 4.6 nor my current code test for e/E as such; though they
detect them.

***

To use Bankers' Rounding sensibly, either it should be rounding to
integer (so x.5 is exact) or one should first round by a modest multiple
of the expected rounding error to an exact base-10 value (implying to a
string) and then do Bankers' on that.
 
J

John G Harris

Dr John Stockton said:
JRS: In article <[email protected]>, dated Tue, 11
Apr 2006 21:16:17 remote, seen in John G


You mean, I think, 2^53, the first integer that cannot be incremented
with ++. But the default conversion from Number to String does not use
e-format below almost 10^21.
<snip>

Displaying something.75 when there are only 2 bits to the right of the
binary point would be thoroughly misleading. Displaying something.00
when there are no bits to the right of the binary point is also
misleading.

Deciding what should be written in the pre-conditions is not easy in
this case. My "thereabouts" had a much larger range than you might have
thought.

John
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top