265

Am I missing something here?

var someNumber = 123.456;
someNumber = someNumber.toFixed(2);
alert(typeof(someNumber));
//alerts string

Why does .toFixed() return a string?

I want to round the number to 2 decimal digits.

Cheeso
  • 189,189
  • 101
  • 473
  • 713
Derek Adair
  • 21,846
  • 31
  • 97
  • 134

16 Answers16

255

Number.prototype.toFixed is a function designed to format a number before printing it out. It's from the family of toString, toExponential and toPrecision.

To round a number, you would do this:

someNumber = 42.008;
someNumber = Math.round( someNumber * 1e2 ) / 1e2;
someNumber === 42.01;

// if you need 3 digits, replace 1e2 with 1e3 etc.
// or just copypaste this function to your code:

function toFixedNumber(num, digits, base){
  const pow = Math.pow(base ?? 10, digits);
  return Math.round(num*pow) / pow;
}

Why isn't this function included in the JavaScript's standard library? Because floating point numbers are hard! Many decimal numbers cannot be directly represented by base-two floats – that's why 0.09 + 0.01 !== 0.1 but 0.09999999999999999. If developers were given a tool which is supposed to round floats to decimal places, they'd (wrongly) assume that it really returns a decimal number, when it in fact returns the closest representable double-precision base-two float.

m93a
  • 8,866
  • 9
  • 40
  • 58
  • 14
    I think this is the best answer. It avoids type conversion. Awesome-sauce! – Phil Nov 08 '15 at 03:39
  • 1
    Great answer! However... I've been doing JavaScript like 20 years or so, but I can't figure out why you're using that +(...) construction around the return value? Thanks @sam for rubbing it in :) As I'm never too old to learn, please elaborate :-) – HammerNL Jan 19 '17 at 13:00
  • 3
    @HammerNL Despite Sam's conviction, it acutally doesn't do anything :) It's just a practise – it makes IDEs recognize this function as `type Number`. The thing is that `+(anyValue)` always returns a number – eg. `+("45")` returns `45`, `+(new Number(42))` returns `42`. It's kinda like strong-typing the function. If you make a habit of it, you can avoid a lot of bugs :) – m93a Jan 21 '17 at 12:57
  • Why is this not baked into core javascript :s – webmaster Sep 09 '17 at 16:21
  • your method just doesn't work good with little numbers, It requires you to have extra attention in the possible number of decimal 0s. Do not make it personal! https://jsfiddle.net/32r5581p/3/ – Evhz Dec 20 '17 at 14:40
  • 5
    The resuit of `someNumber = Math.round( 42.008 * 1e2 ) / 1e2;` is not `42.01`, it is `~42.0099999999999980`. Reason: The number `42.01` does not exists and is rounded to the nearest existing number. btw, proof numbers by `toPrecision(18)` to print it with all relevant digits. – Wiimm May 10 '19 at 15:02
160

I've solved this problem by changing this:

someNumber = someNumber.toFixed(2)

...to this:

someNumber = +someNumber.toFixed(2);

However this will convert the number to a string and parse it again, which will have a significant impact on performance. If you care about performance or type safety, check the the other answers as well.

m93a
  • 8,866
  • 9
  • 40
  • 58
Eve juan
  • 1,781
  • 1
  • 10
  • 4
  • 57
    No, no, no, no, no! **Don't** do that! Number to string conversion just to round it is a **very bad practise**! Instead do `someNumber = Math.round(someNumber * 1e2) / 1e2`! See my answer for a more generalized way. – m93a Apr 07 '15 at 14:39
  • @m93a - why is it such bad practice? – jczaplew Nov 29 '16 at 21:17
  • 3
    @jczaplew Because if you do it this way, the 32bit binary number is converted into a string, using 16bits for every god-damn decimal **digit**! (By the way storing numbers in UTF-16 isn't the most convenient thing you'd go for.) And than the string is converted back to a 32bit floating-point number. Digit by digit. (If I ignore all the tests that must be done before to chose the right parsing algorithm.) And all that in vain considering you can do it using 3 fast operations on the float. – m93a Dec 05 '16 at 14:04
  • 3
    @m93a so it's for performance reasons? does this actually have a noticeable impact in performance? – Sebastianb Jan 12 '17 at 15:11
  • @Sebastianb quite a lot actually https://jsperf.com/rounding-a-number-2 – m93a Jan 12 '17 at 21:05
  • Also you can use the unary plus operator to convert the string returned by the toFixed function to a number `someNumber = +(someNumber.toFixed(2));` – Gaizka Allende Mar 27 '17 at 16:29
  • 2
    @jczaplew, Because Strings are (1) practically slow and (2) theoretically incorrect. – Pacerier Apr 17 '17 at 16:10
  • 4
    `Math.round(someNumber * 1e2) / 1e2` is very easy to remember. JavaScript is such fun – Piotr Kula Jan 23 '20 at 21:56
  • 1
    This round method does not work so well. Rounding 1.50 to the nearest .05 for example yields 0.07500000000000001, which prints just like that. – den232 Feb 20 '20 at 18:45
