5.1 How do I format a Number as a String with exactly 2 decimal places?

D

dhtml

I've been looking at one of the FAQ entry on formatting numbers, or
"toFixed".

http://jibbering.com/faq/#formatNumber

Quote:
| 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. See also: "Why does simple decimal arithmetic give strange
| results?" <URL: http://jibbering.com/faq/#binaryNumbers >
|
| 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 Number.prototype.toFixed, the main problem with this is the
| bugs in JScript's 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 = String(Math.round(X * Number("1e" + N)));
| if (S.search && S.search(/\D/) != -1) {
| return '' + X;
| }
| with (String(Stretch(S, M+N, '0')))
| return substring(0, T = (length-N)) + '.' + substring(T);
| }
|
| function Sign(X) {
| return X > 0 ? "+" : X < 0 ? "-" : " ";
| }
|
| function StrS(X, M, N) {
| return Sign(X) + StrU(Math.abs(X), M, N);
| }
|
| Number.prototype.toFixed = function(n){
| return StrS(this, 1, n);
| };

I think it should not use |with|, should use more descriptive variable
names, should not modify Number.prototype, should only include a sign if
the sign is negative, and should use a more descriptive name, such as
toFixedString(n).

What do you guys think of this code?

I've also noticed that many implementations' toFixed (op, saf, ff)
return a number instead of a string. For example, typeof 1.1.toFixed(2)
is "number" in all three aforementioned browsers. ECMA 262 says it
should be a string[1].

Garrett

[1] http://bclary.com/2004/11/07/#a-15.7.4.5
 
J

JR

Hi Garrett,
I think that formatNumber() function was intentionally made to return
a string, simply as that. Particularly, I prefer the old and good N =
Math.round(N*100)/100, but notice the warning given by the author that
"document.write(N) does not give trailing zeroes". For me, the
author's intention is clearly to format numbers as text, such as
currency.
I think it should not use |with|...

John Resig, the Javascript guru at Mozilla, is writing a book telling
us good things about |with|:
http://ejohn.org/blog/secrets-table-of-contents/ (section 7)
[...], should use more descriptive variable
names, should not modify Number.prototype, should only include a sign if
the sign is negative, and should use a more descriptive name, such as
toFixedString(n).

I agree with you, although the code is just an example.

Cheers,
Joao Rodrigues
 
D

dhtml

JR said:
Hi Garrett,
I think that formatNumber() function was intentionally made to return
a string, simply as that. Particularly, I prefer the old and good N =
Math.round(N*100)/100, but notice the warning given by the author that
"document.write(N) does not give trailing zeroes". For me, the
author's intention is clearly to format numbers as text, such as
currency.
That doesn't work.

Math.round((.1*100) / 100)

result: 0.
John Resig, the Javascript guru at Mozilla, is writing a book telling
us good things about |with|:
http://ejohn.org/blog/secrets-table-of-contents/ (section 7)

And if your friend jumped off a bridge...
[...], should use more descriptive variable
names, should not modify Number.prototype, should only include a sign if
the sign is negative, and should use a more descriptive name, such as
toFixedString(n).

I agree with you, although the code is just an example.

Here's another example, based on the original. It is faster and I think
a little more readable.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<title>Untitled</title>
</head>
<body>
<pre>
<script type="text/javascript">
var numberToFixed;
(function() {
numberToFixed = toFixedString;

function toFixedString(n, digits) {
var unsigned = toUnsignedString(Math.abs(n), digits);
return (n < 0 ? "-" : "") + unsigned;
}

function prependString(s, length, ch) {
var toAdd = length - s.length, pre, i;
if(toAdd-- > 0) {
for(i = 0, pre = ch; i < toAdd; i++) {
pre += ch;
}
return pre + s;
}
return s;
}

function toUnsignedString(n, digits) {
var t, s = Math.round(n * ("1e" + digits)) + "",
start, end;
if (/\D/.test(s)) {
return "" + n;
}
s = prependString(s, 1 + digits, '0');
start = s.substring(0, t = (s.length - digits));
end = s.substring(t);
if(end) {
end = "." + end;
}
return start + end; // avoid "0."
}
//------------------------------------------------------------------
var d = document;
d.writeln(" numberToFixed(9e-3, 12) => " + numberToFixed(9e-3, 12));
d.writeln(" numberToFixed(1.255, 2) => " + numberToFixed(1.255, 2));
d.writeln(" numberToFixed(1.355, 2) => " + numberToFixed(1.355, 2));
d.writeln(" numberToFixed(.1255, 3) => " + numberToFixed(.1255, 3));
d.writeln(" numberToFixed(.07, 2) => " + numberToFixed(.07, 2));
d.writeln(" numberToFixed(0.0000000006, 0) => " +
numberToFixed(0.0000000006, 1));
d.writeln(" numberToFixed(0.0000000006, 0) => " +
numberToFixed(0.0000000006, 0));

})();
</script>
</pre>
</body>
</html>


