Categories
currency formatting javascript

How to format numbers as currency strings

2308

I would like to format a price in JavaScript. I’d like a function which takes a float as an argument and returns a string formatted like this:

"$ 2,500.00"

What’s the best way to do this?

18

  • 609

    Please, to anyone reading this in the future, do not use float to store currency. You will loose precision and data. You should store it as a integer number of cents (or pennies etc.) and then convert prior to output.

    Mar 4, 2012 at 13:35

  • 12

    @user1308743 Float doesn’t store decimal places. It stores numbers using a value, base and offset. 0.01 is not actually representable. See: en.wikipedia.org/wiki/Floating_point#Accuracy_problems

    Jun 10, 2012 at 11:11

  • 7

    @user1308743: Imagine you represent a very big number (lets say you are a lucky guy and it is your bank account balance). Would you really want to loose money because of a precision deficiency ?

    – ereOn

    Aug 6, 2012 at 9:14

  • 195

    So why hasn’t anyone suggested the following? (2500).toLocaleString(“en-GB”, {style: “currency”, currency: “GBP”, minimumFractionDigits: 2}) developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…

    Sep 25, 2013 at 1:41

  • 5

    you can use this Numeral.js javascript library to convert your numbers to dollars. (numeraljs.com) for reference.

    Sep 11, 2014 at 10:30


30

Ok, based on what you said, I’m using this:

var DecimalSeparator = Number("1.2").toLocaleString().substr(1,1);

var AmountWithCommas = Amount.toLocaleString();
var arParts = String(AmountWithCommas).split(DecimalSeparator);
var intPart = arParts[0];
var decPart = (arParts.length > 1 ? arParts[1] : '');
decPart = (decPart + '00').substr(0,2);

return '£ ' + intPart + DecimalSeparator + decPart;

I’m open to improvement suggestions (I’d prefer not to include YUI just to do this 🙂 )

I already know I should be detecting the “.” instead of just using it as the decimal separator…

2

  • 8

    Note that your version doesn’t properly round to two decimal digits. For example, 3.706 would be formatted as “£ 3.70”, not as “£ 3.71” as it’s supposed to be.

    Sep 30, 2008 at 23:33

  • Yes, that’s OK in my particular case, since the amounts I’m working with already have at most 2 digits The reason I need to fix to 2 decimals is for amounts with no decimals or with only 1.

    Oct 2, 2008 at 19:12

1940

Number.prototype.toFixed

This solution is compatible with every single major browser:

  const profits = 2489.8237;

  profits.toFixed(3) // Returns 2489.824 (rounds up)
  profits.toFixed(2) // Returns 2489.82
  profits.toFixed(7) // Returns 2489.8237000 (pads the decimals)

All you need is to add the currency symbol (e.g. "$" + profits.toFixed(2)) and you will have your amount in dollars.

Custom function

If you require the use of , between each digit, you can use this function:

function formatMoney(number, decPlaces, decSep, thouSep) {
    decPlaces = isNaN(decPlaces = Math.abs(decPlaces)) ? 2 : decPlaces,
    decSep = typeof decSep === "undefined" ? "." : decSep;
    thouSep = typeof thouSep === "undefined" ? "," : thouSep;
    var sign = number < 0 ? "-" : "";
    var i = String(parseInt(number = Math.abs(Number(number) || 0).toFixed(decPlaces)));
    var j = (j = i.length) > 3 ? j % 3 : 0;

    return sign +
        (j ? i.substr(0, j) + thouSep : "") +
        i.substr(j).replace(/(\decSep{3})(?=\decSep)/g, "$1" + thouSep) +
        (decPlaces ? decSep + Math.abs(number - i).toFixed(decPlaces).slice(2) : "");
}

document.getElementById("b").addEventListener("click", event => {
  document.getElementById("x").innerText = "Result was: " + formatMoney(document.getElementById("d").value);
});
<label>Insert your amount: <input id="d" type="text" placeholder="Cash amount" /></label>
<br />
<button id="b">Get Output</button>
<p id="x">(press button to get output)</p>

Use it like so:

(123456789.12345).formatMoney(2, ".", ",");

If you’re always going to use ‘.’ and ‘,’, you can leave them off your method call, and the method will default them for you.

(123456789.12345).formatMoney(2);

If your culture has the two symbols flipped (i.e., Europeans) and you would like to use the defaults, just paste over the following two lines in the formatMoney method:

    d = d == undefined ? "," : d,
    t = t == undefined ? "." : t,

Custom function (ES6)

If you can use modern ECMAScript syntax (i.e., through Babel), you can use this simpler function instead:

function formatMoney(amount, decimalCount = 2, decimal = ".", thousands = ",") {
  try {
    decimalCount = Math.abs(decimalCount);
    decimalCount = isNaN(decimalCount) ? 2 : decimalCount;

    const negativeSign = amount < 0 ? "-" : "";

    let i = parseInt(amount = Math.abs(Number(amount) || 0).toFixed(decimalCount)).toString();
    let j = (i.length > 3) ? i.length % 3 : 0;

    return
      negativeSign +
      (j ? i.substr(0, j) + thousands : '') +
      i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thousands) +
      (decimalCount ? decimal + Math.abs(amount - i).toFixed(decimalCount).slice(2) : "");
  } catch (e) {
    console.log(e)
  }
};

