[javascript] How to format numbers?

Due to the bugs found by JasperV — good points! — I have rewritten my old code. I guess I only ever used this for positive values with two decimal places.

Depending on what you are trying to achieve, you may want rounding or not, so here are two versions split across that divide.

First up, with rounding.

I've introduced the `toFixed()` method as it better handles rounding to specific decimal places accurately and is well support. It does slow things down however.

This version still detaches the decimal, but using a different method than before. The `w|0` part removes the decimal. For more information on that, this is a good answer. This then leaves the remaining integer, stores it in `k` and then subtracts it again from the original number, leaving the decimal by itself.

Also, if we're to take negative numbers into account, we need to while loop (skipping three digits) until we hit `b`. This has been calculated to be 1 when dealing with negative numbers to avoid putting something like `-,100.00`

The rest of the loop is the same as before.

``````function formatThousandsWithRounding(n, dp){
var w = n.toFixed(dp), k = w|0, b = n < 0 ? 1 : 0,
u = Math.abs(w-k), d = (''+u.toFixed(dp)).substr(2, dp),
s = ''+k, i = s.length, r = '';
while ( (i-=3) > b ) { r = ',' + s.substr(i, 3) + r; }
return s.substr(0, i + 3) + r + (d ? '.'+d: '');
};
``````

In the snippet below you can edit the numbers to test yourself.

_x000D_
_x000D_
``````function formatThousandsWithRounding(n, dp){_x000D_
var w = n.toFixed(dp), k = w|0, b = n < 0 ? 1 : 0,_x000D_
u = Math.abs(w-k), d = (''+u.toFixed(dp)).substr(2, dp),_x000D_
s = ''+k, i = s.length, r = '';_x000D_
while ( (i-=3) > b ) { r = ',' + s.substr(i, 3) + r; }_x000D_
return s.substr(0, i + 3) + r + (d ? '.'+d: '');_x000D_
};_x000D_
_x000D_
var dp;_x000D_
var createInput = function(v){_x000D_
var inp = jQuery('<input class="input" />').val(v);_x000D_
var eql = jQuery('<span>&nbsp;=&nbsp;</span>');_x000D_
var out = jQuery('<div class="output" />').css('display', 'inline-block');_x000D_
var row = jQuery('<div class="row" />');_x000D_
row.append(inp).append(eql).append(out);_x000D_
inp.keyup(function(){_x000D_
out.text(formatThousandsWithRounding(Number(inp.val()), Number(dp.val())));_x000D_
});_x000D_
inp.keyup();_x000D_
jQuery('body').append(row);_x000D_
return inp;_x000D_
};_x000D_
_x000D_
jQuery(function(){_x000D_
var numbers = [_x000D_
0, 99.999, -1000, -1000000, 1000000.42, -1000000.57, -1000000.999_x000D_
], inputs = \$();_x000D_
dp = jQuery('#dp');_x000D_
for ( var i=0; i<numbers.length; i++ ) {_x000D_
}_x000D_
dp.on('input change', function(){_x000D_
inputs.keyup();_x000D_
});_x000D_
});``````
_x000D_
``````<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>_x000D_
<input id="dp" type="range" min="0" max="5" step="1" value="2" title="number of decimal places?" />``````
_x000D_
_x000D_
_x000D_

Now the other version, without rounding.

This takes a different route and attempts to avoid mathematical calculation (as this can introduce rounding, or rounding errors). If you don't want rounding, then you are only dealing with things as a string i.e. 1000.999 converted to two decimal places will only ever be 1000.99 and not 1001.00.

This method avoids using `.split()` and `RegExp()` however, both of which are very slow in comparison. And whilst I learned something new from Michael's answer about `toLocaleString`, I also was surprised to learn that it is — by quite a way — the slowest method out of them all (at least in Firefox and Chrome; Mac OSX).

Using `lastIndexOf()` we find the possibly existent decimal point, and from there everything else is pretty much the same. Save for the padding with extra 0s where needed. This code is limited to 5 decimal places. Out of my test this was the faster method.

``````var formatThousandsNoRounding = function(n, dp){
var e = '', s = e+n, l = s.length, b = n < 0 ? 1 : 0,
i = s.lastIndexOf('.'), j = i == -1 ? l : i,
r = e, d = s.substr(j+1, dp);
while ( (j-=3) > b ) { r = ',' + s.substr(j, 3) + r; }
return s.substr(0, j + 3) + r +
(dp ? '.' + d + ( d.length < dp ?
('00000').substr(0, dp - d.length):e):e);
};
``````

_x000D_
_x000D_
``````var formatThousandsNoRounding = function(n, dp){_x000D_
var e = '', s = e+n, l = s.length, b = n < 0 ? 1 : 0,_x000D_
i = s.lastIndexOf('.'), j = i == -1 ? l : i,_x000D_
r = e, d = s.substr(j+1, dp);_x000D_
while ( (j-=3) > b ) { r = ',' + s.substr(j, 3) + r; }_x000D_
return s.substr(0, j + 3) + r + _x000D_
(dp ? '.' + d + ( d.length < dp ? _x000D_
('00000').substr(0, dp - d.length):e):e);_x000D_
};_x000D_
_x000D_
var dp;_x000D_
var createInput = function(v){_x000D_
var inp = jQuery('<input class="input" />').val(v);_x000D_
var eql = jQuery('<span>&nbsp;=&nbsp;</span>');_x000D_
var out = jQuery('<div class="output" />').css('display', 'inline-block');_x000D_
var row = jQuery('<div class="row" />');_x000D_
row.append(inp).append(eql).append(out);_x000D_
inp.keyup(function(){_x000D_
out.text(formatThousandsNoRounding(Number(inp.val()), Number(dp.val())));_x000D_
});_x000D_
inp.keyup();_x000D_
jQuery('body').append(row);_x000D_
return inp;_x000D_
};_x000D_
_x000D_
jQuery(function(){_x000D_
var numbers = [_x000D_
0, 99.999, -1000, -1000000, 1000000.42, -1000000.57, -1000000.999_x000D_
], inputs = \$();_x000D_
dp = jQuery('#dp');_x000D_
for ( var i=0; i<numbers.length; i++ ) {_x000D_
}_x000D_
dp.on('input change', function(){_x000D_
inputs.keyup();_x000D_
});_x000D_
});``````
_x000D_
``````<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>_x000D_
<input id="dp" type="range" min="0" max="5" step="1" value="2" title="number of decimal places?" />``````
_x000D_
_x000D_
_x000D_

I'll update with an in-page snippet demo shortly, but for now here is a fiddle:

https://jsfiddle.net/bv2ort0a/2/

Old Method

Why use RegExp for this? — don't use a hammer when a toothpick will do i.e. use string manipulation:

``````var formatThousands = function(n, dp){
var s = ''+(Math.floor(n)), d = n % 1, i = s.length, r = '';
while ( (i -= 3) > 0 ) { r = ',' + s.substr(i, 3) + r; }
return s.substr(0, i + 3) + r +
(d ? '.' + Math.round(d * Math.pow(10, dp || 2)) : '');
};
``````

walk through

``````formatThousands( 1000000.42 );
``````

First strip off decimal:

``````s = '1000000', d = ~ 0.42
``````

Work backwards from the end of the string:

``````',' + '000'
',' + '000' + ',000'
``````

Finalise by adding the leftover prefix and the decimal suffix (with rounding to `dp` no. decimal points):

``````'1' + ',000,000' + '.42'
``````

fiddlesticks

http://jsfiddle.net/XC3sS/