Results:
numberToFixed(9e-3, 12) => 0.009000000000
numberToFixed(1.255, 2) => 1.25
numberToFixed(1.355, 2) => 1.36
numberToFixed(.1255, 3) => 0.126
numberToFixed(.07, 2) => 0.07
numberToFixed(0.0000000006, 0) => 0.0
numberToFixed(0.0000000006, 0) => 0

Since binary number is used, there is uncertainty of rounding x.xx5.

numberToFixed(1.255, 2) => 1.25, yet, numberToFixed(1.355, 2) => 1.36.

Garrett
 
D

Dr J R Stockton

Tue said:
I've been looking at one of the FAQ entry on formatting numbers, or
"toFixed".

You should not be looking at that outdated code. Instead, you should be
looking at the linked code at Merlyn (4pp.), including its comment (so
use IE; or use View Source, noting that StrU, etc., are defined in file
include1.js).

| Number.prototype.toFixed = function(n){
| return StrS(this, 1, n);
| };
I think it should not use |with|,

Mere prejudice. There are cases where 'with' may be risky, but this is
not one of them. However, the current version at Merlyn does not use
'with'.
should use more descriptive variable names,
should only include a sign if the sign is negative,

That is easily changed by altering function 'Sign'. IMHO it is better
to provide the most complete answer, if brief; removing code is easier
than adding it. Note that a number which may be positive or negative
always needs a sign; "-" for negative but one of "" or " " or "+" for
positive. For example, in a simple table using <pre>, sign "" is
generally unsuitable.

The following gives a correct sign in all cases where there is one,
including zero (that will rarely be wanted, but it is available) :

function Sygn(X) { return (X + 1/X) < 0 ? "-" : "+" }

should not modify Number.prototype,
and should use a more descriptive name, such as toFixedString(n).

Inserted, I think by Jim, specifically to provide a reliable .toFixed.

I've also noticed that many implementations' toFixed (op, saf, ff)
return a number instead of a string. For example, typeof 1.1.toFixed(2)
is "number" in all three aforementioned browsers. ECMA 262 says it
should be a string[1].

"aforementioned"?

One cannot have 5.00, as opposed to 5 or 5.0, when using a Number.


If Thomas does not complain about citing a non-definitive reference, he
may be mellowing. And if ECMA or ISO had been cited, I'd not have
fetched all those 'bclary' kilobytes across the world.


The first argument to StrU is expected to be a Number or something
simple. StrU gives appropriate results for non-Numbers including
Boolean, null, undefined. It gives non-misleading results if the
argument is a function, array, or object. If there are any other
accessible types, I don't know what it does with them.

There's no need to insist that M & N are non-zero; but N should
definitely not be negative. N=0 is, however, generally inappropriate;
and one must think hard to find any use for M<0.

Merlyn also has StrT and StrW.
 
D

dhtml

Dr said:
You should not be looking at that outdated code. Instead, you should be
looking at the linked code at Merlyn (4pp.), including its comment (so
use IE; or use View Source, noting that StrU, etc., are defined in file
include1.js).

OK.

The explanation needs updating, too.

FOr example:
| 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.

THe problem is not with document.write, but with conversion of number to
string where the trailing 0's are lost.

| n = Math.round( n * 100)/100</ICODE> only converts n to a Number of
| value close to a multiple of 0.01; but converting the number to a
| string <ICODE>(n + "")</ICODE>, does not give trailing zeroes.

function numberToFixed2(n) {
n = "" + Math.round( n * 100)/100;
var split = n.split('.'),
end = split[1] || ".00";
if(end.length == 2) end += "0";
return split[0] + end;
}

Would answer the question in the FAQ. Maybe we should just go with
something short like that and leave a link to Merlyn for a longer
explanation of rounding.
Mere prejudice. There are cases where 'with' may be risky, but this is
not one of them. However, the current version at Merlyn does not use
'with'.

Being "less clear" might be subjective, but |with| is measurably slower.
That is easily changed by altering function 'Sign'.

It is trivial to include that code for the sign as a statement.

IMHO it is better
to provide the most complete answer, if brief; removing code is easier
than adding it. Note that a number which may be positive or negative
always needs a sign; "-" for negative but one of "" or " " or "+" for
positive. For example, in a simple table using <pre>, sign "" is
generally unsuitable.

The following gives a correct sign in all cases where there is one,
including zero (that will rarely be wanted, but it is available) :