document.getElementById("b").addEventListener("click", event => {
  document.getElementById("x").innerText = "Result was: " + formatMoney(document.getElementById("d").value);
});
<label>Insert your amount: <input id="d" type="text" placeholder="Cash amount" /></label>
<br />
<button id="b">Get Output</button>
<p id="x">(press button to get output)</p>

42

  • 28

    first of all, excellent, concise code. however, if you are american, you should change the defaults of d and t to be . and , respectively so that you don’t have to specify them every time. also, i recommend modifying the beginning of the return statement to read: return s + '$' + [rest], otherwise you will not get a dollar sign.

    – Jason

    Jan 31, 2011 at 23:58

  • 792

    Not sure why people think this code is beautiful. It is indecipherable. It seems to work nicely, but it is not beautiful.

    – usr

    Oct 24, 2012 at 16:28

  • 90

    Is this formatMoney function copied from some minified JavaScript code somewhere? Can you not post the original? What do the variables c, d, i, j, n, s, and t stand for? Judging by the amount of upvotes and comments this post has I can assume this code has been copy pasted into production websites everywhere… Good luck maintaining the code if it has a bug some day!

    – zuallauz

    Dec 17, 2012 at 20:41

  • 268

    “poetry”? More like obscurity. This isn’t code golf; use a little white space. Proper var names wouldn’t hurt, either.

    Dec 30, 2012 at 14:07

  • 48

1466

Short and fast solution (works everywhere!)

(12345.67).toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,');  // 12,345.67

The idea behind this solution is replacing matched sections with first match and comma, i.e. '$&,'. The matching is done using lookahead approach. You may read the expression as “match a number if it is followed by a sequence of three number sets (one or more) and a dot”.

TESTS:

1        --> "1.00"
12       --> "12.00"
123      --> "123.00"
1234     --> "1,234.00"
12345    --> "12,345.00"
123456   --> "123,456.00"
1234567  --> "1,234,567.00"
12345.67 --> "12,345.67"

DEMO: http://jsfiddle.net/hAfMM/9571/


Extended short solution

You can also extend the prototype of Number object to add additional support of any number of decimals [0 .. n] and the size of number groups [0 .. x]:

/**
 * Number.prototype.format(n, x)
 * 
 * @param integer n: length of decimal
 * @param integer x: length of sections
 */
Number.prototype.format = function(n, x) {
    var re="\\d(?=(\\d{" + (x || 3) + '})+' + (n > 0 ? '\\.' : '$') + ')';
    return this.toFixed(Math.max(0, ~~n)).replace(new RegExp(re, 'g'), '$&,');
};

1234..format();           // "1,234"
12345..format(2);         // "12,345.00"
123456.7.format(3, 2);    // "12,34,56.700"
123456.789.format(2, 4);  // "12,3456.79"

DEMO / TESTS: http://jsfiddle.net/hAfMM/435/


Super extended short solution

In this super extended version you may set different delimiter types:

/**
 * Number.prototype.format(n, x, s, c)
 * 
 * @param integer n: length of decimal
 * @param integer x: length of whole part
 * @param mixed   s: sections delimiter
 * @param mixed   c: decimal delimiter
 */
Number.prototype.format = function(n, x, s, c) {
    var re="\\d(?=(\\d{" + (x || 3) + '})+' + (n > 0 ? '\\D' : '$') + ')',
        num = this.toFixed(Math.max(0, ~~n));

    return (c ? num.replace('.', c) : num).replace(new RegExp(re, 'g'), '$&' + (s || ','));
};

12345678.9.format(2, 3, '.', ',');  // "12.345.678,90"
123456.789.format(4, 4, ' ', ':');  // "12 3456:7890"
12345678.9.format(0, 3, '-');       // "12-345-679"

DEMO / TESTS: http://jsfiddle.net/hAfMM/612/

33

  • 22

    I actually went a step further: .replace(/(\d)(?=(\d{3})+(?:\.\d+)?$)/g, "$1,").

    Mar 21, 2013 at 2:50

  • 4

    CoffeeScript version with of VisioN & kalisjoshua regexp and way of specifying decimal place (so you can leave the default of 2 or specify 0 for no decimal): Number.prototype.toMoney = (decimal=2) -> @toFixed(decimal).replace /(\d)(?=(\d{3})+(?:\.\d+)?$)/g, "$1,"

    Jun 18, 2013 at 15:43

  • 11

    @Abbas Yeah, replace \. with $ (end of line), i.e. this.toFixed(0).replace(/(\d)(?=(\d{3})+$)/g, "$1,").

    – VisioN

    Aug 15, 2013 at 9:26

  • 2

    @hanumant The regular grammar is a bit complicated here, so I suggest you to read the manuals about regular expressions first (e.g. at MDN). The idea behind it is replacing matched sections with first match and comma, i.e. $1,. The matching is done using lookahead approach. You may read the expression as “match a number if it is followed by a sequence of three number sets (one or more) and a dot”.

    – VisioN

    Oct 22, 2013 at 15:08

  • 2

    @JuliendePrabère Please give an example of a long number which doesn’t work with this approach.

    – VisioN

    Mar 25, 2014 at 10:53