FAQ Topic - How do I convert a Number into a String with exactly 2 decimal places?

F

FAQ server

-----------------------------------------------------------------------
FAQ Topic - How do I convert a Number into a String with exactly 2 decimal places?
-----------------------------------------------------------------------

When formatting money for example, to format 6.57634 to
6.58, 6.5 to 6.50, and 6 to 6.00?

Rounding of x.xx5 is uncertain, as such numbers are not
represented exactly.

N = Math.round(N*100)/100 only converts N to a Number of value
close to a multiple of 0.01; but document.write(N) does not give
trailing zeroes.

ECMAScript Ed. 3.0 (JScript 5.5 [but buggy] and JavaScript 1.5)
introduced N.toFixed, the main problem with this is the bugs in
JScripts implementation.

Most implementations fail with certain numbers, for example 0.07.
The following works successfully for M>0, N>0:

function Stretch(Q, L, c) { var S = Q
if (c.length>0) while (S.length<L) { S = c+S }
return S
}
function StrU(X, M, N) { // X>=0.0
var T, S=new String(Math.round(X*Number("1e"+N)))
if (S.search && S.search(/\D/)!=-1) { return ''+X }
with (new String(Stretch(S, M+N, '0')))
return substring(0, T=(length-N)) + '.' + substring(T)
}
function Sign(X) { return X<0 ? '-' : ''; }
function StrS(X, M, N) { return Sign(X)+StrU(Math.abs(X), M, N) }

Number.prototype.toFixed= new Function('n','return StrS(this,1,n)')

http://www.merlyn.demon.co.uk/js-round.htm

http://msdn.microsoft.com/library/en-us/script56/html/js56jsmthToFixed.asp


===
Postings such as this are automatically sent once a day. Their
goal is to answer repeated questions, and to offer the content to
the community for continuous evaluation/improvement. The complete
comp.lang.javascript FAQ is at http://www.jibbering.com/faq/.
The FAQ workers are a group of volunteers.
 
D

Dr J R Stockton

In comp.lang.javascript message
function Stretch(Q, L, c) { var S = Q
if (c.length>0) while (S.length<L) { S = c+S }
return S
}
function StrU(X, M, N) { // X>=0.0
var T, S=new String(Math.round(X*Number("1e"+N)))
if (S.search && S.search(/\D/)!=-1) { return ''+X }
with (new String(Stretch(S, M+N, '0')))
return substring(0, T=(length-N)) + '.' + substring(T)
}
function Sign(X) { return X<0 ? '-' : ''; }
function StrS(X, M, N) { return Sign(X)+StrU(Math.abs(X), M, N) }

The functions on my site have been updated since that was put in the
FAQ.



function Sign(X) { return X>0 ? "+" : X<0 ? "-" : " " }

function PrfxTo(S, L, C) { S += ""
if (C.length>0) while (S.length<L) S = C + S ; return S }

function SpcsTo(S, L) { S += "" // SpcsTo is a reduction of PrfxTo
while (S.length<L) S = " " + S ; return S }

function StrU(X, M, N) { // X > -0.5e-N ; to M digits point N digits
var S = String(Math.round(X*Math.pow(10, N)))
if (/\D/.test(S)) return SpcsTo(X, M+N+1) // cannot cope
S = PrfxTo(S, M+N, '0') ; var T = S.length - N
return S.substring(0, T) + '.' + S.substring(T) }

function StrS(X, M, N) { return Sign(X) + StrU(Math.abs(X), M, N) }


Given that some of the function set are intended to give fixed-width
results, ISTM that Sign should give fixed-width output. When that's
*not* wanted, removing a bit of the code will be easy.
 
R

Randy Webb

Dr J R Stockton said the following on 12/13/2006 5:32 PM:
In comp.lang.javascript message


The functions on my site have been updated since that was put in the FAQ.



function Sign(X) { return X>0 ? "+" : X<0 ? "-" : " " }

function PrfxTo(S, L, C) { S += ""
if (C.length>0) while (S.length<L) S = C + S ; return S }

function SpcsTo(S, L) { S += "" // SpcsTo is a reduction of PrfxTo
while (S.length<L) S = " " + S ; return S }

