JavaScript number_format
Formats a number with grouped thousands
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 3536 37 38 39 4041 42 43 44 4546 47 48 49 5051 52 53 54 5556 57 58 59 6061 62 63 64 6566 67 68 69 7071 72 73 | function number_format (number, decimals, dec_point, thousands_sep) { // Formats a number with grouped thousands // // version: 1109.2015 // discuss at: http://phpjs.org/functions/number_format // + original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com) // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // + bugfix by: Michael White (http://getsprink.com) // + bugfix by: Benjamin Lupton // + bugfix by: Allan Jensen (http://www.winternet.no) // + revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com) // + bugfix by: Howard Yeend // + revised by: Luke Smith (http://lucassmith.name) // + bugfix by: Diogo Resende // + bugfix by: Rival // + input by: Kheang Hok Chin (http://www.distantia.ca/) // + improved by: davook // + improved by: Brett Zamir (http://brett-zamir.me) // + input by: Jay Klehr // + improved by: Brett Zamir (http://brett-zamir.me) // + input by: Amir Habibi (http://www.residence-mixte.com/) // + bugfix by: Brett Zamir (http://brett-zamir.me) // + improved by: Theriault // + input by: Amirouche // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // * example 1: number_format(1234.56); // * returns 1: '1,235' // * example 2: number_format(1234.56, 2, ',', ' '); // * returns 2: '1 234,56' // * example 3: number_format(1234.5678, 2, '.', ''); // * returns 3: '1234.57' // * example 4: number_format(67, 2, ',', '.'); // * returns 4: '67,00' // * example 5: number_format(1000); // * returns 5: '1,000' // * example 6: number_format(67.311, 2); // * returns 6: '67.31' // * example 7: number_format(1000.55, 1); // * returns 7: '1,000.6' // * example 8: number_format(67000, 5, ',', '.'); // * returns 8: '67.000,00000' // * example 9: number_format(0.9, 0); // * returns 9: '1' // * example 10: number_format('1.20', 2); // * returns 10: '1.20' // * example 11: number_format('1.20', 4); // * returns 11: '1.2000' // * example 12: number_format('1.2000', 3); // * returns 12: '1.200' // * example 13: number_format('1 000,50', 2, '.', ' '); // * returns 13: '100 050.00' // Strip all characters but numerical ones. number = (number + '').replace(/[^0-9+\-Ee.]/g, ''); var n = !isFinite(+number) ? 0 : +number, prec = !isFinite(+decimals) ? 0 : Math.abs(decimals), sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep, dec = (typeof dec_point === 'undefined') ? '.' : dec_point, s = '', toFixedFix = function (n, prec) { var k = Math.pow(10, prec); return '' + Math.round(n * k) / k; }; // Fix for IE parseFloat(0.55).toFixed(0) = 0; s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.'); if (s[0].length > 3) { s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep); } if ((s[1] || '').length < prec) { s[1] = s[1] || ''; s[1] += new Array(prec - s[1].length + 1).join('0'); } return s.join(dec); } |
Examples
» Example 1
Running
1 | number_format(1234.56); |
Should return
1 | '1,235' |
» Example 2
Running
1 | number_format(1234.56, 2, ',', ' '); |
Should return
1 | '1 234,56' |
Dependencies
No dependencies, you can use this function standalone.
Open syntax issues
php.js uses JsLint to help us keep our code consistent and prevent some common bugs.
Eventually we want all code to pass or at least take into consideration most fixes suggested by JsLint, following this JsLint configuration we’ve decided on.
Authors
Thanks to the following developers, you get to have number_format goodness in JavaScript.
Sorry, my fix did not work, it should be
s = (prec ? toFixedFix(Math.abs(n), prec) : '' + Math.round(Math.abs(n))).split('.');
and
return (n<0 ? '-' : '')+s.join(dec);
number_format(-5.5) returns -6 in PHP (because PHP rounds half numbers up = away from zero, while JavaScript rounds them always up). Hence I suggest changing line 66 to
s = ((n<0 ? '-' : '') + (prec ? toFixedFix(Math.abs(n), prec) : '' + Math.round(Math.abs(n)))).split('.');
and adding example 14:
// * example 14: number_format('-5.5');
// * returns 14: '-6'
@WoofWoof: This bug should now be fixed. The new source is available on GitHub, or https://github.com/kvz/phpjs/raw/master/functions/strings/number_format.js
@WoofWoof: It does look like a bug. I'm a little busy at the moment unless someone else can help, but in the meantime, you might try money_format() as it can handle larger numbers.
Nice one!
But I have problem when there's 1,000,000.00... it'll return 0, it is unable to handle such a big sum? only 1000,000.00? I'm relying on this to format $$$... but when dealing with Indonesian Rupiah... which usually of very huge sum.
OK, I´m wrong, because, in PHP, the result gives us 555,42 to in number_format(555.425, 2, ',', '.').
I was looking a solution to round like excel.
Anyway my "solution" works fine for "excel´s round".
Greets.
One posible solution. Add
var ajuste = n + 1/(k*10);
and replace the return value for this:
return '' + Math.round(ajuste * k) / k;
in the function toFixedFix declared over the line 50, leave it something like that:
toFixedFix = function (n, prec) {
var k = Math.pow(10, prec);
var ajuste = n + 1/(k*10);
return '' + Math.round(ajuste * k) / k;
};
Any suggestion??
Hi guys,
I think that I found a bug because I get 555,42 in that function number_format(555.425, 2, ',', '.') when the result could be 555,43
Any solution?
Thanks.
Sorry for my english
@Ronnie: We're really just focused here on implementing the already-existing API of PHP, and PHP doesn't have any formal way of converting back to a numeric format. Why don't you just transmit both forms together within your application? Or are you depending on user input which may include use of commas, etc.?
If the latter, you might be able to use this:
function strtonumber( str, dec_point, thousands_sep ) {
// + based on: http://www.php.net/manual/en/function.number-format.php#76448
// + adapted by: Brett Zamir (http://brett-zamir.me)
// - depends on: localeconv
// - depends on: str_replace
// * example 1: strtonumber('1,123.564', '.', ',');
// * returns 1: 1123.564
// * example 2: strtonumber('1,123', '.', ',')
// * returns 2: 1123
if(typeof dec_point === 'undefined' || typeof thousands_sep === 'undefined') {
var locale = this.localeconv();
if(typeof dec_point === 'undefined') {
dec_point = locale['decimal_point'];
}
if(typeof thousands_sep === 'undefined') {
thousands_sep = locale['thousands_sep'];
}
}
return parseFloat(this.str_replace(dec_point, '.', this.str_replace(thousands_sep, '', str)));
}
I was able to avoid the extra code used in the original (at the end) since JavaScript doesn't represent integers differently from floats as variable types.
Be sure to add the dependencies and also note that localeconv() also has its own dependency of setlocale() which you also will need, at least if you may rely on defaults.
But consider that the default locale set up by setlocale() was based on the default locale I obtained in my own testing which did not include a thousands separator (as one might expect the default to be ',' as English). So, unless you adapt the locale(s) inside setlocale(), you have to specify all of the arguments for strtonumber.
Hope that helps...
I like it, but now I have to calculation on it, so I need to format the number back to a numeric format for example
from
1,000,235.22
to
1000235.22
@ Josh: as you can see this function has been revised 2 times. That means a complete rewrite. I guess what you could blame us for is regression. More specifically: not having a test case for the negative numbers.
But hey, there are over 400 functions we have to maintain and we do all of this for free so please take it or by all means - leave it.
@ Brett Zamir: I chose not to have an incremental version for each function because I felt that could easily lead to inconsistency. Instead it's the shortest form of the modification date that still makes sense.
e.g.: 906.1806 would translate to 20090618 06:00:00
@Josh: Although I don't know what Kevin's system of numbering is about exactly, I'm pretty darn sure it wasn't 900 revisions! Please keep in mind that these functions have been posted here by different people, often with submissions which started as mere approximations of the PHP function and over time have had a few bugs fixed, some more PHP behaviors added, etc. Not all of us have the time or experience to review each function in full detail, compare it with PHP behavior and its idiosyncrasies, etc. (and functions earlier on were accepted with more loose standards than now). A lot of functions were added in good faith. We're volunteers just like you are in submitting the patch, only Kevin has done the added work of making a website, a compiler, etc., just in the hopes of it being useful. So, take it or leave it for what it's worth. We'd all love to see a comprehensive review and have the functions put through all of the test cases as used in the PHP source code, but we're not there yet. I think maybe the Duct Tape Programmer philosophy applies to our project: http://www.joelonsoftware.com/items/2009/09/23.html :)
@Andras, Thanks very much for the function. Before Kevin or I get a chance to look at it, could you be sure that it follows the PHP behavior at least as well as the one we have now--passing test cases, etc. and addressing Josh's issue?
Thank you for the function, however it boggles my mind that you can go through 906 revisions and still can't get negative numbers right (at least in Firefox 3.5.3 or IE 8). Negative numbers are numbers that are less than 0, and yes, they do come into play occasionally in this world. I added a couple changes at the very beginning and end of the function to account for the problem:
[CODE]
//beginning
var prefix = '';
if(number < 0)
prefix = '-';
//end
return prefix + s;
This is shorter :) Feel free to change var names.
function number_format(amount, nDec, sDec, sTho){
if (!sDec) sDec = ',';
if (!sTho) sTho = '.';
amount = (amount * 1).toFixed(nDec);
if (isNaN(amount)) return NaN;
amountExp = (amount + '').split('.');
amount = amountExp[0];
var rgx = /(\d+)(\d{3})/;
while (rgx.test(amount)) {
amount = amount.replace(rgx,'$1' + sTho + '$2');
};
if (nDec > 0){
amount += sDec + amountExp[1];
};
return amount;
};
Ok, great, thanks Amir. Fixed and tested (Mozilla). (Also allowed precision 1 to work in all cases.)
To continu with Jay's comment, if you try :
number_format('1.2', 2);
You'll still get '1.2' instead of '1.20'
When using number_format to format numbers used for currency, I noticed that if the number has a single decimal followed by a zero initially, and I apply number_format to it (with 2 decimal precision) I don't get 2 decimals. It strips the trailing zero.
number_format('1.20', 2); // returns 1.2, should return 1.20
Using the above version as of this writing (906.111)
@Kheang Hok Chin, thanks for the report and @davook, thanks for the workaround! I've incorporated the workaround (and added some more test cases), and also fixed a problem when the final value was an absolute number and was thus not getting zeroes added. (FYI, the referenced discussion thread was missing a digit at the end: http://forums.mozillazine.org/viewtopic.php?f=9&t=999945 )
Related to the last comment, i've the same problem.
It seems that "toFixed()" is the problem:
http://forums.mozillazine.org/viewtopic.php?f=9&t=99994
As a temporary, bad and ugly solution i'm using an auxiliar function replacing toFixed().
Example:
// Original code n.toFixed(prec); // New code toFixedFix(n, prec);
The toFixedFix function would be like this:
function toFixedFix(n,prec) {
var k = Math.pow(10,prec);
return (Math.round(n*k)/k).toString();
}
Any idea how improve it?
Hi, I have a question in regard to this comment:
- bugfix by: Rival
note 1: For 1000.55 result with precision 1 in FF/Opera is 1,000.5, but in IE is 1,000.6
Has this been fixed or does this mean that if a user using Firefox, Opera, Safari will always get the result 1000.5 when formatting the number 1000.55 to precision 1?
While in IE it returns 1000.6 which is correct as number_format in PHP works the same way.
Is there a fix for this?
@ Rival: Hey there, excellent work Rival! I've also added 2 test cases that cover your findings so we can't make that mistake again. Thanks!
- show thousand separator for number=1000
- decimal point for numbers lower than 1000 is undefined
note: for 1000.55 result with precision 1 in FF/Opera is 1,000.5, but in IE is 1,000.6
[CODE="Javascript"]
function number_format(number, decimals, dec_point, thousands_sep) {
var n = number, prec = decimals;
n = !isFinite(+n) ? 0 : +n;
prec = !isFinite(+prec) ? 0 : Math.abs(prec);
var sep = (typeof thousands_sep == "undefined") ? ',' : thousands_sep;
var dec = (typeof dec_point == "undefined") ? '.' : dec_point;
var s = (prec > 0) ? n.toFixed(prec) : Math.round(n).toFixed(prec); //fix for IE parseFloat(0.55).toFixed(0) = 0;
var abs = Math.abs(n).toFixed(prec);
var _, i;
if (abs >= 1000) {
_ = abs.split(/\D/);
i = _[0].length % 3 || 3;
_[0] = s.slice(0,i + (n < 0)) +
_[0].slice(i).replace(/(\d{3})/g, sep+'$1');
s = _.join(dec);
} else {
s = s.replace('.', dec);
}
return s;
}
[/CODE]
This does not work as is on IE6 and IE7, very simple to fix.
search
is not defined and ie throws an error.
so just add
var search;
to the top of the script.
It's not correct for numbers below 1000. It needs an else statement like this:
[CODE="Javascript"]
if (abs > 1000) {
...
} else {
s = abs.replace('.', ',');
}
[/CODE]
@ Luke: After I changed:
[CODE="Javascript"]
prec = !isFinite(+prec) ? 2 : Math.abs(prec);
[/CODE]
To
[CODE="Javascript"]
prec = !isFinite(+prec) ? 0 : Math.abs(prec);
[/CODE]
..All test cases from the php manual worked perfectly. And since your code is a bit easier on the eyes as well, I'll include it in our project. Thanks a lot!
I attacked the same problem recently and came up with a similar solution
http://gist.github.com/47871
more lines, but fewer characters, and compresses (via YUI Compressor) to about 83% of the version above. Take a look; there may be improvements to be made.
This method does not seem to work correctly with negative numbers, for example (to 2 decimal places):
-16038 returns: -160,38.00
-820 returns -,820.00
Ignore my previous comment, I'm an idiot. I was checking to see if it was larger than 1000 before it ran through the function. Duh, sorry.
Love the script!
Chad
Anyone else notice that if you just put in 1000 it doesn't format the number to 1,000? But if you put in 1001 it formats correctly to 1,001.
Any idea what could make that happen?


Nima
Jan 7th