How to format numbers in JavaScript flexibly and efficiently
If you have questions or comments or bugs report, or a change to make, be sure to use the project’s new homepage: Flexible JS Formatting Libraries
This article continues my series on parsing and formatting data with JavaScript, this time with numeric data. I don’t need to do number parsing, but formatting is very useful. The technique is similar to my date formatting code — code that writes code (for raw speed), using custom format specifier strings (for flexibility and ease of use). The result is number formatting functionality that is highly efficient, flexible, and easy to use.
First, the idea: you have a number, you want it formatted a certain way. Here’s how:
var dollars = 5.001;
alert(dollars.numberFormat("$0.00");
// result: "$5.00"
var percent = .08134;
alert(percent.numberFormat("0.00%");
// result: "8.13%"
var bignum = 12831242485472;
alert(bignum.numberFormat("0,0,, million");
// result: "12,831,243 million"
My custom date formatting code used PHP’s date-formatting syntax because it’s much less context-sensitive and (I think) more useful than Microsoft’s, but my number-formatting syntax is similar to Microsoft’s because it’s much more widely used and I don’t see an existing, better alternative. Rather than documenting it separately, I’ll just point you to the (poor quality) Microsoft documentation for the .NET Custom Numeric Format Strings functionality, and list the differences from my implementation:
- Rounding works differently in multi-section format strings. In .NET with a two-section string,
This is not true in my code — the number is formatted according to its value, and once the code decides which section applies, that section will be used no matter what happens during rounding.If the number to be formatted is negative, but becomes zero after rounding according to the format in the second section, then the resulting zero is formatted according to the first section.
- Question marks are digit placeholders just like the number sign (
#), but if there’s no digit to insert, they get replaced with spaces, not removed. They can be used for space-padding, which might be useful for, say, accounting notation. - You don’t have to enter quotes around strings that should be mixed in with the number placeholders. In fact, my syntax is much more permissive than the Microsoft syntax: anything can go anywhere. You can put arbitrary strings smack in the middle of your number if you want.
- It’s not internationalized.
I’ve only implemented a subset of the various number-formatting syntaxes I’ve seen in spreadsheets and so forth. The subset is about 85% complete in my opinion. However, I think it’s functionally about 99% complete, which means I think 99% of the time you want to format a number, it will do what you want. The tradeoff is simplicity and speed. Number formatting is actually much more difficult than date formatting, and I’ve tried to keep the code sane.
I have a set of unit tests, which use the excellent JsUnit library. Bring up the unit test page and enter the following url to be tested: www.xaprb.com/articles/number-test.html.
Of course there’s the obligatory demo page, too.



This is a great script!
I noticed 2 things though:
1. If a number such as -100000 is supplied and the format is #,#.00, the resulting string is -,100,000.00.
2. If a value such as -1 is formatted as #,#.00, the resulting formatted string is 01.00 (no more negative symbol).
[Ed: removed suggested fixes... thanks for the help finding the problem]
Mark
30 May 06 at 12:41 am
Mark, that’s a great catch. I’ve added those two cases to the unit test file and fixed them. Thanks for the help testing this. If you find anything else wrong, please post more comments.
Xaprb
30 May 06 at 2:39 pm
Bloody fantastic script!!! Number formatting has never been easier and user-friendly!
A few things noticed from the demo:
1. (An extension of Mark’s comment) The number -1 with “#,#.00″ format returns “-01.00″. This happens with truncated numbers too (e.g., -2000 with the format “#,#, thou” returns “-02 thou”).
2. When truncating a long number it always rounds up. Take the number 1200300, “#,#,, mil” returns “2 mil” and “#,#, thou” returns “1,201 thou”. The way around this is by adding decimal places, such as “#,#,,.# mil”, then 1240000 returns “1.2 mil” and 1250000 returns “1.3 mil”
3. (A wishful thinking) In accounting the format for a negative amount is -$300, which does read better than $-300.
Jen
2 Jul 06 at 10:17 pm
Jen, thanks for pointing those out. I hope to be able to fix point 1 soon. I will have to research point 2 more, but I think that’s correct behavior according to the MS documentation, and for point 3, I’m not sure what to do. In many accounting applications the $ should actually be lined up along the leftmost column, with spaces between the $ and the – or numbers:
Xaprb
8 Jul 06 at 4:37 pm
HI, can you to put the code of example?
Anonymous
13 Oct 06 at 11:29 am
I’m not sure what you mean. Can you re-word the question please?
Note to all: sorry I’ve not been giving much love to this lately. I have so much going on, so many projects… if you fix anything, please send patches. I can at least integrate patches, even if I don’t take the time to fix any code myself.
Xaprb
13 Oct 06 at 12:34 pm
Hi, i’m italian user that use your fantastic script.
Here we have a problem, for decimal part we use , instead of . and on thousand we use . instead of ,
Where i can modify to invert comma and dot, or can you insert a variable like ‘us’ or ‘eu’ for metric users.
Regards and thank for what you can do
Bye,
Alex
Alex
26 Dec 06 at 12:59 pm
Hi Alex, thanks for writing in. I think it would be hard to do “right,” BUT there is a possibility: all the internal work could still be done with US formatting, but translated on the way in and out of the code. So you could input 5.000,00 and it could be string-replaced to 5,000.00, then string-replaced again after formatting.
Real i18n/l10n is never easy, which is why I deliberately avoided it :-)
Xaprb
26 Dec 06 at 4:09 pm
Any word on when this bug will be fixed,
The number -1 with “#,#.00″ format returns “-01.00″.
Othewise excellent script!!
Mike
2 Jan 07 at 12:44 pm
Great work!
vibul
2 Jan 07 at 10:51 pm
Mike — sorry, I simply don’t have time to work on this. I’m consumed with innotop. I hope someone else can fix the remaining problems and contribute the solutions back. I certainly have time to update the files if someone can submit a patch.
Xaprb
10 Jan 07 at 11:09 am
This is a great script, but I believe I found one bug with it and have a fix. If I pass a value of 11.95 and format of 0.0 for example, the result I get is 11.1 instead of 12.0. I traced this to the Number.prototype.round function and believe the section of code should read:
if (m && m.length) { var s = String.leftPad(Math.round(m[2] + "." + m[3]), decimals, "0"); return (s.length > decimals) ? new Number(m[1]) + 1 : new Number(m[1] + "." + s); }The problem in this case is that ’s’ ends up being ‘10′, so previously you would end up with 11.10. I believe the code I wrote handles this case in general (i.e format of ‘0.00′), but please check and let me know.
Thanks,
Ron
Ron MacCracken
4 Apr 07 at 1:35 pm
Thanks Ron! I’m sure you’re right. I love patches… keep sending them :-) I’ll write a test case to cover this particular problem and ensure this patch solves it.
Xaprb
5 Apr 07 at 6:59 am
That test case is added and the patch is integrated. Thanks.
Xaprb
17 Apr 07 at 8:46 pm
Thanks Xaprb. You rock.
Andrew Janssen
1 Jun 07 at 8:21 pm
A new version is released with a fix for the zero-padding of negative numbers.
If you find bugs, please send me test cases I can use to reproduce and add to the unit test suite. One test per line, like “input”, “format”, “expected” is best. For example,
-1, “#,#.00″, “-1.00″
Is a great test case. I can plug that directly into the unit tests, run it, and if it gives back “-01.00″ it will fail the test. This makes it much easier and more convenient for me to fix bugs.
Sponsoring bug fixes won’t hurt either ;-)
Xaprb
19 Jun 07 at 10:05 pm
Great script!
Jon
3 Jan 08 at 1:33 pm
Thanks very much! That was great, exacly what I needed
Livia
9 Jan 08 at 5:15 am
Hey Xaprb, I tried the following in my code:
new Number(85.68).numberFormat(‘$#,###.##’);
but it returned $86.00. I then tried the same number and format on your demo page, and it correctly displayed $85.68.
Is there a difference between the demo page code and what’s in the download? My version of number-functions.js is dated 6/15/2007.
Bruce
5 Feb 08 at 8:35 pm
Thanks for this excellent script, Xaprb. It’s solved my problem in one go.
I am having a problem, though; I want fixed-width numbers so they are aligned in columns in a textarea, so I’m using “?????.####” as the format. However, if the numbers are negative, I get one more column since the minus sign is inserted after the spaces and before the number.
I need the minus sign to take the space of one of the “?”, so the columns are aligned correctly irrespective of the sign.
Angel
2 Apr 08 at 7:45 pm
Hi, I found another bug on round function, even with Ron’s patch.
As Ron said, if you enter: “11.95″ with format “#.0″ you get “12.0″, that’s ok.
But on negative numbers, ie: “-11.95″ with format “#.0″ you get “-10.0″. That’s wrong, because the nearest number to -11.95 is -12.
So, here is my patch for this function. I recoded it from scratch, And tested a bit.
[code]
Number.prototype.round = function(decimals) {
if (decimals > 0) {
var toRound = this * (Math.pow(10, decimals));
var strToRound = (toRound.toString().indexOf(".") = 5) {
return ((parseInt(strToRound.substring(0, strToRound.indexOf(".")), 10) (this > 0 ? 1 : (-1))) / (Math.pow(10, decimals)));
} else {
return (parseInt(strToRound.substring(0, strToRound.indexOf(".")), 10) / (Math.pow(10, decimals)));
}
}
return this;
}
[/code]
I hope that helps someone!.
Matias Moncho
7 Apr 08 at 5:51 pm
Matias, something is missing in your code, before the brace ( { ) at the end of
var strToRound = (toRound.toString().indexOf(“.”) = 5)
?
Angel
8 Apr 08 at 2:57 pm
@Angel: Yes, somehow my clippboard didn’t paste the whole code.
Here I let you a link to a file white the “round” method:
http://www.clanpesto.com.ar/round.js
Regards!
Matias Moncho
8 Apr 08 at 3:57 pm
Hi,
i need a script which can fix the length of a number with given number of digits (not adding decimal point). e.g. need to have a 2 digit number so method(5) should return 05.
i need this for month and days of a date. How to do this?
shelly
22 May 08 at 8:07 am
Using : http://www.xaprb.com/articles/number-formatting-demo.html
value: 4.99547
format: #.##
result: 4.10
shouldn’t this be 5.00 ?
thanks
Heath
13 Aug 08 at 5:44 am
This project’s new home page is at http://code.google.com/p/flexible-js-formatting/
Xaprb
2 Oct 08 at 10:24 am
[...] More about library can be founded on the Google Code and on the Author page. [...]
Aleksander Å.
9 Oct 08 at 4:46 am
When I pass number as 5.01/-5.01 and formatting as #, I am getting 6/-6 instead of 5/-5
Satya
9 Dec 08 at 3:30 pm
We recently came across this great number-formatter JavaScript. We immediately jumped on to it and we are using it. However, our company requires that displaying and rounding to function in a similar way as that of MS Excel. To be more specific, we want 2.3 using the format #.#### to be formatted as 2.3 not as 2.3000. But 2.3 should be displayed as 2.3000 if the format is #.#000.
Does any body has a solution or a suggestion for this?
Thanks.
Abraham
15 Dec 08 at 4:23 pm
To correct the problem found by Heath change the old method by this new one:
Number.prototype.round = function(decimals) {
if (decimals > 0) {
var m = this.toFixed(decimals + 1).match(
new RegExp(“(-?\\d*)\.(\\d{” + decimals + “})(\\d)\\d*$”));
if (m && m.length) {
var mathRoundedValue = Math.round(m[2] + “.” + m[3]);
var paddedValue = String.leftPad(mathRoundedValue, decimals, “0″);
if (paddedValue.length > decimals) {
m[1] = ((new Number(m[1])) + 1).toString();
paddedValue = paddedValue.toString().substring(1);
}
return new Number(m[1] + “.” + paddedValue);
}
}
return this;
}
Jeferson Zanim
30 Dec 08 at 12:58 pm
THANK YOU!
TQ
16 Jan 09 at 4:19 am
Great!…realy usefull!, thanks
Felix
17 Jun 09 at 4:21 pm