Use PHP functions in JavaScript

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
function number_format(number, decimals, dec_point, thousands_sep) {
    // Formats a number with grouped thousands  
    // 
    // version: 1001.2911
    // 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
    // *     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'
    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);
}
external links: original PHP docs | raw js source

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.

Comments

Add Comment
Use:
[CODE]
your_stuff('here');
[/CODE]
for proper code formatting
By submitting code here you are allowing us to use it in php.js hence dual licensing it under the MIT and GPL licenses

Gravatar
Brett Zamir
Jan 8th Permalink

q   @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:

1
2
3
4
56
7
8
9
1011
12
13
14
1516
17
18
19
2021
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...

Gravatar
Ronnie
Jan 7th Permalink

q  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

Gravatar
Robert
Jan 2nd Permalink

q  thank you! You just made my life 20 times easier.

Gravatar
Kevin van Zonneveld
25 Oct '09 Permalink

q  @ 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

Gravatar
Brett Zamir
11 Oct '09 Permalink

q  @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?

Gravatar
Josh
10 Oct '09 Permalink

q   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 lang="javascript"]
//beginning
var prefix = '';
if(number < 0)
prefix = '-';

//end
return prefix + s;

Gravatar
Andras
10 Oct '09 Permalink

q   This is shorter :) Feel free to change var names.

1
2
3
4
56
7
8
9
1011
12
13
14
1516
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;};

Gravatar
Jay Klehr
19 Jun '09 Permalink

q  Thanks Brett, looks good now.

Gravatar
Brett Zamir
19 Jun '09 Permalink

q  Ok, great, thanks Amir. Fixed and tested (Mozilla). (Also allowed precision 1 to work in all cases.)

Gravatar
Amir Habibi
18 Jun '09 Permalink

q   To continu with Jay's comment, if you try :

1
number_format('1.2', 2);


You'll still get '1.2' instead of '1.20'

Gravatar
Kevin van Zonneveld
18 Jun '09 Permalink

q  @ Brett Zamir: Thanks for fixing Brett!!

Gravatar
Brett Zamir
18 Jun '09 Permalink

q  Thanks for the report, Jay. Should be fixed now.

Gravatar
Jay Klehr
11 Jun '09 Permalink

q   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.

1
number_format('1.20', 2); // returns 1.2, should return 1.20


Using the above version as of this writing (906.111)

Gravatar
Brett Zamir
1 Jun '09 Permalink

q  @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 )

Gravatar
davook
25 May '09 Permalink

q   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:

1
2
3
4
5
// Original code
n.toFixed(prec);
 
// New code
toFixedFix(n, prec);


The toFixedFix function would be like this:
1
2
3
4
function toFixedFix(n,prec) {
  var k = Math.pow(10,prec);
  return (Math.round(n*k)/k).toString();
}


Any idea how improve it?

Gravatar
Kheang Hok Chin
22 May '09 Permalink

q  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?

Gravatar
Kevin van Zonneveld
15 Feb '09 Permalink

q  @ 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!

Gravatar
Rival
12 Feb '09 Permalink

q   cleanup

1
2
3
....
    var abs = Math.abs(s);
....

Gravatar
Rival
12 Feb '09 Permalink

q   - 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

1
2
3
4
56
7
8
9
1011
12
13
14
1516
17
18
19
2021
22
23
24
2526
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 == &quot;undefined&quot;) ? ',' : thousands_sep;    var dec = (typeof dec_point == &quot;undefined&quot;) ? '.' : dec_point;
 
    var s = (prec &gt; 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 &gt;= 1000) {
        _ = abs.split(/\D/);
        i = _[0].length % 3 || 3; 
        _[0] = s.slice(0,i + (n &lt; 0)) +
              _[0].slice(i).replace(/(\d{3})/g, sep+'$1');
 
        s = _.join(dec);    } else {
        s = s.replace('.', dec);
    }
 
    return s;}

Gravatar
Kevin van Zonneveld
2 Feb '09 Permalink

