Precision of `exp2f` on x86 without SSE
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 https://github.com/rust-lang/rust/issues/114479 . In a nutshell, LLVM
ignores the requirement to round each intermediate result to its type. For example, code like
```rust
let xy: f32 = x + y;
xy - y
```
might be evaluated more like
```rust
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.)
0 条评论