function Sygn(X) { return (X + 1/X) < 0 ? "-" : "+" }




Inserted, I think by Jim, specifically to provide a reliable .toFixed.

An application with several pages, it might be uncertain which 'toFixed'
is being used - the native one, or the user-defined one. This confusion
is increased when there are multiple developers on a project. To avoid
this ambiguity, the user-defined toFixed should be imported on every page.

However, most applications won't need a toFixed on all or even most
pages -- probably only a minority -- so it would make sense to import
that code only where it is used. The alternative is to create a
numberToFixed function, then import that function where it is needed.
I've also noticed that many implementations' toFixed (op, saf, ff)
return a number instead of a string. For example, typeof 1.1.toFixed(2)
is "number" in all three aforementioned browsers. ECMA 262 says it
should be a string[1].

"aforementioned"?

One cannot have 5.00, as opposed to 5 or 5.0, when using a Number.

Seems to be only with a negative number:-

javascript:alert( typeof (-1.255 .toFixed(5)) + ': '+ -1.255
..toFixed(5) )

Safari 2, Safari 3, Firefox 3, Opera 9.5

elerts "number: -1.255"

The first argument to StrU is expected to be a Number or something
simple. StrU gives appropriate results for non-Numbers including
Boolean, null, undefined. It gives non-misleading results if the
argument is a function, array, or object. If there are any other
accessible types, I don't know what it does with them.

That is fine. The caller, toFixed, in this case, should be responsible
for that. There is no need to check to see if - n - is a function. That
would make the code ugly.
There's no need to insist that M & N are non-zero; but N should
definitely not be negative. N=0 is, however, generally inappropriate;
and one must think hard to find any use for M<0.

Variable N is the number being formatted. I've renamed this to n.
Variable M is how many digits to extend the number by. This gets passed
to Stretch as parameter variable L. I've renamed to digits.

Garrett
 
D

Dr J R Stockton

Wed said:
Here's another example, based on the original. It is faster and I think
a little more readable.
function prependString(s, length, ch) {
var toAdd = length - s.length, pre, i;
if(toAdd-- > 0) {
for(i = 0, pre = ch; i < toAdd; i++) {
pre += ch;
}
return pre + s;
}
return s;
}

I see no point in calculating the number of characters to add and then
testing to see if that is positive and then building a string of that
length and then returning the concatenation when one can just add
characters while the string is shorter than desired. It does seem a
little quicker; but, if speed matters, that code should be in-line and
optimised. PrfxTo works reasonably if c is not a one-character string;
prependString assumes that it will be. Here it will always contain a
single space. Counting down from toAdd to 0 might be faster.

Once the length of a needed non-short string of a given character (or
string) is known, that string can be generated more quickly by a subtler
approach than adding characters one by one. I doubt whether any
advantage will be seen for likely sizes here.

function BigCat(N) {
if (!N) return ""
if (N&1) return BigCat(N-1) + "x"
var T = BigCat(N>>1) ; return T + T }

If that were made iterative, it should be faster still.

