2

For rounding decimals (prices), I've been using .toFixed(2) for quite some time now. But I just recently discovered that Javascript can't "precisely" round decimals. I was a bit shocked that even 10.005 couldn't be rounded correctly to 10.01. It just got rounded down to 10.00. And other times it did round correctly. I like to have control over my code, so this is a big no-no for me.

And since I'm calculating prices, I think I need something more (100%) accurate for rounding only 2- or 3-decimal numbers, maybe a 4-decimal one.

Is there no straightforward way of doing basic rounding in javascript, the correct way?

UPDATE: As Felix Kling has suggested, the method of processing my prices as integers of cents, there are also drawbacks to this (besides more code)?

fishbaitfood
  • 659
  • 2
  • 10
  • 19
  • 6
    Don't use floating point values for money. The easiest (imo) solution would be to compute prices in cents (or whatever currency you use) and format the value according to your needs (e.g. in dollars and cents). You avoid rounding errors that way. – Felix Kling Oct 07 '12 at 10:57
  • In cents?? Have to look up that one! – fishbaitfood Oct 07 '12 at 10:59
  • 1
    Btw, are you sure that it was `10.005` that wasn't rounded correctly, and not something like `10 * 1.0005`? – raina77ow Oct 07 '12 at 11:00
  • @alex Indeed, it's because of binary processing, I've read somewhere. – fishbaitfood Oct 07 '12 at 11:03
  • 2
    Other languages, such as Java, have a `Decimal` data type, but such a thing does not exist natively in JS. See also: [Precise Financial Calculation in JavaScript. What Are the Gotchas?](http://stackoverflow.com/questions/2876536/precise-financial-calculation-in-javascript-what-are-the-gotchas). – Felix Kling Oct 07 '12 at 11:04

2 Answers2

1

The reason that a number like 10.005 can't be rounded corretly is that you don't really have the number 10.005, you only have a number that is the closest possible one that can be represented using a double precision floating point variable.

The actual number that you have might be someting like 10.00499999999276253, and that would naturally round to 10.00 rather than 10.01.

To handle monetary values you should use a data type that can represent the value exactly. As numbers in Javascript are always floating point numbers, what you are left with is representing the numbers as text, and writing your own functions to do the math (or find someone who has done that already).

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • So use BigDecimal or BigNumber, instead of processing my prices as cents? Also, 10.00499999999276253 rounded, should still be 10.01, as .0049 should become .005, which should result in the correct .01. Or am I missing something? – fishbaitfood Oct 07 '12 at 11:20
  • 3
    @fishbaitfood: That's not how rounding works. If I round `0.4445`, I'd expect to get `0`. With your rules, I get `0.445`, then `0.45`, then `0.5`, then `1`. Rounding multiple times is not a good idea. – Eric Oct 07 '12 at 11:22
  • That makes sense, but for a 2-decimal price number, it just seems the right rounding... :/ – fishbaitfood Oct 07 '12 at 11:27
  • @fishbaitfood—when rounding to *n* places, only the first *n* + 1 digits are considered. So `10.00499999999276253` is truncated to `10.004` then rounded to `10.00`. – RobG Oct 07 '12 at 20:54
  • 1
    @fishbaitfood: It wasn't a tip, it's an explanation of how rounding works. – Guffa Oct 08 '12 at 10:42
0

This should work for you, here's a fiddle to play around with it http://jsfiddle.net/5ffyC/1/

function moneyRound(flt){
   var splitStr = flt.toString().split('.'),
      whole = (flt * 100) | 0;

   if (splitStr.length > 1  && splitStr[1].length > 2){
      return splitStr[1][2] > 4? (whole + 1) / 100: whole / 100;
   } else {
      return flt;   
   }
}
hobberwickey
  • 6,118
  • 4
  • 28
  • 29