Floating point: does same formula with same inputs give same result?

There is a bug appeared with gcc+x87: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323
If intermediate result is not stored from 80-bit fpu register to 32-bit variable, the result can change, and it is related from optimization options.
If we use SSE, the problem will not occured.

I'm compiling program in i686-pc-windows-gnu target.
Does compiler use SSE instructions by default?
Can I expect same formula results with same imputs in different context?

That bug "appeared" a long time ago. In the year 2000. And closed as "not a bug" as far as I can tell.

You should not expect precision in your results greater what the IEEE 754 standard specifies. Some intel floating point units have 80 bit precision internally so expect so some little differences.

Also that test is testing for the equality of two floating pint numbers. This is generally a bad idea.

See: What Every Computer Scientist Should Know About Floating-Point Arithmetic

I have no idea about how Rust handles this but the same advice applies no matter what language.

1 Like

I dont talk about comparing numbers calculated by different formulas. 2.0.sqrt() * 2.0.sqrt() is not equal to 2.0 in floating-point numbers, I know it.
I talk about determinism. Does 2.0.sqrt() * 2.0.sqrt() equal to 2.0.sqrt() * 2.0.sqrt() or not?

Yes, the target definition uses "pentium4" as the base CPU, which enables sse and sse2.

It does occur, but less often. In particular rcpps produces different results for the same input on Intel/Via CPUs and AMD CPUs.

And clang likes to use FMA instructions if they are available, which may also produce different results.

Do they produce different results on different machines?
Can they produce different results on same machine?

32-bit x86 (especially without sse) is a disaster. Just stop using it, if you care about floats.

2 Likes

I use -i686, so it enables SSE2, isnt it?
And I dont use flag -C target-cpu=pentium

Even with SSE enabled, the x86 C calling convention states that floats have to be returned in an x87 register, not an SSE one. And libm functions like sin and sqrt will likely still use x87 internally.

I tried these functions, sqrt compiled to sqrtss %xmm0, %xmm0, sin compiled to

movss	%xmm0, (%esp)
calll	_sinf

fma(x, y, z) has to return the same thing no matter what: the closest number to x*y + z. ("Closest" depends on your rounding mode which as far as I know you can't even set in Rust.) But x86 has seemingly redundant fma instructions that differ in which NaN propagates to the result (i.e., if all three inputs are NaN, which input should the output copy?).

Three “redundant” instructions are not redundant at all. Like with many RISC implementations you pick the output argument from three input ones.

And the trouble with fma is not that it's defined differently on different CPUs (it haven't), but with the fact that when you replace x * y + z with one fma instructions you may get different result.

It's, in some sense, “better” (read numerous papers about how fma is important for many algorithms), but, for the context of this thread, the important thing is that it's different.

The only way to guaranteed that floating point would be 100% stable is to encode everything manually in assembler, I'm afraid.

And even then you have to remember which instructions produce different results on different CPUs.

That's why GoogleTest includes set of macros to deal with floats, e.g.

That's why I wrote "seemingly." The difference between overwriting x or y in fma(x, y, z) has to do with NaN.

Is it legal for rustc to optimize the expression x*y + z to an fma? This seems bad for code that needs to recover the low bits of the product with fma(x, y, -x*y).

Currently no.

Not everyone agrees with that, however, see https://github.com/rust-lang/rfcs/pull/2686

I would be pretty upset if fma(x, y, -x*y) got optimized to 0. But I would be happy with a multiplication operator that couldn't be fused with an addition.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.