function toUnsignedString(n, digits) {

That omits the useful feature of padding to the left to get a fixed-
length string, which is useful in, for example, simple <pre> tables. It
is IMHO better not to use variables i j k l m n for values which are not
necessarily integer when 20 other letters including x y z are available.

var t, s = Math.round(n * ("1e" + digits)) + "",

Using Math.pow(10, digits) seems to be quicker in IE7, Opera, & Safari
by a greater factor than it is slower in FF3. And Math.pow may be
easier to understand.

if (/\D/.test(s)) {
return "" + n;

If the argument is 'null', it will be converted to zero. That is
undesirable here; if a programmer wants that, it can be done externally.

end = s.substring(t);
if(end) {

I don't much like 'end' there. It's not an ISO 16262 3 keyword or
future reserved word; but it is commonly one in other languages, and
might perhaps become one in JS. How about 'head' and 'tail' (or 'bow' *
'aft' !)?
return start + end; // avoid "0."

One should not preclude "0.", since it could conceivably be wanted, and
digits=0 does call for it. Granted that a decimal point should *almost*
always have a digit on each side.


- -

AISB^n : A little while ago, late 2008 UTC, a PC set to Sydney (where it
was then already 2009) showed an error in http://saturn.jpl.nasa.gov/.
The PC is now set for Hawaii and is awaiting the passage of midnight
UTC, just after 23:59:60 on this occasion - a different manifestation of
the error is expected. No symptom will be seen when/where the UTC and
local year numbers are the same.

- -

The thrice-weekly FAQ posts are still posting an October version.
 
D

dhtml

Conrad said:
Dr J R Stockton wrote:
[...]


That's the wrong test.

Right. I wasn't paying close enough attention.


The numberToFixed example is useful, but the explanation is getting long
already. Would a shorter answer be enough?

| 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. See also: #binaryNumbers
|
| n = Math.round( n * 100)/100 converts n to a Number value
| close to a multiple of 0.01. Converting the number to a string
| (n + ""), does not give trailing zeroes.
|
| Function numberToFixed2 formats a number to two decimal places.

function numberToFixed2(n) {
n = "" + Math.round(n * 100)/100;
var split = n.split('.'),
end = split[1] || "00";
if(end.length == 1) end += "0";
return split[0] + "." + end;
}

| This is mostly accurate, though errors in rounding will occur
| with numbers that are very close to x.x5. For example,
| numberToFixed2(.004999999999999999); results 0.01.
|
| ECMAScript Ed. 3.0 (JScript 5.5 [but buggy] and JavaScript 1.5)
| introduced Number.prototype.toFixed. There are bugs in
| JScript's implementation with certain numbers, for example 0.07.
|
| Many implementations do not round, so 1.255.toFixed(2) will
| return 1.25 and 1.355.toFixed(2) returns 1.36.
| However, some implementations (notably JScript and some versions
| of Webkit) do round, so 1.255.toFixed(2) will return 1.26.
|

Garrett
 
D

Dr J R Stockton

Wed said:
OK.

The explanation needs updating, too.

FOr example:
| 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.

THe problem is not with document.write, but with conversion of number
to string where the trailing 0's are lost.

We are discussing a FAQ; the *perceived* problem is with document.write.

| n = Math.round( n * 100)/100</ICODE> only converts n to a Number of
| value close to a multiple of 0.01; but converting the number to a
| string <ICODE>(n + "")</ICODE>, does not give trailing zeroes.

Those who need the information are typically not writing that.
function numberToFixed2(n) {
n = "" + Math.round( n * 100)/100;
var split = n.split('.'),
end = split[1] || ".00";
if(end.length == 2) end += "0";
return split[0] + end;
}

Would answer the question in the FAQ.

Better not to post untested code. Those who have read Merlyn's
<URL:http://www.merlyn.demon.co.uk/js-round.htm#BoF> will have seen
where putative solutions can be pasted for test. If there are likely
faults not found, I can add test cases.

Note that numberToFixed2 is not suited to all currencies. There are or
can be 1000 dirhams or fils or millimes in the dinar; and 10 haos in the
dong <http://en.wikipedia.org/wiki/List_of_circulating_currencies>.


It is much easier to simplify the argument list for StrU and get an
equally reliable function stru such that stru(X) == StrU(X, 1, 2)
than it would be to make anything else reliable of numbertoFixed2.

Code should not give a confusing output on being given, for X, NaN
Infinity or undefined.


Maybe we should just go with something short like that and leave a
link to Merlyn for a longer explanation of rounding.

I think that could be too big a jump for the class of user for which the
FAQ is intended.

Being "less clear" might be subjective, but |with| is measurably slower.

The prime use of StrU is to format for output; in considering internal
speed, one should be aware of the time taken for output.


It is trivial to include that code for the sign as a statement.

It is not trivial to think of doing so. It is a useful reminder.
IMHO it is better

An application with several pages, it might be uncertain which
'toFixed' is being used - the native one, or the user-defined one. This
confusion is increased when there are multiple developers on a project.
To avoid this ambiguity, the user-defined toFixed should be imported on
every page.

However, most applications won't need a toFixed on all or even most
pages -- probably only a minority -- so it would make sense to import
that code only where it is used.

How about putting the .toFixed bit in a code box of its own? It should
probably be wrapped in code to install it only if .toFixed does not
exist or shows the bug : Merlyn include1,js has the former :
if (!Number.prototype.toFixed) {
Number.prototype.toFixed = // JL
new Function("X",
" /* toFixed */ if (!X) X=0\n return StrS(this, 1, X)") }

(which is contrived so that, in IE, toFixed.toString() includes comment
and layout).





Confusing notation. It made me look in the spec for .toFixed with three
arguments.
return a number instead of a string. For example, typeof 1.1.toFixed(2)
is "number" in all three aforementioned browsers. ECMA 262 says it
should be a string[1].
"aforementioned"?
One cannot have 5.00, as opposed to 5 or 5.0, when using a Number.

Seems to be only with a negative number:-

javascript:alert( typeof (-1.255 .toFixed(5)) + ': '+ -1.255
.toFixed(5) )

Safari 2, Safari 3, Firefox 3, Opera 9.5

One should always test Web code in IE also; IE is dominant.
elerts "number: -1.255"

There seems to be a question of operator priority.

(-1.255).toFixed(5) // -1.25500 // OK
-1.255 .toFixed(5) // -1.255 // Firefox, IE

From small Flanagan, dot is highest priority, subtract is high priority,
unary minus is unmentionable.

X = -1.255 ; X.toFixed(5) // -1.25500

Nowadays, one should only deem an effect to be a browser bug if at least
one browser does NOT show it.


That is fine. The caller, toFixed, in this case, should be responsible
for that. There is no need to check to see if - n - is a function. That
would make the code ugly.

A function makes the output wonderfully ugly, If it is not noticed in
test, the author of the page should get a new job. StrU can reasonably
be used on a one-element array. I think I'd recommend handling function
and object better if it cost no more than about five characters of code.

Variable N is the number being formatted. I've renamed this to n.
Variable M is how many digits to extend the number by. This gets passed
to Stretch as parameter variable L. I've renamed to digits.

I'm referring to StrU, as in the FAQ.


In any discussion of rounding numbers such as 0.125 to two places, one
must refer to the technical convention and to Banker's, which give
respectively 0.13 & 0.12. if that is forgotten, confusion may occur.
 
J

JR

Math.round((.1*100) / 100)

Look out where you put the parentheses because your example is
different from mine:

Math.round(N*100)/100

For instance:
var N = 12.356667;
Math.round(N*100)/100 // returns 12.36

But Math.round((N*100)/100) returns 12 !

And if your friend jumped off a bridge...

No, John Resig is a great developer and when he says / writes about
with keyword we have to pay attention to his words.

Cheers,
Joao Rodrigues
 
D

dhtml

JR said:
Look out where you put the parentheses because your example is
different from mine:

You didn't see the more recent post from yesterday? It has an example
that uses that.
No, John Resig is a great developer and when he says / writes about
with keyword we have to pay attention to his words.

You've provided no explanation of why |with| should be used instead of a
variable.

The advice to use |with| is bad advice for the reasons I've already
mentioned. You seem to have missed that. Here it is again:

1) slower
2) reduces code clarity

Following programming advice that you do not have any ability to
qualitatively assess is unprofessional. Especially when we're talking
advice over such matters as whether or not it is a good idea to use the
|with| statement (it isn't).

Garrett
 
J

JR

Hi Garrett (dhtml),
The advice to use |with| is bad advice for the reasons I've already
mentioned [...]

In my first post I did not encourage anyone to use "with" statement.
What I tried to say is that "with" must have its advantages only
because of what is written in John Resig's website (http://ejohn.org/
blog/secrets-table-of-contents/):

"7. with(){} (Understanding how with works - its benefits and how it
can be best used.)