function StrU(X, M, N) { // X > -0.5e-N ; to M digits point N digits
var S = String(Math.round(X*Math.pow(10, N)))
if (/\D/.test(S)) return SpcsTo(X, M+N+1) // cannot cope
S = PrfxTo(S, M+N, '0') ; var T = S.length - N
return S.substring(0, T) + '.' + S.substring(T) }

function StrS(X, M, N) { return Sign(X) + StrU(Math.abs(X), M, N) }


Given that some of the function set are intended to give fixed-width
results, ISTM that Sign should give fixed-width output. When that's
*not* wanted, removing a bit of the code will be easy.

All of the code snippets, every one of them, will be reviewed and
re-written for 10.0 with some documentation and better variable names to
make them easier for newbes to be able to read and learn from. The FAQ
isn't geared to experienced users that can decipher code such as that
above, it is for people who *can't* write code such as that above but
want to learn from it.
 
R

RobG

Randy Webb wrote:
[...]
All of the code snippets, every one of them, will be reviewed and
re-written for 10.0 with some documentation and better variable names to
make them easier for newbes to be able to read and learn from. The FAQ [...]
is for people who *can't* write code such as that above but
want to learn from it.

Amen to that.
 
D

Dr J R Stockton

In comp.lang.javascript message <[email protected]>,
All of the code snippets, every one of them, will be reviewed and re-
written for 10.0 with some documentation and better variable names to
make them easier for newbes to be able to read and learn from. The FAQ
isn't geared to experienced users that can decipher code such as that
above, it is for people who *can't* write code such as that above but
want to learn from it.

Where a FAQ entry is headed by a request for an algorithm, a coding
tutorial is not called for. The Notes are the place for that.
 
R

Randy Webb

Dr J R Stockton said the following on 12/14/2006 4:09 PM:
In comp.lang.javascript message <[email protected]>,


Where a FAQ entry is headed by a request for an algorithm, a coding
tutorial is not called for. The Notes are the place for that.

Is there a point to that? I will repeat what I said "All of the code
snippets, every one of them, will be reviewed and *rewritten for 10.0*.
 
K

krs

The functions on my site have been updated since that was put in the
FAQ.

function Sign(X) { return X>0 ? "+" : X<0 ? "-" : " " }

function PrfxTo(S, L, C) { S += ""
if (C.length>0) while (S.length<L) S = C + S ; return S }

function SpcsTo(S, L) { S += "" // SpcsTo is a reduction of PrfxTo
while (S.length<L) S = " " + S ; return S }

function StrU(X, M, N) { // X > -0.5e-N ; to M digits point N digits
var S = String(Math.round(X*Math.pow(10, N)))
if (/\D/.test(S)) return SpcsTo(X, M+N+1) // cannot cope
S = PrfxTo(S, M+N, '0') ; var T = S.length - N
return S.substring(0, T) + '.' + S.substring(T) }

function StrS(X, M, N) { return Sign(X) + StrU(Math.abs(X), M, N) }

Given that some of the function set are intended to give fixed-width
results, ISTM that Sign should give fixed-width output. When that's
*not* wanted, removing a bit of the code will be easy.

can someone else confirm that StrS(1.035,1,2) returns +1.03, instead of
the correct value of +1.04, on Firefox2 (winxp)?
-ks
 
E

Evertjan.

krs wrote on 16 jan 2007 in comp.lang.javascript:
can someone else confirm that StrS(1.035,1,2) returns +1.03, instead
of the correct value of +1.04, on Firefox2 (winxp)?

Both FF2.0.0.1 and IE7 return:

+1.03

Why would +1.04 be the correct value?
 
V

VK

Evertjan. said:
Both FF2.0.0.1 and IE7 return:

+1.03

Why would +1.04 be the correct value?

Because:
If the number you are rounding is followed by 5, 6, 7, 8, or 9, the
number has to be rounded up.
If the number you are rounding is followed by 0, 1, 2, 3, or 4, round
the number down.

If some other rounding logic implemented then it must be spelled in the
code comments.

In this aspect the native toFixed method follows the common rounding
rules only on IE (JScript):
alert(1.035.toFixed(2)); // gives 1.04

Both FF and Opera show 1.03

