rust 这是浮点型融合乘加的预期行为吗?

ecbunoof  于 2023-01-17  发布在  其他
关注(0)|答案(1)|浏览(120)

我有三个使用(32位)浮点数精确表示的数字:

x = 16277216, y = 16077216, z = -261692320000000

我期望执行一个融合乘加x*y+z来返回数学上正确的值,但是四舍五入了,正确的数学值是-2489344,它不需要四舍五入,因此这应该是融合乘加的输出,但是当我执行fma(x,y,z)时,结果是-6280192,为什么?
我用的是铁 rust 色。注意z-x*y的四舍五入结果。

let x: f32 = 16277216.0;
let y: f32 = 16077216.0;
let z = - x * y;
assert_eq!(z, -261692320000000.0 as f32); // pass
let result = x.mul_add(y, z);
assert_eq!(result, -2489344.0 as f32); // fail

println!("x: {:>32b}, {}", x.to_bits(), x);
println!("y: {:>32b}, {}", y.to_bits(), y);
println!("z: {:>32b}, {}", z.to_bits(), z);
println!("result: {:>32b}, {}", result.to_bits(), result);

输出为

x:  1001011011110000101111011100000, 16277216
y:  1001011011101010101000110100000, 16077216
z: 11010111011011100000000111111110, -261692320000000
result: 11001010101111111010100000000000, -6280192
pokxtpni

pokxtpni1#

我有三个使用(32位)浮点数精确表示的数字:
x = 16277216, y = 16077216, z = -261692320000000
这个前提是错误的。-261,692,320,000,000不能用任何32位浮点格式精确表示,因为它的有效位需要37位来表示。
float通常使用IEEE-754 binary32格式,其有效位为24位,将− 261,692,320,000,000的有效位大小调整到224以下,得到− 261,692,320,000,000 = − 15,598,077.7740478515625·224。有效位在这个范围内不是整数,所以不能精确表示,我也不认为它是精确的,最接近的可表示值是-15,598,078·224 = -261,692,323,790,848。
println!("z: {:>32b}, {}", z.to_bits(), z);
...

z: 11010111011011100000000111111110, -261692320000000

rust 在撒谎z的值不是-261692320000000,它可能使用了某种算法,比如四舍五入到8位有效数字,剩下的数字用零表示,z的实际值是-261,692,323,790,848。
16,277,216·16,077,216 - 261,692,323,790,848的值使用普通实数运算是-6,280,192,所以FMA的结果是正确的。
舍入错误发生在let z = - x * y;中,其中将16,277,216与16,077,216相乘会将实数算术结果261,692,317,510,656舍入为最接近的二进制值32,261,692,323,790,848。

相关问题