5 ms·
Work with cents everywhere and format the output in the UI. Done.
by EastSmith 7y ago
Work with cents everywhere and format the output in the UI. Done.
- moftz 7y agoUnless I have to actually work with floats, I try to only do everything with int math to keep things sane. Not to mention, that it usually makes calculations faster.
- Aardwolf 7y agoDecimal floating point exists (https://en.wikipedia.org/wiki/Decimal64_floating-point_format https://en.wikipedia.org/wiki/Decimal64_floating-point_forma...), is that considered useable for financial data, or does it turn out in practice to also suffer from the same issues? Since, e.g., dividing through 3 will not be exact here either of course.
- HideousKojima 7y agoIt doesn't have the same issues (at least from my experience in C#) but it runs quite a bit slower than normal floats and doubles: http://csharphelper.com/blog/2017/05/compare-the-performance-of-the-float-double-and-decimal-data-types-in-c/ http://csharphelper.com/blog/2017/05/compare-the-performance...
- throwaway744678 7y agoNot that simple, though. That's the "division and rounding" method of the article: share a $.99 fee equally between two parties. That's either 49 cents each or 50 cents each if you're not careful. In both cases, where is the extra cent?
- ars 7y agoIf you use floats and rounded that one cent would disappear with them as well. Unless you're storing half cents in their account and not showing it. Which is perfectly legitimate by the way, because if you earn a fraction of a cent interest accumulated over a couple months that might actually be a cent and you have to pay it. The solution might be to store everything as 1/100 of a cent, as an integer.
- kennu 7y agoThis approach doesn't work when dealing with fractional cents, like charging $0.0001 per hour for something. I would rather recommend using an arbitrary precision decimal library like Decimal.js, and using strings to represent money in JSON. And of course using an appropriate database type for storing the data.
- lanna 7y agoJust make the smallest possible fractional cent the unit then
- Kiro 7y agoWhat if you can't know that beforehand? E.g. if you suddenly need to charge $0.000000001 per second.
- kennu 7y agoThat works if you are 100% sure you know beforehand what the smallest possible fraction will be. What you gain by consistently using an arbitrary precision decimal library to process all monetary values, is the freedom to process smaller fractions later if necessary. Also you'll never overflow with really big values. In other words, when you represent monetary values with arbitrary precision Decimal objects everywhere, you can easily expand your application as much as you want on either side of the decimal dot. Changing existing integer-based code do to that is very error-prone, you quickly lose sight of what exactly an integer value or parameter means in different places.
- beerandt 7y agoTake the inverse and use fractional hours per penny. Edit: And sum your (optionally price-weighted) time first.
- DeedsMoraine 7y agoThe 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?
- Scirra_Tom 7y agoI 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.
- beatgammit 7y agoYeah, 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.
- tzs 7y ago> 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.
- tunesmith 7y agoReally the only foolproof method is to use rationals - numerators and denominators.
- iod 7y agoWas going to say this too, specifically rationals with arbitrary precision should cover all general currency related issues assuming irrational values are forbidden.
- tzs 7y agoWorking in cents isn't always sufficient. Let's do 34.7% of $25. In cents, $25 is 2500. 34.7% of that is exactly 867.5 cents, which should round to 868 cents under the most common rounding methods in most commercial applications (round to nearest with 0.5 rounding up, or round to nearest, with 0.5 rounding to even). In JavaScript: > p=2500; r=0.347; < 0.347 > Math.round(p*r) < 867 You need to integerize the percentage, too. For my current financial applications, all the percentages I need are integer multiple of 0.01%, so I use that as my "percent cent". 34.7% is then represented as 3470. That leads to a function something like this (assuming you need the 0.5 round up rule...changing it to 0.5 rounds to even is left as an exercise): function percent(amt2, rate4) { return Math.floor((amt2 * rate4 + 5000)/10000) } (The 2 and 4 suffixes are a naming convention I use. They are reminders that amt2 is the underlying amount x 100, and that rate4 is the underlying rate x 10000).