Fixed point isn't bad at all, just often slower when a compliant FPU is available.
IEEE only mandates results within ½ ULP (= best possible) for basic operations such as addition, subtraction, multiplication, division, and reciprocal.
For many other ones such as trigonometric functions, exponential and logarithms, results can (and do) vary between conforming implementations.
https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.h...:
“The IEEE standard does not require transcendental functions to be exactly rounded because of the table maker's dilemma. To illustrate, suppose you are making a table of the exponential function to 4 places. Then exp(1.626) = 5.0835. Should this be rounded to 5.083 or 5.084? If exp(1.626) is computed more carefully, it becomes 5.08350. And then 5.083500. And then 5.0835000. Since exp is transcendental, this could go on arbitrarily long before distinguishing whether exp(1.626) is 5.083500...0ddd or 5.0834999...9ddd. Thus it is not practical to specify that the precision of transcendental functions be the same as if they were computed to infinite precision and then rounded. Another approach would be to specify transcendental functions algorithmically. But there does not appear to be a single algorithm that works well across all hardware architectures. Rational approximation, CORDIC,16 and large tables are three different techniques that are used for computing transcendentals on contemporary machines. Each is appropriate for a different class of hardware, and at present no single algorithm works acceptably over the wide range of current hardware.”
> A conforming operation shall return results correctly rounded for the applicable rounding direction for all operands in its domain.
so all of them are supposed to be correctly rounded. I think IEEE 754-2008 also requires correct rounding, but I don't have that spec in front of me right now.
In practice, they're not correctly rounded--the C specification explicitly disclaims the need for them to be (§F.3¶20), reserving the cr_ prefix for future mandatory correctly-rounded variants.
Even with that and ignoring C’s “we don’t support that”), it still can be hard to write C code that provides identical results on all platforms. For example, I don’t think much code uses float_t or double_t or checks FLT_EVAL_METHOD (https://en.cppreference.com/w/c/types/limits/FLT_EVAL_METHOD)
The actual thing you need to do for consistency is to be extremely vigilant in the command line options you use, and also bring your own math library implementations rather than using the standard library. You also need vigilance in your dependencies, for somebody deciding to enable denormal flushing screws everybody in the same process.
As part of this I construct a series of test inputs, and confirm that they are bitwise equivalent to the high level language. It's usually as simple as aligning the rounding mode, disabling fused MAC, and a few other compiler flags that shouldn't be project defaults.
The other fun part is using the vector unit - for that we have to define IEEE arithmetic in the order the embedded device does it(usually 4x or 8x interleaved), port that back up, and verify.
Never did use a whole lot of transcendentals - maybe due to the domains I worked in.