147

It returns a string because 0.1, and powers thereof (which are used to display decimal fractions), are not representable (at least not with full accuracy) in binary floating-point systems.

For example, 0.1 is really 0.1000000000000000055511151231257827021181583404541015625, and 0.01 is really 0.01000000000000000020816681711721685132943093776702880859375. (Thanks to BigDecimal for proving my point. :-P)

Therefore (absent a decimal floating point or rational number type), outputting it as a string is the only way to get it trimmed to exactly the precision required for display.

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • 46
    at least javascript could save me some finger-work and convert it back to a number... sheesh... – Derek Adair Feb 17 '10 at 19:20
  • 13
    @Derek: Yeah, but once you convert it back into a number, you run into the same inaccuracy issues again. :-P JS doesn't have decimal floating point or rational numbers. – C. K. Young Feb 17 '10 at 19:21
  • 1
    @DerekAdair I've recently written a post that explains this even further, that you may be interested in. Enjoy! http://stackoverflow.com/a/27030789/13 – C. K. Young Nov 26 '14 at 04:38
  • 7
    Actually this caused me to do some pretty heavy research into this subject! thanks for all your help! – Derek Adair Nov 28 '14 at 22:07
  • 2
    Your answer is slightly misleading: `toFixed` is a formatting function, which has a sole purpose of converting a number to a string, formatting it using the specified number of decimals. The reason it returns a string is because it's supposed to return a string, and if it was named `toStringFixed` instead, OP wouldn't be surprised at the results. The only issue here is that OP expected it to work like `Math.round`, without consulting JS reference. – vgru Apr 28 '15 at 14:04
  • How 0.1 be 0.100000...55511... ? because 0.1 in binary = 0.0001100110011... and convert it to 10 base than 0.09999999999999987 in 64 bit computer. – JillAndMe Apr 16 '21 at 05:52
  • @ChrisJester-Young - but wouldn't it be useful, for example, if we're sending json data to an api, to send it as a number like `{ "amount": 5.95 }` instead of a string like `{ "amount": "5.95" }`? – jbyrd Nov 26 '21 at 21:42
  • 1
    @jbyrd It would be, except that 5.95 doesn't exist. What that gets parsed into, instead, is 5.95000000000000017763568394002504646778106689453125. If you're willing to deal with that kind of difference, cool. Just know that an API cannot actually send 5.95 exactly, at least not as a JSON number. – C. K. Young Nov 27 '21 at 05:40
  • @JillAndMe Which floating-point format are you using? I'm using IEEE 754's `binary64` format, known as `double` in many programming languages. In that format, the numbers closest to 0.1 are `0.09999999999999999167332731531132594682276248931884765625` and `0.1000000000000000055511151231257827021181583404541015625`, and the latter is closer to 0.1 than the former, so `0.1` would parse to the latter. – C. K. Young Nov 27 '21 at 05:50
  • @ChrisJester-Young - I see what you mean. I wonder how financial software handles this typically? – jbyrd Nov 27 '21 at 21:10
  • 1
    @jbyrd Financial software have two options: 1. decimal floating-point types, if supported by the language in use, and 2. using an integer to specify the number of cents (or even hundredths of cents). In the case of option 1, note that JSON doesn't have a decimal floating-point type, so it still has to be transmitted as a string. – C. K. Young Nov 30 '21 at 04:24
41

Why not use parseFloat?

var someNumber = 123.456;
someNumber = parseFloat(someNumber.toFixed(2));
alert(typeof(someNumber));
//alerts number
Ronan Boiteau
  • 9,608
  • 6
  • 34
  • 56
Leon
  • 2,386
  • 2
  • 20
  • 17
26

I solved it with converting it back to number using JavaScript's Number() function