1. Understanding with: Pros and cons
2. Packages and Namespacing
3. function test(){with(this){ ... }}
4. Templating"

Everybody seems to be influenced by the excellent Javascript - The
Definitive Guide 4th edition, which states that "despite its
occasional convenience, the use of the with statement is frowned upon.
JavaScript code that uses with is difficult to optimize and may
therefore run more slowly than the equivalent code written without the
with statement. Furthermore, function definitions and variable
initializations within the body of a with statement can have
surprising and counterintuitive behavior. For these reasons, it is
recommended that you avoid the with statement."

But let's notice that "avoid" the with statement doesn't mean
necessarily "to never use" it. In this sense, I truly believe that
John Resig must have very good reasons to spend his precious (and
expensive) time writing about some benefits in using "with" statement
in modern javascript.

Kind regards,
Joao Rodrigues
 
D

dhtml

Dr said:
We are discussing a FAQ; the *perceived* problem is with document.write.

Usually the output would be set on the text of an element or value
property of an input.

input2[textContent] = formatNumber(input1.value, 2);

(assuming textContent defined elsewhere).

Converting a number to a string, the formatting must be defined. That is
at the heart of the problem.
Better not to post untested code. Those who have read Merlyn's
<URL:http://www.merlyn.demon.co.uk/js-round.htm#BoF> will have seen
where putative solutions can be pasted for test. If there are likely
faults not found, I can add test cases.

Yes there were fairly obvious problems that.

[...]
How about putting the .toFixed bit in a code box of its own? It should
probably be wrapped in code to install it only if .toFixed does not
exist or shows the bug : Merlyn include1,js has the former :
if (!Number.prototype.toFixed) {
Number.prototype.toFixed = // JL
new Function("X",
" /* toFixed */ if (!X) X=0\n return StrS(this, 1, X)") }

(which is contrived so that, in IE, toFixed.toString() includes comment
and layout).

That would add Number.prototype.toFixed to implementations that did not
have it. It would not fix implementations that have different (buggy)
implementation of Number.prototype.toFixed.