But alert(1.036.toFixed(2)); // show 1.04 on all test browsers

This way FF and Opera seems implementing an altered rounding rule:

If the number you are rounding is followed by 6, 7, 8, or 9, the number
has to be rounded up.
If the number you are rounding is followed by 0, 1, 2, 3, 4 or 5 round
the number down.

Could anyone comment on "the bugs in JScripts implementation" - what is
exactly meant by that?
 
E

Evertjan.

FAQ server wrote on 13 dec 2006 in comp.lang.javascript:
-----------------------------------------------------------------------
FAQ Topic - How do I convert a Number into a String with exactly 2
decimal places?
-----------------------------------------------------------------------

When formatting money for example, to format 6.57634 to
6.58, 6.5 to 6.50, and 6 to 6.00?

Rounding of x.xx5 is uncertain, as such numbers are not
represented exactly.

N = Math.round(N*100)/100 only converts N to a Number of value
close to a multiple of 0.01; but document.write(N) does not give
trailing zeroes.

ECMAScript Ed. 3.0 (JScript 5.5 [but buggy] and JavaScript 1.5)
introduced N.toFixed, the main problem with this is the bugs in
JScripts implementation.

Most implementations fail with certain numbers, for example 0.07.
The following works successfully for M>0, N>0:

function Stretch(Q, L, c) { var S = Q
if (c.length>0) while (S.length<L) { S = c+S }
return S
}
function StrU(X, M, N) { // X>=0.0
var T, S=new String(Math.round(X*Number("1e"+N)))
if (S.search && S.search(/\D/)!=-1) { return ''+X }
with (new String(Stretch(S, M+N, '0')))
return substring(0, T=(length-N)) + '.' + substring(T)
}
function Sign(X) { return X<0 ? '-' : ''; }
function StrS(X, M, N) { return Sign(X)+StrU(Math.abs(X), M, N) }

Number.prototype.toFixed= new Function('n','return StrS(this,1,n)')

1
I would prefer to use regex when molding a string into shape:

<script type='text/javascript'>

function toFixed2prot(N) {
var re = new RegExp('(\d{'+N+'})$','')
return Math.floor(this*Math.pow(10, N)+.5).toString()
.replace(/(\d{2}$)/,'.$1')
}

Number.prototype.toFixed2 = toFixed2prot

// testing:

var x = (1.0351).toFixed2(2)
document.write(x+'<br>')
var x = (1.035).toFixed2(2)
document.write(x+'<br>')

</script>

The 1.035 problem i will adress in answer to VK
 
E

Evertjan.

VK wrote on 16 jan 2007 in comp.lang.javascript:
Because:
If the number you are rounding is followed by 5, 6, 7, 8, or 9, the
number has to be rounded up.
If the number you are rounding is followed by 0, 1, 2, 3, or 4, round
the number down.

True, if you define it that way, but a totally wrong argument.

document.write(1.035 + 0.005);

will return:

1.0399999999999998

so a decimal string like 1.035 seems to be internally represented and
stored by something a bit less than exactly 1.035.

When rounding 1.035 so in reality something like 1.0344444444444449,
would you expect the .toFixed to make an educated guess if
eiter 1.035 or 1.0344444444444449 was entered, as a string of the source
code or in a user input, before it was stored?

No. I for one would not! That is not the job expected from either
Math.round() or .toFixed().

I would have those functions to do exactly what is intended and fix the
problem of the binary conversion [the root of all this evil] sepretately.

If I needed to have an exact BCD [Binary Coded Decimal] effect,
I better construct this myself using strings or integer values with
exponents.

====================

btw:

That

document.write(1.035);

outputs:

1.035

is probably an effect of internal rounding by the write()
and the alert() methods.

document.write(1.0344444444444449);

outputs:

1.034444444444445

also a rounding effect, it seems.
 
V

VK

Evertjan. said:
document.write(1.035 + 0.005);

will return:

1.0399999999999998

so a decimal string like 1.035 seems to be internally represented and
stored by something a bit less than exactly 1.035.

