The "bug" here is that a single operation x + 0.1 and x + 0.1 can give two different results depending on the mood of the compiler.
It's not actually a bug, because the IEEE 754 standard permits this kind of sloppiness by default. You can get stricter semantics, but you have to ask for it explicitly by setting the right compiler flags. This is why I say that the IEEE 754 standard makes floating-point arithmetic deterministic "in principle" - in practice, you have to work for it. (Changes to the rounding mode are another big issue here.)
An extra error of 1 ulp somewhere might not seem like much for your average numerical code, but it's a big deal in some situations:
* Assumptions about the precise behavior of floating-point cancellations are often used in numerical libraries, and deviations can cause disastrous loss of accuracy. For example, Kahan summation is a widely used algorithm for computing sums with increased accuracy. If compilers optimize the code too aggressively, the algorithm doesn't work and you get worse accuracy. And ironically, x87 long doubles can make general numerical calculations less accurate (not more accurate as intended) due to the double rounding issue.
* If you use floats in game logic, you might want it to behave exactly the same way on different platforms, e.g. for synchronization or replays.
* In scientific computing, you might want results to be precisely reproducible by others.
* Debugging!
So just a warning to everyone out there: unless you are very, very careful your floating point code will not be deterministic. If you thought you can just use floats and expect it to "just work" the same, then you're out of luck.
Here's a couple of articles which go into more detail about problems with floating point determinism:
http://gafferongames.com/networking-for-game-programmers/flo...
https://randomascii.wordpress.com/2013/07/16/floating-point-...
[1] transcendental functions IIRC are not required to be exact to the full precision and historically they aren't. But there are (portable) math libraries that guarantee full precision.
abs(a - b) <= eps*0.5*(a + b)
or even (abs(a - b) <= eps*abs(a)) && (abs(a - b) <= eps*abs(b))
which is implemented in Boost [0].[0] http://www.boost.org/doc/libs/1_34_0/libs/test/doc/component...
(a / b) != (a / b)
assert x/y == x/y
There are some cases where direct comparison of floating point values is what you want, and this issue can still bite you even then.
The bug is that GCC is utilizing the (documented, by-design) x87 behavior in a way that causes it to produce the wrong result, for the sake of performance.
I still think the relationship between compiler developers and developer-users is toxic as fuck, but as long as we keep comparing compilers by increasingly anal microbenchmarks instead of increasingly anal "correct in practice" tests, I don't see why we continue to find this a surprise.
When we demand better performance, compiler developers are going to exploit undefined behavior as well as other language "loopholes" that don't make intuitive sense. Perhaps a new focus on correctness in practice would change this.
Just because someone doesn't know the basics of floating point numbers it doesn't mean that the floating point format is somehow evil.
Testing floats for equality is one of those no-nos which is taught right in the first class of introduction to FP numbers.
Another very basic issue covered right in the intro clsses are problems caused by using decimal values to define numbers encoded in binary format, which is often explained through the famous "0.1 doesn't exist in binary" trivia bit.
Of course, it's not fine to ask "Does the result of this computation equal this value?", but everyone knows that.
How about "Does the result of this computation equal the result of this exact same computation?"? That's the issue here.
If you have infinite memory, you would probably start by asking whether you have countably infinite or uncountably infinite memory.
Floating point doesn't actually fix this issue. A fun bug I dealt with was a black screen caused by a few pixels having NaN values - which were then picked up by a blur pass which would poison basically the entire back buffer. The original NaNs were caused by divides by near-zero values causing out-of-range results.
You now add precision issues: Multiple coordinate systems are a common hack-around to maintain local precision when using single-precision based physics engines. Stuttering effects when time is represented as "floating point seconds since uptime" when integer wraparound would avoid any problem in the first place. And this is before even adding e.g. half precision or worse floats into the mix.
There's a reason floating point isn't used for a lot of serious banking calculations.
Financial calculations are another matter; they usually require decimal arithmetic, not because it doesn't give rounding errors, but because it gives the same rounding errors an accountant gets when he checks the result with his desk calculator, and that's the requirement.
64-bit fixed point solves a lot of problems too :)
> I think single precision floating point should never have been invented, and given that it unfortunately was, it's best to behave as though it doesn't exist. Don't get me started about half precision.
A 4x memory bandwidth & storage penalty for HDR buffers would be absolute murder (bloating a single 4k RGBA surface to 265MB if I've done my math right), so I'm glad we have the option. And in the historical context lower precision options were invented in, RAM wasn't nearly as cheap as it is today. Tradeoffs.