q  @ Diogo Resende: Confirmed. I've processed your fix. Thanks a lot!

Gravatar
Simon
2 Feb '09 Permalink

q  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.

Gravatar
Diogo Resende
2 Feb '09 Permalink

q   It's not correct for numbers below 1000. It needs an else statement like this:

1
2
3
4
5
if (abs &gt; 1000) {
...
} else {
        s = abs.replace('.', ',');
}

Gravatar
Kevin van Zonneveld
16 Jan '09 Permalink

q   @ Luke: After I changed:

1
prec = !isFinite(+prec) ? 2 : Math.abs(prec);


To

1
prec = !isFinite(+prec) ? 0 : Math.abs(prec);


..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!

Gravatar
Luke
16 Jan '09 Permalink

q  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.

Gravatar
Memiux
15 Jan '09 Permalink

q  Thanks, It's so beautiful and tiny :)

Gravatar
Cees
14 Jan '09 Permalink

q  This was so what I was looking for!!! Thanks, guys!!!

Gravatar
Mariusz
4 Nov '08 Permalink

q  I take it back, got the latest version and all looks good. Thanks to those involved with the fixes!

Gravatar
Mariusz
4 Nov '08 Permalink

q  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

Gravatar
Chad Smith
28 Aug '08 Permalink

q  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

Gravatar
Chad Smith
28 Aug '08 Permalink

q  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?

Gravatar
Kevin van Zonneveld
8 Jun '08 Permalink

q  @ Howard Yeend: Thanks for noticing. This function was revised, and that reimported a previously fixed bug. It's probably because in some countries (including my own), the dec_point and thou_sep are indeed the other way around compared to the english standard. Anyway. fixed again :)

Gravatar
Howard Yeend
6 Jun '08 Permalink

q  just a comment; aren't the default values for dec_point and thou_sep the wrong way around?

by default it formats 12345.67 as 12.345,67

shouldn't it be 12,345.67 ?

Gravatar
Kevin van Zonneveld
13 Apr '08 Permalink

q  @ Jonas Raoni: Thanks I've updated the function. I have one remark though. We generally compress code with packer & jsmin to obtain bandwidth & speed advantages, while maintaining readability so developers can more easily understand and update each others code.

Gravatar
Jonas Raoni
12 Apr '08 Permalink

q  I corrected all of the bugs that I found:
- Negative numbers;
- Starting with zero;
- Rouding (when cutting the decimal places of "0.9" it should be 1 not 0).

And here is the last version of the code:

Number.prototype.formatMoney = function(c, d, t){
var n = this, c = isNaN(c = Math.abs(c)) ? 2 : c, d = d == undefined ? "," : d, t = t == undefined ? "." : t, s = n < 0 ? "-" : "",
i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", j = (j = i.length) > 3 ? j % 3 : 0;
return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t)
+ (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
};

Gravatar
Howard Yeend
9 Apr '08 Permalink

q  yay for Allan Jensen. I just noticed the same bug he fixed. cheers dude.

Gravatar
Kevin van Zonneveld
29 Mar '08 Permalink

q  @ Allan Jensen: Wow thanks for fixing it!

Gravatar
Allan Jensen
28 Mar '08 Permalink

