I originally had the wrong answer here, but I'm including below for historical purposes and embarrassment. Re-reading on my laptop meant I didn't misread/read-the-expected?
Basically by default when compiling for IA32 gcc assumes that there is no SSE unit, and so must use the x87 fpu.
Alas the x87 unit doesn't have either 32 or 64 bit ieee floating point types, so the calculation at runtime must be rounded to float/double at the end of the computation. That results in a double round that is incorrect.
Minor additional comment - x87's reduced precision modes (that reduce mantissa precision to ieee754-32/64) don't reduce the exponent size so it's conceivable you could get multiple-rounding derived errors even then. But no one would be toggling x87 state like that anyway, so theoretically not something that would matter in most cases.
Old incorrect answer:
On ia32 long double is the x87 80bit ieee754 numeric type.
On x86_64 on windows, and maybe Linux, long double is just a 64bit ieee float.
So my guess is that GCC computes constant evaluation of floating point expressions in “long double” for maximum precision, and then rounds to the required precision at the end. This is not valid going from 64->32 bit floats and you could probably induce this bug if you tried hard enough.
Anyway it is not sound to round twice in any numeric operation, and by doing the calculation at one precision and then rounding that to the required precision that is what gcc is doing.
This is a bug in GCC, floating point is well defined, there isn’t randomness to it, and they’ve introduced additional rounding that is not valid.
Except the C standard has a way to communicate how the extra rounding is being introduced, and it's not random (for C, not C++).
There is simply no way around this issue with the x87 instruction set, GCC (again only for C only, not C++) implements FLT_EVAL_METHOD==2 which is the most sensible that can be implemented with decent performance and without breaking 80-bit long double.
If you are sure you don't need long doubles, use -mpc64.
If you are constrained to x87 your only option is multiple roundings which is bad. Ideally you'd just keep processing in the x87 stack as long as possible, but that would result in a different version of "incorrect" results.
Honestly if you're a compiler required to use x87 with non-80bit floating point I'm not sure how you can do the right thing :-/
I don't know what gcc has but llvm has a full software floating point implementation to handle cross compiling so it can match native arithmetic.
That's the best they can do.
> The long double type uses a 15 bit exponent, a 64-bit mantissa with an explicit high order significant bit and an exponent bias of 16383.
So it is an 80-bit float.
For example on osx it is 80bit ieee, but on win64 it is a 64bit ieee.
That ABI difference means that having the same instruction set is not the only thing that matters.