I thought that the proposed proc was for rounding _provided_ numbers,
not for rounding internal IEEE-754 representations. The whole purpose
of such algorithm IMO is to make as close as possible i) the strict
"human" math learned at the school and ii) the approximate PC math. If
it was not a purpose then this code is misplaced to FAQ - let it be
moved to some PC-math specific site. For a user seeking for a quick
answer it is irrelevant that
US$ calculated price of 10.05 is really internally stored as
10.05XXXXXXXXXXX. All she wants to know is now to round it without
monetarily abusing himself or her buyer.
When rounding 1.035 so in reality something like 1.0344444444444449,
would you expect the .toFixed to make an educated guess if
eiter 1.035 or 1.0344444444444449 was entered, as a string of the source
code or in a user input, before it was stored?

Yes, absolutely. Rounding goes from rightmost number to left, with
0,1,3,4 rounded down and 5,6,7,8,9 rounded up. This is the school/bank
math and the proc either has to work this way or no use of it.
1.0344444444444449
1.034444444444445
1.03444444444445
1.0344444444445
1.034444444445
1.03444444445
1.0344444445
1.034444445
1.03444445
1.0344445
1.034445
1.03445
1.0345
1.035

If some proc cannot outperform this then no use to start the mess on
the first place, as there are no advantages over the native toFixed()
method.
 
E

Evertjan.

VK wrote on 16 jan 2007 in comp.lang.javascript:
If some proc cannot outperform this then no use to start the mess on
the first place, as there are no advantages over the native toFixed()
method.

Indeed.

However, I think there where / are some real bugs in .toFixed,
that have to be fixed. John S. knows such things.

However having or building a BCD library or using integer cents/pennies
throughout is always a possibility, when dealing with currency.
 
R

Richard Cornford

VK said:
I thought

If you appreciated for yourself how badly you do that you might be in a
position to see it as flagging the imminent arrival of another of your
streams of irrational, irrelevant, ill-considered and worthless
nonsense. Of course to appreciate your shortcomings you would have to
not suffer from them.
that the proposed proc was for rounding _provided_ numbers,
not for rounding internal IEEE-754 representations.

In a system where all numbers are IEEE floating point numbers it is not
possible to provide a number in any other form.
The whole purpose of such algorithm IMO

You opinions are, as always, wothless.
is to make as close as possible i) the strict "human" math
learned at the school

Where in school? Basic arithmetic classes, introductory statistics, at
Accountancy school, degree level pure mathematics?
and ii) the approximate PC math.

"And"? And what is "approximate PC math"? You have spouted a great deal
of twaddle on the subject of number representations in computers over
the years, which has made it obvious that you hardly understand what
computers do (or can do) when representing and manipulating numbures.
If it was not a purpose then this code is misplaced to
FAQ -

The code in the FAQ is intended to be an alternative to - toFixed - that
produces the same output as the ECMA 262 specified algorithm for -
toFixed - (ECMA 262. 3rd Ed. section 15.7.4.5). This allows for
consistent and predictable behaviour in the face of implementation bugs
in - toFiexed -, and says nothing as to the appropriateness of the code
(or of - toFixed -) for any particular task.
let it be moved to some PC-math specific site.

An alternative to - tofixed - is appropriate in a javascript FAQ.
For a user seeking for a quick answer it is irrelevant that
US$ calculated price of 10.05 is really internally stored as
10.05XXXXXXXXXXX. All she wants to know is now to round it
without monetarily abusing himself or her buyer.

You won't see it (being fundamentally incompetent yourself) but for
someone attempting to program a system that handles currency to take
such a cavalier attitude would be inept.

Because I have worked with them I know that UK share trading systems
employ 64 bit integer values for all currency values (representing
multiples of 1/1000 of the currency unit) and only perform integer
arithmetic on those values. Thus fractional results are truncated, not
rounded.
Yes, absolutely.

Why am I not surprised by that?
Rounding goes from rightmost number to left, with
0,1,3,4 rounded down and 5,6,7,8,9 rounded up.

So you think that there is only one way to round a number?
This is the school/bank math

You have never heard of "Banker's rounding"? And never been educated
beyond basic arithmetic?
and the proc either has to work this way or no use of it.

That is so often you answer. You encounter something that does not work
the way your deranged mind expects and so you declare it unusable.
If some proc cannot outperform this then no use to start
the mess on the first place, as there are no advantages
over the native toFixed() method.

