Description
#include <math.h>
float foo() {
return fmaf(
-0.000000000000000000000000000000000000014728589,
0.0000037105144,
0.000000000000000000000000000000000000000000055
);
}
results in (e.g. via godbolt):
llvm/lib/Support/APFloat.cpp:1805:
llvm::lostFraction llvm::detail::IEEEFloat::addOrSubtractSignificand(const llvm::detail::IEEEFloat&, bool):
Assertion `!carry' failed.
However, that's not how the bug was found. I noticed 8-bit formats (like Float8E5M2
and Float8E4M3FN
) were added to APFloat
, and decided to try brute-forcing all possible inputs for a few common operations (it only takes 8 seconds, and ~98% of that is FMA, because it uniquely has 3 inputs).
So the first example I found was for Float8E4M3FN
, namely: FMA(0.0254, 0.0781, -0.00195)
(the encoded byte values being 0x0d
, 0x1a
, 0x81
).
Then @solson helped me turn some formula sketches I made looking at the code, into Z3, which confirmed that the example I found was the only one across all possible 8-bit floats, but 16-bit and 32-bit have a lot more cases. (I'm only not including all that here because it's not really set up to generate ready to use examples, it's really finicky as-is)
My understanding is that e62555c fixed most of the previously problematic cases, but not the ones where both significands are equal before subtraction, which will only work if lost_fraction == lfExactlyZero
.
(cc @ekatz)
But if there is a lost fraction, neither direction will work to subtract equal significands - the code seems to rely on being able to assume that only equal exponents can result in equal significands, but we know that's not true w/ FMA.
(also, the lost fraction is always subtracted, regardless of the subtraction direction, but before e62555c the source of the lost_fraction
was tied to reverse
, i.e. a.subtractSignificand(b, lost_fraction != lfExactlyZero)
was always performed with lost_fraction
coming from lost_fraction = b.shiftSignificandRight(...);
, regardless of how a
and b
mapped to *this
and temp_rhs
, respectively - this seems significant as well, but I'm not sure how to tell or test this)