A function expression or declaration is a better choice than new Function.

What I was trying to explain about "most applications won't need a
toFixed on all or even most pages" is that there should not be two
versions of Number.prototype.toFixed. If a user-defined
Number.prototype.toFixed replaces the native one, then that method
should be included for the entire application. Most applications would
probably only need to rely on number formatting in a few places.
Including that function globally would be wasted bandwidth on pages that
don't use it and would consume browser cache.

The alternative is a function that is used on certain pages:-

numberToFixed(n, precisionDigits);

Looking at the source code, it would be obvious to distinguish a call to
user-defined numberToFixed from native Number.prototype.toFixed.
A function makes the output wonderfully ugly, If it is not noticed in
test, the author of the page should get a new job. StrU can reasonably
be used on a one-element array. I think I'd recommend handling function
and object better if it cost no more than about five characters of code.

inp = +inp||0;

It would always "work", even for function, et c, resulting in 0's.
(Although I don't see why anyone would pass a function to StrU, even by
accident).

If N is going to be 0, then it should handle that case better.
StrU("1.1", 1, 0);
Results:
"1."

Garrett
 
D

dhtml

JR said:
Hi Garrett (dhtml),
The advice to use |with| is bad advice for the reasons I've already
mentioned [...]

In my first post I did not encourage anyone to use "with" statement.
What I tried to say is that "with" must have its advantages only
because of what is written in John Resig's website (http://ejohn.org/
blog/secrets-table-of-contents/):

"7. with(){} (Understanding how with works - its benefits and how it
can be best used.)

1. Understanding with: Pros and cons
2. Packages and Namespacing
3. function test(){with(this){ ... }}
4. Templating"

So?

Everybody seems to be influenced by the excellent Javascript -

Many of us here are more influenced by the language specification and
our own experience.

You've also stated that |with| "must have its advantages solely on the
basis of what is written on John Resig's weblog." The entry doesn't say
what those benefits/advantages are and you didn't either. We have not
seen any evidence that there are advantages to using |with|. There are
certainly none to be had in the example code and the updated code; quite
the opposite. The code is faster without it. I see the new for StrU on
Merlyn does not use |with|.

Garrett
 
D

Dr J R Stockton

Thu said:
I see the new for StrU on Merlyn does not use |with|.

It's not new, at least in that respect. The previous FAQ maintainer was
unwilling to visit the site.
 
D

Dr J R Stockton

Thu said:
Dr said:
We are discussing a FAQ; the *perceived* problem is with
document.write.

Usually the output would be set on the text of an element or value
property of an input.

input2[textContent] = formatNumber(input1.value, 2);

(assuming textContent defined elsewhere).

Converting a number to a string, the formatting must be defined. That
is at the heart of the problem.

Yes; I should have provided examples other than document.write; or at
least an "etc.". The point remains that making marks on the screen is
likely to take more time than first converting Number to String.

That would add Number.prototype.toFixed to implementations that did not
have it. It would not fix implementations that have different (buggy)
implementation of Number.prototype.toFixed.

The code in include1.js has (now) comment
// || !+0.9.toFixed(0) maybe
I don't know of any version of toFixed that has bugs but not that one;
but I'd prefer not to trust toFixed. Before changing my actual
executable code, I need to review all use of toFixed ... I only use it
"for real" as toFixed(3) on large positive numbers in a page using no
include files; that should be safe.
A function expression or declaration is a better choice than new Function.
What I was trying to explain about "most applications won't need a
toFixed on all or even most pages" is that there should not be two
versions of Number.prototype.toFixed. If a user-defined
Number.prototype.toFixed replaces the native one, then that method
should be included for the entire application. Most applications would
probably only need to rely on number formatting in a few places.
Including that function globally would be wasted bandwidth on pages
that don't use it and would consume browser cache.

It's rather small and would normally be in an include file.
The alternative is a function that is used on certain pages:-

numberToFixed(n, precisionDigits);

Looking at the source code, it would be obvious to distinguish a call
to user-defined numberToFixed from native Number.prototype.toFixed.

The intent, AISB, was to provide a substitute for toFixed, so that code
containing .toFixed would execute without alteration. In other
circumstances, StrS can be used in the normal manner.

There could be a FAQ entry on adding methods to an existing object type
- my js-date8.htm could probably be improved.

inp = +inp||0;

It would always "work", even for function, et c, resulting in 0's.
(Although I don't see why anyone would pass a function to StrU, even by
accident).

That code gives zero for inputs which are not zero, including NaN, so is
unacceptable. I can easily write a function name F where I needed F();
it's a likely accident. But the error of providing an Object, a
Function, or a multi-element array is obvious on test, which suffices.
If N is going to be 0, then it should handle that case better.
StrU("1.1", 1, 0);
Results:
"1."