While the native - toFixed - method produces inconsistent results across
implementations (and particularly while it is evidently faulty in IE) a
consistent alternative has many advantages. It may be that what you
think the code should do is not what it does do, and it may be that it
is inappropriate for many of the tasks that the inconsiderate would
apply it to (particularly systems handling currency), but so long as
what it does do is consistent then its use is appropriate in contexts
where that is the desired behaviour.

Richard.
 
V

VK

Richard Cornford wrote:
The code in the FAQ is intended to be an alternative to - toFixed - that
produces the same output as the ECMA 262 specified algorithm for -
toFixed - (ECMA 262. 3rd Ed. section 15.7.4.5).

On the entire Earth there are 2 to many 3 people giving some sh** on
such subject. This way for FAQ it is irrelevant
This allows for
consistent and predictable behaviour in the face of implementation bugs
in - toFiexed -, and says nothing as to the appropriateness of the code
(or of - toFixed -) for any particular task.

FAQ Topic - How do I convert a Number into a String with exactly 2
decimal places?
-----------------------------------------------------------------------
When formatting money for example, to format 6.57634 to
6.58, 6.5 to 6.50, and 6 to 6.00?
<snip>

So I would propose to remove all this irrelevant self-exposure stuff -
I am taking that JRS is cool in all imaginable aspects of math, but
this poor FAQ topic is not a place to fall it out.

Just answer straight to the question with the promised results, so
6.57634 becomes 6.58
and - by the way -
1.035 becomes 1.04

P.S. Obviously when someone is asking "How do I convert 10.5678 into a
String with exactly 2 decimal places?"
she doesn't mean " "How do I convert 10.5678 into 10.56". Even for ones
with C on calculus the presumed result is 10.57 and not 10.56
Thus way "how to round?" is always presumed, not "how to truncate". For
the latter a one-liner code is sufficient. What do IEEE-754 or
IEEE-754r tell is irrelevant for this FAQ: though anyone is welcome to
analyze it up to any depth in personal blog and advertise in post
signatures.
 
D

Dr J R Stockton

In comp.lang.javascript message <[email protected]
can someone else confirm that StrS(1.035,1,2) returns +1.03, instead of
the correct value of +1.04, on Firefox2 (winxp)?

It should on all systems return +1.03, because
1.035*100 -> 103.49999999999998
in IEEE Doubles.

If you want exact results for currency including halfpennies, you need
to work in units such that halfpenny quantities are stored exactly.
Study the second paragraph of the FAQ entry.

Read IEEE 754, if that's the right number; ECMA 262 should tell you.

The code in the FAQ is not exactly what I currently use.

<FAQENTRY>
The FAQ lacks the word "currency" which might well be a search target.

IMHO it would be better to omit the first three words of FAQ 4.6, "When
formatting money" and to create a new 4.x on the general topic of "How
should currency calculations be done?".

Points to include : reference to 4.6 & 4.7; distinction between
accounting (must be exact) and finance (often approximate)<g>[*]</g>;
converting to new currencies (e.g. DMk to Euro) has exact rules.

</FAQENTRY>

For the handling of currency (in Delphi), Google News for "Herbster" and
for "JohnH".


Any astrodynamicists or orbital mechanics here?


[*] "A billion here, a billion there ... it soon adds up to real
money!".
 
D

Dr J R Stockton

In comp.lang.javascript message <[email protected]>
, Tue, 16 Jan 2007 16:42:41, Richard Cornford
In a system where all numbers are IEEE floating point numbers it is not
possible to provide a number in any other form.

One might consider that to be tautological.

But one can represent numbers in other ways. In strings, for example,
or in arrays/objects. I have Pascal code for computing with integers of
up to about 65000 digits (bases 2 to 16), though the largest native size
is 32-bit.

The code in the FAQ is intended to be an alternative to - toFixed - that
produces the same output as the ECMA 262 specified algorithm for -
toFixed - (ECMA 262. 3rd Ed. section 15.7.4.5). This allows for
consistent and predictable behaviour in the face of implementation bugs
in - toFiexed -, and says nothing as to the appropriateness of the code
(or of - toFixed -) for any particular task.

