Hacker News new | ask | show | jobs
by zokier 853 days ago
This gets repeated a lot, and I don't disagree. But I find odd that doubles would be so unsuitable for monetary (and other similar) arithmetic; in principle you have 15 significant digits which should be more than enough, and precise control how the results are rounded. And all the basic arithmetic should return correctly rounded values to the last ULP. So it is weird that those tools are still not good enough and it is also difficult (at least for me) to fully characterize why exactly they are not suitable.

Part of me wonders if this (justified!) fear of floats is in part because a history of bad implementations (looking at x87) and difficulties in controlling floating-point env (looking at libs randomly poking fpenv), and less due floats intrinsically being bad.

3 comments

The problems of floats are not the number of significant digits, it’s the imprecision of the representation (floats don’t just cut off at the end), that these imprecisions compound, and that float operations are not commutative. At the end of the day, 0.1 + 0.2 != 0.3 is a fact you have to live with.

X87 does not really factor into it, if anything in your view of the world x87 floats would be better since x86-EP is 80 bits. Except its involvement now leads to intermediate-precision-driven inconsistencies.

Control (which you mention) and consistency are the issues, as well as the interaction between that and comparators.

Guarding against floating-point issues or considering precision errors is neither part of school-learned arithmetics, nor of most CS programs, to almost every developer just flings around floats like they’re genuine reals, and when problems start surfacing floats are so threaded through without consideration it becomes very hard to untangle, which leads to local patch jobs which make the problem worse.

> At the end of the day, 0.1 + 0.2 != 0.3 is a fact you have to live with

That is the one example that floats around a lot, but its also imho not very good one. '0.1', '0.2', and '0.3' are not floating point values, so the premise is flawed.

       0.1000000000000000055511151231257827021181583404541015625
     + 0.200000000000000011102230246251565404236316680908203125
    != 0.299999999999999988897769753748434595763683319091796875
is far less surprising.

Also `round(0.1 + 0.2, 15) == 0.3` is true (in python), so being conscious about rounding things appropriately goes long way. And I imagine that correct rounding is relevant in monetary calculations no matter what sort of numbers you are using, so while while floats the situation might be more pronounced I don't see it being such fundamental problem.

> That is the one example that floats around a lot, but its also imho not very good one. '0.1', '0.2', and '0.3' are not floating point values, so the premise is flawed.

No, it’s the entire point. None of the values we deal with day to day are binary floating point, and certainly not currencies. So this sort of representational approximations is a major and constant issue of using floats.

> Also `round(0.1 + 0.2, 15) == 0.3` is true (in python), so being conscious about rounding things appropriately goes long way.

See above, rounding off and collecting error after every arithmetic operation is not the expected norm and what developers are taught.

> And I imagine that correct rounding is relevant in monetary calculations no matter what sort of numbers you are using

While that is true, it does not normally need to be done after every arithmetic operation, especially not after additions of already rounded off values.

> See above, rounding off and collecting error after every arithmetic operation is not the expected norm and what developers are taught.

Question is, is that a problem with developers or floats? :)

Ecosystem and tooling might help here, iirc that is something Kahan himself has been complaining about a lot. For example hypothetically you could have something like FP contexts or specialized high-level types where you can easily express how many digits are you expecting and the runtime/compiler would manage rounding etc so that you'd get more often correct results. Tbh that's just top of my head, and I didn't think too much about it.

But I think the question remains, how much of the problems are actually intrinsic to FP, and if you did actually cross t's and dot i's then would there still be some intractable problems in using FP with money? So is the problem "just" that FP can easily be misused, or that its impossible to use correctly?

I want to emphasize that I do not recommend anyone go using FP for money now. I'm just curious because its something I don't fully understand and well, HN has smart people that can hopefully help me there.

Question is, is that a problem with developers or floats?

Of course with floats. Requirements come from decimal-expecting people and developers have to convert requirements into an algorithm. If there’s a fundamental semantic or at least syntactic obstacle, it’s not a problem with developers.

Iow, if a language/system only has floats as “numbers”, it sucks for most business-level calculations.

> None of the values we deal with day to day

'0' is twice-defined by IEEE 754, together with ±inf and nan. They aren't very interesting values in terms of monetary transactions, though.

> and that float operations are not commutative. At the end of the day, 0.1 + 0.2 != 0.3 is a fact you have to live with.

Addition and multiplication of floating point numbers is commutative (sole theoretical exception: there principally exist multiple representations of NaNs, even though in practice processors do not make use of this freedom, i.e. in practice these floating point operations are completely commutative).

Yes, but not associative. I guess this is what GP was trying to say (and which actually can cause issues).
> At the end of the day, 0.1 + 0.2 != 0.3 is a fact you have to live with.

But 0.1 + 0.2 == 0.3 — if you round both sides to 2 (or however many you need) decimal points before comparing them.

And that’s a problem, now you have to round-off defensively which complexifies the code and you have to decide how often and how defensively you round things.

Plus you’ve got the added fun that fp rounding routines don’t necessarily take a rounding mode.

> Guarding against floating-point issues or considering precision errors is neither part of school-learned arithmetics, nor of most CS programs

What the fuck? I didn't even take a CS major and we got the imprecision of floats bashed into our heads. What are CS majors being taught?

Floats vs money is like `ascii` vs `utf-8`.

You can get a bunch of bits, but how you handle it make a ton of differences

Plus, so little languages care about us living in the business world. You can count with fingers in a single hand the languages that are meant for business app.

All the others are bad languages (and libraries, frameworks, data stores), ill-suited for the job. And so, all of them need to reimplement (badly, ad-hoc, bug-ridden) version of money and friends.

And weirdly, no hardware support at all, so our friends on C say: "Look, no important to handle money, bye" and nobody else does it either.

The problem is intrinsic to floats, not just due to bad implementations.

Some values like 0.3 simply cannot be represented precisely with floats.