var x = 2.2873424;
x = Number(x.toFixed(2));
ProgramFOX
  • 6,131
  • 11
  • 45
  • 51
Nizar
  • 481
  • 5
  • 8
16

Of course it returns a string. If you wanted to round the numeric variable you'd use Math.round() instead. The point of toFixed is to format the number with a fixed number of decimal places for display to the user.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
8

You can simply use a '+' to convert the result to a number.

var x = 22.032423;
x = +x.toFixed(2); // x = 22.03
meisam
  • 465
  • 6
  • 18
6

May be too late to answer but you can multiple the output with 1 to convert to number again, here is an example.

const x1 = 1211.1212121;
const x2 = x1.toFixed(2)*1;
console.log(typeof(x2));
Pumpkin Pie
  • 510
  • 1
  • 4
  • 15
4

You should use it like below.

var someNumber: number = 0.000000;
someNumber = Number(someNumber.toFixed(2))
Jenish Zinzuvadiya
  • 999
  • 2
  • 15
  • 29
3

What would you expect it to return when it's supposed to format a number ? If you have a number you can't pretty much do anything with it because e.g.2 == 2.0 == 2.00 etc. so it has to be a string.

Tomas Vana
  • 18,317
  • 9
  • 53
  • 64
3

Because its primary use is displaying numbers? If you want to round numbers, use Math.round() with apropriate factors.

Christoph
  • 164,997
  • 36
  • 182
  • 240
  • but it's displaying NUMBERS so shouldn't it return a "number"? – Derek Adair Feb 17 '10 at 19:08
  • 3
    @Derek: Only in the way that `'42'` is a number...which it's not. Just because a string happens to contain digits only does not make it a number. This isn't PHP. :-P – C. K. Young Feb 17 '10 at 19:10
  • lol. It's not a string that contains a number... It's a number that is passed to a method. The method takes a number and returns a string. – Derek Adair Feb 17 '10 at 19:18
  • @DerekAdair right but a browser can't display a Number, it displays strings, thus the conversion. – Nick M Jun 03 '16 at 02:19
3

To supply an example of why it has to be a string:

If you format 1.toFixed(2) you would get '1.00'.

This is not the same as 1, as 1 does not have 2 decimals.


I know JavaScript isn't exactly a performance language, but chances are you'd get better performance for a rounding if you use something like: roundedValue = Math.round(value * 100) * 0.01

Pyro
  • 1,447
  • 1
  • 10
  • 8
3

Why not * the result by 1 i.e

someNumber.toFixed(2) * 1

AllStackDev
  • 65
  • 1
  • 8
2

Here's a slightly more functional version of the answer m93a provided.

const toFixedNumber = (toFixTo = 2, base = 10) => num => {
  const pow = Math.pow(base, toFixTo)
  return +(Math.round(num * pow) / pow)
}

const oneNumber = 10.12323223

const result1 = toFixedNumber(2)(oneNumber) // 10.12
const result2 = toFixedNumber(3)(oneNumber) // 10.123

// or using pipeline-operator
const result3 = oneNumber |> toFixedNumber(2) // 10.12
Sartaj
  • 108
  • 1
  • 1
  • 9
  • it's handy function, for undefined type it does not work, I have added code for this case; if (num !== undefined) { return +(Math.round(num * pow) / pow) }else { return 0; } – Dino Liu Oct 30 '18 at 07:46
2

For others like me that happen upon this very old question, a modern solution:

const roundValue = (num, decimals = 2) => {
    let scaling = 10 ** decimals;
    return Math.round((num + Number.EPSILON) * scaling) / scaling;
}

ref: https://stackoverflow.com/a/11832950

OneManBand
  • 528
  • 5
  • 24
0

Be careful using toFixed() and Math.round(), they can produce unexpected results due to the floating point number system:

function toFixedNumber(num, digits, base){
  var pow = Math.pow(base||10, digits);
  return Math.round(num*pow) / pow;
}

console.log(toFixedNumber(130.795, 2, 10));
// 130.79 (incorrect)
console.log(toFixedNumber(100.795, 2, 10));
// 100.8 

console.log(+130.795.toFixed(2));
// 130.79 (incorrect)
console.log(+100.795.toFixed(2));
// 100.8

I recommend using Lodash's _.round() function: https://lodash.com/docs/4.17.15#round

_.round(130.795, 2);
// 130.8
Hedley Smith
  • 1,307
  • 15
  • 12