That is what it should do, since that is what N=0 means, and it is
something that might be wanted. To get an integer string with no
decimal point, one can use String(Math.round(X)), for example.
 
D

dhtml

Dr said:
Yes; I should have provided examples other than document.write; or at
least an "etc.". The point remains that making marks on the screen is
likely to take more time than first converting Number to String.

You lost me on that one.
The intent, AISB, was to provide a substitute for toFixed, so that code
containing .toFixed would execute without alteration. In other
circumstances, StrS can be used in the normal manner.


That code gives zero for inputs which are not zero, including NaN, so is
unacceptable. I can easily write a function name F where I needed F();
it's a likely accident. But the error of providing an Object, a
Function, or a multi-element array is obvious on test, which suffices.

The example on the FAQ, when passed a function, results " NaN" (note the
leading space).

That is what it should do, since that is what N=0 means, and it is
something that might be wanted. To get an integer string with no
decimal point, one can use String(Math.round(X)), for example.

One could also then add the decimal:-

Math.round(X) + ".";

Native Number.prototype.toFixed, for 1.1.toFixed() return "1" not "1.".


From: http://www.merlyn.demon.co.uk/js-rndg1.htm#toF

Function Sign returns "+" if positive. Number.prototype.toFixed does
not. It might be a good idea to not use this function for StrS.

Convention reserves UpperCamelCase for function constructors. Variables,
Functions that are not constructors, properties, are typically camelCase.

So, for example, Sign would be named sign, PrfxTo, prfxTo, et al.

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 += "";
while (S.length < L) {
S = " " + S;
}
return S;
}

function StrU(X, M, N) {

// (GS) Not necessary to convert St to string, PrfxTo does that.
var St = String(Math.round(X * Math.pow(10, N)));

// (GS) null check is redundant; all
// that is needed is: /\D/.test(St)
if (X === null || /\D/.test(St)) {
return SpcsTo(X, M + N + 1);
}
St = PrfxTo(St, M + N, "0");
var J = St.length - N;
return St.substring(0, J) + "." + St.substring(J);
}

function StrS(X, M, N) {
// (GS) "+" might be undesirable. Number.prototype.toFixed
// does not use a "+" sign.
// x < 0 ? "-" : "" + StrU(Math.abs(x), M, N);
return Sign(X) + StrU(Math.abs(X), M, N);
}


Function 'SpcsTo' could also be faster as:-
function prependString(s, length, ch) {
var toAdd = length - s.length, pre, i;
if(toAdd-- > 0) {
for(i = 0, pre = ch; i < toAdd; i++) {
pre += ch;
}
return pre + s;
}
return s;
}


Garrett
 
D

Dr J R Stockton

Sun said:
You lost me on that one.

Number-to-String is, very commonly, followed by making marks on the page
corresponding to the String. That's a fairly slow process. Therefore,
considerations of refining speed of operation of StrU are unlikely to
justify omitting of features in the conversion routine.

Changing to Math.power from using that "1e" trick saved time without
affecting functionality.

Argument c could probably be removed from PrfxTo; a search of my site
shows that I do not use anything other than "0" & '0'. Or PrfxTo could
be used to implement what SpcsTo does. And both routines could be
inlined within StrU.


The example on the FAQ, when passed a function, results " NaN" (note
the leading space).

Well, a function is not a number, so that's tolerable. But the
StrU(Func) with code as on my site is equivalent to Func.toString().
The important thing is that it should not give anything that might be
taken as an ordinary number. You seem to be referring to StrS;
Math.abs(Func) is NaN, and the space is the sign, as NaN is neither >0
nor <0.

I now see that StrS also could be upgraded to deal with some cases of
non-numeric input; I've rather concentrated on StrU, and on the calls of
StrS actually in my other code. In particular, null & undefined need
attention. It might be desirable to merge StrU and StrS more closely.

P.S. YES OR NEARLY SO; DONE, but needs a night's thought and more test.
Looks OK in js-round.htm#BoF .


Function Sign returns "+" if positive. Number.prototype.toFixed does
not. It might be a good idea to not use this function for StrS.

No; that means that the new .toFixed should not use StrS, but something
like StrS but with a different Sign function, if an exact match is
needed.



Probably the FAQ should say that the sign function should be chosen or
adapted ...