q   Fixed the problem with negative numbers myself. Function needs to look like this:

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
function number_format( number, decimals, dec_point, thousands_sep ) {
    // http://kevin.vanzonneveld.net
    // +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +     bugfix by: Michael White (http://crestidg.com)    // +     bugfix by: Benjamin Lupton
        // +     bugfix by: Allan Jensen (http://www.winternet.no) (fixed formatting negative numbers)
    // *     example 1: number_format(1234.5678, 2, '.', '');
    // *     returns 1: 1234.57
     var i, j, kw, kd, km;
        var neg = &quot;&quot;;
 
        // Input sanitation &amp; defaults
        if ( isNaN(decimals = Math.abs(decimals)) ) {                decimals = 2;
        }
        if (dec_point == undefined) {
                dec_point = &quot;.&quot;;
        }        if (thousands_sep == undefined) {
                thousands_sep = &quot;,&quot;;
        }
 
        i = parseInt(number = (+number || 0).toFixed(decimals)) + &quot;&quot;;        if (i.substr(0,1) == &quot;-&quot;) {
                number = Math.abs(number);
                neg = &quot;-&quot;;
                i = i.substr(1);
        } 
        if ((j = i.length) &gt; 3 ) {
                j = j % 3;
        } else {
                j = 0;        }
 
        km = (j ? i.substr(0, j) + thousands_sep : &quot;&quot;);
        kw = i.substr(j).replace(/(\d{3})(?=\d)/g, &quot;$1&quot; + thousands_sep);
        //kd = (decimals ? dec_point + Math.abs(number - i).toFixed(decimals).slice(2) : &quot;&quot;);        kd = (decimals ? dec_point + Math.abs(number - i).toFixed(decimals).replace(/-/, 0).slice(2) : &quot;&quot;);
        return neg + km + kw + kd;
}

Gravatar
Allan Jensen
28 Mar '08 Permalink

q  The function has a problem with negative numbers.

This:

number_format(-258.75, 2, ",", ".")

incorrectly gives:

-,258,75

Please help me fix this.

Gravatar
Kevin van Zonneveld
20 Mar '08 Permalink

q  @ Benjamin Lupton: Thank you!

Gravatar
Benjamin "balupton" Lupton
20 Mar '08 Permalink

q  dec_point and thousands_sep defaults should be the other way round

Gravatar
Michael White
1 Mar '08 Permalink

q  No problem. I love having these familiar PHP functions available. I have a sort of proposal for you, maybe you could contact me via email (michael -a-t- crestidg.com) so I can get you some more details. It probably wouldn't take too much time for us to do and it would benefit those who want to use the entire PHP.js package along side other JavaScript quite a lot.

Gravatar
Kevin van Zonneveld
1 Mar '08 Permalink

q  @ Michael White: It sure did, thanks for taking the time to supply better code. I've updated the function!

Gravatar
Michael White
1 Mar '08 Permalink

q   There is a bug in Safari - at least in Safari 3.0.4 that causes the combination of Math.abs(0).toFixed(x) where x seems to be any positive integer to provide erroneous output.

The sample code below contains problematic code at the top and a sample solution line at the end. The new code for the number_format() function actually is in a separate code block below this one.

1
2
3
4
56
7
8
9
1011
12
13
14
1516
17
var price = 0.00;
alert(Math.abs(price)); // Alerts: 0 - correct.
        
// Alerts: 0.00 in most browsers.
// In Safari 3.0.4 you get: 0.-0alert(Math.abs(price).toFixed(2));
        
// However - If you skip the use of Math.abs() on a 0 value then the toFixed() function works correctly.
var new_price = 0;
alert(new_price.toFixed(2));        
// Temp-fix ( I submitted this bug to Apple)
var num = 0;
var result = Math.abs(0).toFixed(1);
alert(result);result = result.replace(/-/, 0);
alert(result);


Since this problem only occurs when working on a value of 0 (zero) to begin with we know that the hyphen character should be a 0 so we can simply replace it with a 0 if it is found.

1
2
3
4
56
7
8
// The easiest way to fix this is to simply modify the line that reads:
kd = (decimals ? dec_point + Math.abs(number - i).toFixed(decimals).slice(2) : &quot;&quot;);
 
// with the following line of code:
kd = (decimals ? dec_point + Math.abs(number - i).toFixed(decimals).replace(/-/, 0).slice(2) : &quot;&quot;); 
// The only difference is the addition of .replace(/-/, 0) just before the slice() method is used.
// This tiny extra amount of processing should not be significant unless number_format() is run in very tight loops - and even then it can only be considered extra in browsers like IE, Opera, and Firefox since this appears to be a Safari only bug. Checking the browser seemed to be overkill in this instance.


I hope it helps!

Michael White - 2008
michael -a-t- crestidg.com
http://crestidg.com/


Contribute a New function