rust-lang/compiler-builtins

Rounding error in `__mulsf3`

tgross35 opened this issue · 6 comments

This is a problem in Rust's compiler-builtins, but also the LLVM library (which is demonstrated here). The correct answer should be 0.0.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=54ee2169049eb22eb1b44d41c6f37e1e

extern "C" {
    fn __mulsf3(a: f32, b: f32) -> f32;
}

fn main() {
    let a = 7.83651e-39f32;
    let b = 2.7310085e-8f32;
    let res_asm = a * b ;
    let res_mulsf3 = unsafe { __mulsf3(a, b) };
    println!("asm {} {:#010x}", res_asm, res_asm.to_bits());
    println!("mulsf3 {} {:#010x}", res_mulsf3, res_mulsf3.to_bits());
}
asm 0 0x00000000
mulsf3 0.000000000000000000000000000000000000000000001 0x00000001

Hm, I can't reproduce this in C

Also a problem with f64: f64 mul(1.488018149503636e-307, -2.8933234101276373e-18): crate: -5e-324, asm: -0.0

res_asm doesn't actually use asm, it's const-evaluated by rustc. However inline assembly also produces a result of 0: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=89eb0e7d4c02667b77234004c5f313ec

It definitely seems like a library problem, IEEE734 should produce an exact result for multiply assuming round-to-nearest.

This is probably related to the rounding issue that I thought was limited to f128 llvm/llvm-project#91840

multf3 too, on x86, I am surprised this didn't come up on fuzzing since it seems like the system functions do the right thing here.

#![feature(f128)]

use rustc_apfloat::ieee::Quad;
use rustc_apfloat::Float;

extern "C" {
    fn __multf3(a: f128, b: f128) -> f128;
}

#[test]
fn foo() {
    let a = f128::from_bits(0x03a00000000000000000000000005400);
    let b = f128::from_bits(0x3c2caaaaaaaaaa00000000002aa80002);
    let aq = Quad::from_bits(a.to_bits());
    let bq = Quad::from_bits(b.to_bits());
    let res_llvm = a * b;
    let res_crate = compiler_builtins::float::mul::__multf3(a, b);
    let res_sys = unsafe { __multf3(a, b) };
    println!("llvm {:?}", res_llvm);
    println!("crate {:?}", res_crate);
    println!("sys {:?}", res_sys);
    println!("ap {:#034x}", (aq * bq).value.to_bits());
    assert_eq!(res_llvm.to_bits(), res_crate.to_bits());
    assert_eq!(res_llvm.to_bits(), res_sys.to_bits());
}
llvm 0x00000000000000001aaaaaaaaaa00000
crate 0x00000000000000001aaaaaaaaaa00001
sys 0x00000000000000001aaaaaaaaaa00000
ap 0x00000000000000001aaaaaaaaaa00000
thread 'foo' panicked at testcrate/tests/break.rs:23:5:
assertion `left == right` failed
  left: 1921535841010712576
 right: 192153584101071257

Note that on -gnu targets, the system version of __mulsf3 and other builtins comes from libgcc, not LLVM's compiler-rt.