Earlier quoted context omitted.
> I would argue that financial math by definition needs to be accurate to the penny. Where is "pretty close" financial calculations considered acceptable? There is a difference between analysis and accounting. There are many financial models (e.g. Black-Scholes-Merton option pricing) that are analytic in nature and use transcendental functions, so the idea of getting an exact, arbitrary-precision answer is hopeless.…
This is an excellent point. When computing around money, you're either working with magnitudes or units. If you don't know which, you're working with units; use a Decimal.
Using floating-point numbers for money
101–110 of 128 posts
Re: Using floating-point numbers for money
#102Earlier quoted context omitted.
> I would argue that financial math by definition needs to be accurate to the penny. Yes, indeed. There is no such thing as "financial math that does not need to be accurate to the penny". I wonder if OP has ever worked with a bookkeeper...
That's not strictly true. Some of the work I do involves software to do premium calculations for insurance. While pennies do matter for intermediate values during calculation, virtually everyone rounds the final premium to a dollar amount or the closest 10 cent increment. Nobody cares about pennies when each transaction is hundreds or thousands of dollars.
Re: Using floating-point numbers for money
#103> if you are doing some financial math that does not need to be accurate to the penny
There is no such thing. I worked with applications that dealt with tens of billions of Euros, and if the bottom line was off by even 1 cent, the users would come to us to figure out what went wrong. Suggesting that this is acceptable to knowingly introduce errors, when there is a pretty well-established practice that allows to avoid them entirely, is baffling.
Re: Using floating-point numbers for money
#104No, do not do financial calculations in binary floating point. Converting between base 2 and base 10 fractions can do funny things (including during the rounding step, which is also 99% guaranteed to be inaccurate because base 10 fractions can't be exactly represented as base 2 fractions). Most professional financial packages use fixed point decimal, which can easily be implemented by specifying a fractional unit (su…
https://docs.microsoft.com/en-us/dotnet/csharp/language-refe...
Re: Using floating-point numbers for money
#105Earlier quoted context omitted.
Updated my comment, thanks. I meant binary float. Decimal float is rather unknown (unfortunately), so most people imply binary when they say "float", and most languages don't even have decimal float, so the warning is necessary. PSA: If your favorite language doesn't support ieee754 decimal float, start badgering them to add it! This has gone on long enough, and the inertia against change is strong.
> If your favorite language doesn't support ieee754 decimal float, start badgering them to add it! This has gone on long enough, and the inertia against change is strong. Without hardware support (which makes decimal float a good choice for performance), I'm not sure I've ever run into a use case where decimal float is a compelling choice if I already have arbitrary precision decimal and hardware-backed binary float…
Generally, whatever you think you are getting from decimal floating point, you very, very likely are not.
There is exactly one place where decimal floating point is the Right Thing: when interoperating with another system that is already using it. Those exist because others' superstitions have been locked in, sometimes even into regulatory frameworks. Decimal floating point is inherently less accurate, on any lossy computation, than binary, so you need a lot more digits to maintain the same result accuracy. This is why 128-bit decimal is common.
It is generally pointless to argue with anybody who thinks decimal computations are better. If reason mattered to them, they wouldn't be stuck on the idea. So, just roll your eyes and, if they have any authority, use a library. Performance will suck, but not so badly as you might guess, and you can spend the time until release circulating your CV without panic.
Re: Using floating-point numbers for money
#106From the article: "I am not a theoretician and have not proven that this is actually correct." So, yes, you might get away with floating point values for financials -- and I have even seen banking code that did -- but that doesn't mean it's a good idea. Especially when libraries providing fixed point and arbitrary precision decimal representations are widely available and easy to use. The biggest problem with using I…
It’s great how coding style books rarely address these important things and focus only on bookshedding. Anyone know of a good presubmit checklist for semantic issues?
Re: Using floating-point numbers for money
#107> Solution: Round after every operation No, the solution is not to use floating point numbers to store money. I worked on an iOS app in fintech for years, and let me assure you, using floating point numbers to calculate currency is an exercise in frustration and lack of correctness. When balances are wrong you're losing your customers money, which in turn loses trust in your product. You know it's inexact, an approxi…
> Just do it with a fixed-precision decimal number, and represent it as a string. Why? If you are going to do that, might as well use an integer of cents. It's more compact and if you're worried about these calculations and storing this kind of information, it's likely you are storing lots of it (or it wouldn't be a problem).
Re: Using floating-point numbers for money
#108Of course you can , but for the vast majority of cases, you shouldn’t . Floating points aren’t designed to solve the problems financial calculations bring, they’re designed for general purpose math and efficiency. If you’re programming a point-of-sale system, or a ecommerce site or something, using IEEE-754 floats would be madness. The increase in performance compared to decimal types is absolutely infinitesimal, and…
How would you suggest to store and calculate things like taxes? For example in NYC the retail sales tax is 8.735%. Obviously the final amount could be stored as an integer in cents, but I'm talking about the tax rate and the calculations of it. I guess you need to calculate the tax for each item, round it to cents (and I assume the rules on how you round can vary by jurisdiction, so it's not just calling your languag…
In C:
unsigned long tax_in_cents(double amt, double rate)
{
/* requirement: amt is an integer multiple of 0.01
and rate is an integer multiple of 0.00001 */
const unsigned long M = 10000000;
return (unsigned long)round((round(amt * rate * M) / (M/100)));
}
That should work, at least for amt that isn't too high.Here is how I would do it, using integers:
unsigned long tax_in_cents(unsigned long amt2, unsigned long rate5)
{
/* amt2 is the amount in cents, rate5 is tax rate * 10000 */
return (amt2 * rate5 + 50000) / 100000;
}
(My convention in this kind of code is that if an underlying thing is represented as an integer by multiplying it by a power of 10, I put that power as a suffix on the integer variable name. So amt2 means the real amount x 100, and rate5 means the tax rate x 100000. A ...2 x ...5 gives something that is ...7, which is why we divide by 10^5 to get the tax in cents (a ...2). Half of the divisor, or 10^5/2, is what you add for rounding).I've not proven the above floating point code, but I have brute force tested it with every combination of rates from 0.00001 to 0.99999, price from $0.01 to $10000.00, and floating rounding modes FE_UPWARD, FE_TONEAREST, and FE_DOWNWARD.
Most initial attempts to write such a function make the mistake of doing the rounding at the wrong place. They just try something like amt x rate then round that to the nearest cent.
The problem with that can be seen by a very simple example: 10% of $21.15. It should be $2.12. It comes out before rounding as 2.1149999999999998, so rounding to the nearest 0.01 comes out as $2.11. Various "obvious" ways you might do this might work for that case, but they fail in others. For example, here are three ways in Python that readily come to mind:
def tax_f1(amt, rate):
tax = round(amt * rate,2)
return round(tax * 100)
def tax_f2(amt, rate):
return round(amt*rate*100)
def tax_f3(amt, rate):
return round(amt*rate*100+.5)
but try all of those on these: 1% of $21.50
3% of $21.50
6% of $21.50
10% of $21.15
and every one of them fails at least once. The correct answers 22, 65, 129, and 212. Here is what they give, with the wrong ones prefixed with !: tax_f1: !21 65 129 !211
tax_f2: 22 !64 129 !211
tax_f3: 22 65 !130 212
There was a discussion of this here a couple months ago [1]. In that discussion, someone who knows considerably more about floating point that I do (kccqzy) suggested how to fix it, and even his first try failed when I run it through my brute force testing. He then suggested a method that worked (at least according to my brute force testing).Re: Using floating-point numbers for money
#109We've all probably read that “Programs must be written for people to read, and only incidentally for machines to execute.”
When another (maintenance) developer sees float/double being used to represent money, their first reaction is going to be "uh oh, this dev didn't know what s/he was doing". There's a big risk of someone reflexively refactoring this later on.
You may ask, "What if I put in big comment blocks saying essentially 'I knew what I was doing here'?"
Well, even if maintenance devs read that and believe you, they're still likely to introduce errors, since they aren't used to using floats for money, and don't know all the tricks to avoid errors...
Unless there's some justifiable win in size / performance, this (although interesting) falls under my "Don't be too clever" rule.
Re: Using floating-point numbers for money
#110Disclaimer: 30+ year veteran at fixing these fucking bugs. My opinion: just NO. MULTIPLE failures to understand the problem have occurred in this article, and they deal with representation . A) It is never acceptable to 'round up' on interest payments/loans/debts/balances, without having an explicit line item for why. "Because compiler decision made by programmer" is indefensible. B) Representation is EVERYTHING. Cas…