function StrU(X, M, N) {

// (GS) Not necessary to convert St to string, PrfxTo does that.

I wondered for a while why there was an ASCII Group Separator reference
in those parentheses.

So does .test(St) : one should do the conversion once, not twice, on the
normal route through the code..


var St = String(Math.round(X * Math.pow(10, N)));

// (GS) null check is redundant; all
// that is needed is: /\D/.test(St)
if (X === null || /\D/.test(St)) {
return SpcsTo(X, M + N + 1);

It is not redundant. In JavaScript, +0, -0, and null are all different.
If the argument has a value of null, the reference code should not
interpret it as 0. An end programmer is at liberty to remove the test.


Function 'SpcsTo' could also be faster as:-
function prependString(s, length, ch) {
var toAdd = length - s.length, pre, i;
if(toAdd-- > 0) {
for(i = 0, pre = ch; i < toAdd; i++) {
pre += ch;
}
return pre + s;
}
return s;
}

I think a while loop could be faster too; counting down to 0 has been
said to be better. And if toAdd is more than a few, using the method in
my BigCat (or Wombat) (in js-misc0.htm; I see I have a different one in
js-tests.htm :-( ; that needs attention ; fixed, but not uploaded).
 
D

Dr J R Stockton

That was posted at 23:39 on Jan 11 - why does Google put 12:39 am ?

P.S.  YES OR NEARLY SO; DONE, but needs a night's thought and more test..
Looks OK in js-round.htm#BoF .


function StrC(X, M, N, B) { var St, J // to M digits point N digits
St = String(Math.round((B ? Math.abs(X) : X)*Math.pow(10, N)))
if (X===null || /\D/.test(St)) return SpcsTo(X, M+N+1+B) // Exit
St = PrfxTo(St, M+N, "0") ; J = St.length - N
return (B ? Sign(X) : "" ) +
St.substring(0, J) + "." + St.substring(J) }

function StrU(X, M, N) { return StrC(X, M, N, 0) /* X > -0.5e-N */ }

function StrS(X, M, N) { return StrC(X, M, N, 1) }
 
D

Dr J R Stockton

In comp.lang.javascript message <927dac94-be2b-4bf6-8377-d5adf107c335@i1
8g2000prf.googlegroups.com>, Mon, 12 Jan 2009 04:05:56, Dr J R Stockton
function StrC(X, M, N, B) { var St, J // to M digits point N digits
St = String(Math.round((B ? Math.abs(X) : X)*Math.pow(10, N)))
if (X===null || /\D/.test(St)) return SpcsTo(X, M+N+1+B) // Exit
St = PrfxTo(St, M+N, "0") ; J = St.length - N
return (B ? Sign(X) : "" ) +
St.substring(0, J) + "." + St.substring(J) }

function StrU(X, M, N) { return StrC(X, M, N, 0) /* X > -0.5e-N */ }

function StrS(X, M, N) { return StrC(X, M, N, 1) }

One may want StrS to give the same sign for a non-zero input of either
sign that rounds to zero as it does for a true zero input. One way of
doing that is to replace Sign(X) with Sign(X*St). Another is to insert
if (St=="0") X = 0.

StrU(1e00,1,2) gives "1.00".
StrU(1e18,1,2) gives "1000000000000000000.00".
StrU(1e19,1,2) gives "10000000000000000000".
StrU(1e20,1,2) gives "100000000000000000000".
StrU(1e21,1,2) gives "1e+21".

If it is felt necessary to get trailing ".00" for 1e19 & 1e20, the Exit
route can be modified with a RegExp replace that adds ".00" if String(X)
is (signed) all-digit.
 
D

dhtml

Dr said:
In comp.lang.javascript message <927dac94-be2b-4bf6-8377-d5adf107c335@i1
8g2000prf.googlegroups.com>, Mon, 12 Jan 2009 04:05:56, Dr J R Stockton


One may want StrS to give the same sign for a non-zero input of either
sign that rounds to zero as it does for a true zero input. One way of
doing that is to replace Sign(X) with Sign(X*St). Another is to insert
if (St=="0") X = 0.

StrU(1e00,1,2) gives "1.00".
StrU(1e18,1,2) gives "1000000000000000000.00".
StrU(1e19,1,2) gives "10000000000000000000".
StrU(1e20,1,2) gives "100000000000000000000".
StrU(1e21,1,2) gives "1e+21".

If it is felt necessary to get trailing ".00" for 1e19 & 1e20, the Exit
route can be modified with a RegExp replace that adds ".00" if String(X)
is (signed) all-digit.

What does StrC mean? I was guessing would mean "convert to a signed
numerical string."

The variable names are also unclear to me. Also, as a convention,
variables are camel case, as are function names (unless the function is
intended to be used as a constructor).

The FAQ mostly follows that convention. The code could be more explicit
with more descriptive variable and function names, such as
'prependString', 'toUnsignedString', 'toFixedString'.

What do you think?
 

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,011
Latest member
AjaUqq1950

Latest Threads

Top