Hacker News new | ask | show | jobs
by DeedsMoraine 2309 days ago
The smallest unit of US money is the mill or 1/1000 of a dollar. The smallest unit of US currency is the cent. You're supposed to work in mills and round to cents.

Or just work in floats and round as the last step. Or do both selectively depending on which rounding error works in your favor, but don't tell anyone that's what you're doing.

The real problem is that nobody at the company seems to have looked at the parts of their software that everyone sees, so what else have they not looked at?

2 comments

> Or just work in floats and round as the last step

That's not as easy to get right as one might like. Let's say we have an amount in dollars, represented as an IEEE 754 double, and a tax rate, also represented as a double. Let's assume that the amount is always some integer multiple of 0.01, and the tax rate is always some integer multiple of 0.00001.

Let's say we want the tax in cents. A first try might be:

  unsigned long tax_in_cents(double subtotal, double rate)
  {
    return (unsigned long)round(subtotal * rate * 100);
  }
(Doing tax in cents because C/C++ doesn't seem to have a standard variant of round() that lets you say to round to the nearest 0.01. It only rounds to integers).

That will sometimes fail. The problem is it is rounding at the wrong place. It's logically rounding to the nearest multiple of 0.01, which is too crude. The rounding has to be much farther to the right.

This will do the trick:

  unsigned long tax_in_cents(double amt, double rate)
  {
    const unsigned long M = 100 * 100000;

    return (unsigned long)round((round(amt * rate * M) / (M/100)));
  }
That will work for all rates from 0 to 1 that are integer multiple of 0.00001, and all amounts from 0 to 10000 that are integer multiples of 0.01, in all IEEE rounding modes. I've verified this via brute force. I'm not sure how high the amount can go before it breaks down.

I'm not at all sure that if I had come across that first try in real life I would have noticed that it is not adequate.

I really don't see any need or benefit in storing values as mills unless you're writing forex trading platforms. It's inviting more mistakes.
Yeah, nobody is going to notice or care about an error of a cent or two for something like retail, especially if it evens out in the long run. It matters for finance, not for stuff like Uber or Amazon. Just make sure you render it properly on the front end and nobody will care whether you use floats, ints, or decimals. Be consistent in the back end and careful in the front end, ideally with standard functions for translations.