Not in the opinion of its author. The code was intended to do the
required job, including leading zeroes or spaces, as is commonly
required and generally provided in other languages. It was NOT intended
to match a (debugged) toFixed.

Because I have worked with them I know that UK share trading systems
employ 64 bit integer values for all currency values (representing
multiples of 1/1000 of the currency unit) and only perform integer
arithmetic on those values. Thus fractional results are truncated, not
rounded.

Truncation is not a necessary consequence.
Delphi "currency" type is scaled by 10,000.


In such cases, the algorithm should round to, say, an integer of
micropounds, convert to string, add a decimal point and chop trailing
zeroes as needed.
Why am I not surprised by that?

Unreasonable anti-2 prejudice.
 
R

Richard Cornford

Dr said:
Richard Cornford posted:


One might consider that to be tautological.

It is tautological. VK is the only person who would ever need being told
(and probably would not see the inevitability even then).
But one can represent numbers in other ways.

Where a number is an entity of a strictly defined type and form being a
representation of a number is not necessarily being a number. Otherwise,
yes. Numbers, in the mathematical sense, can be represented in all sorts
of ways in javascript, and arbitrary mathematical operations applied to
those representations by javascript code.

Not in the opinion of its author. The code was intended to do the
required job, including leading zeroes or spaces, as is commonly
required and generally provided in other languages. It was NOT
intended to match a (debugged) toFixed.

Fair enough. I wrote a step-by-step implementation of the - toFixed -
algorithm to verify bugs in implementations, and it is not a piece of
code that I would want to use.
Truncation is not a necessary consequence.
Delphi "currency" type is scaled by 10,000.


In such cases, the algorithm should round to, say, an integer of
micropounds, convert to string, add a decimal point and chop
trailing zeroes as needed.
<snip>

You cannot pay out, or receive, a micropound. Centipouds are as small as
British money gets ;)

Richard.
 
D

Dr J R Stockton

In comp.lang.javascript message <[email protected]>
I would prefer to use regex when molding a string into shape:

Agreed.

Here's a rough first try, undertested, unsigned, PrfxTo cf FAQ 4.6
Stretch :-

function Hasty1(X) { var Y, Z, Q=0 // Trunc; Q=5 to Round. Bankers???
Y = String(Math.round(X*1000)+Q)
Z = PrfxTo(Y, 4, "0").replace(/(\d\d)(\d)$/, ".$1")
return Z }


OTOH, for a variable number of decimal places, substr/substring may be
easier than RegExp.
 
E

Evertjan.

Dr J R Stockton wrote on 17 jan 2007 in comp.lang.javascript:
In comp.lang.javascript message <Xns98BA88CBE8778eejj99@ 194.109.133.242>


Agreed.

Here's a rough first try, undertested, unsigned, PrfxTo cf FAQ 4.6
Stretch :-

function Hasty1(X) { var Y, Z, Q=0 // Trunc; Q=5 to Round.
Y = String(Math.round(X*1000)+Q)

Y = String(Math.floor(X*1000+Q))
Z = PrfxTo(Y, 4, "0").replace(/(\d\d)(\d)$/, ".$1")

You would not need the second ()

Z = ... .replace(/(\d\d)\d$/, ".$1")
return Z }

Would it be an idea if one defines x.xx5 as always round up,
thinking for the moment only of positive values,
to add a tiny bit:

round: Q = 5.0001

floor: Q = 0.0001

Is it true that the binary converion problem always gives result
differences that are slightly to low, [or correct of course]?
Bankers???

Personally, I do not need or want this silly idea of toggled rounding.
It is not important in scientific measurement, where an exact value is
never obtained anyway, only with currency, perhaps, in the amount
nonvirtual mortals only dream of.

So it cannot be that important in clientside(!) javascript.
OTOH, for a variable number of decimal places, substr/substring may be
easier than RegExp.

var Q = .0001 //5.0001
var NN = 1; for var i=0;i<N;i++) NN*=10
var Y = String(Math.floor(X*NN+Q))
..... .replace(new RegExp("(\\d{"+N+"})\\d$"), ".$1")
[N>0]

or:

var NN='1000000000000'.substr(0,N+1)
 

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,756
Messages
2,569,535
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top