-
Notifications
You must be signed in to change notification settings - Fork 247
Description
Currently libm::exp2f
is producing very inaccurate results on x86 without SSE (aka i586 in rustc). For example,
exp2f(1.0312501)
evaluates to approximately 2.0885...
, when it should be 2.0438...
, which is a relative error of ~2%, or only four correct fractional bits.
I looked into why that is, with the conclusion that it's a bad case of rust-lang/rust#114479 . In a nutshell, LLVM
ignores the requirement to round each intermediate result to its type. For example, code like
let xy: f32 = x + y;
xy - y
might be evaluated more like
let xy: f80 = x as f80 + y as f80;
(xy - y as f80) as f32
The current implementation of exp2f
is using that implied rounding to compute the exact rounding error, and thus reduce it without loss of precision. (I believe it is effectively https://en.wikipedia.org/wiki/2Sum or at least similar). This of course doesn't work at all if the compiler may arbitrarily choose to leave out some intermediate rounding steps.
It may be possible to tweak exp2f
so that it doesn't rely on correct rounding as crucially. However, it's worrying that even basic arithmetic isn't reliable.
One solution could be to implement it directly with inline assembly like we did with ceil
and floor
. By relying on x87s f2xm1
-instruction, it isn't too complex, and I've tested this already. However, this would mean that exact numeric results could still depend on platform. (For ceil
/floor
, there is only one allowed value, so it didn't matter.)