Rust 使用f64::mul_add时,如何引入额外的舍入错误?
根据文档,可用于减少舍入错误的机会: 融合乘加。计算Rust 使用f64::mul_add时,如何引入额外的舍入错误?,rust,double,precision,floating-accuracy,Rust,Double,Precision,Floating Accuracy,根据文档,可用于减少舍入错误的机会: 融合乘加。计算(自身*a)+b,只需一次四舍五入 错误。这将产生更精确的结果和更好的性能 而不是一个单独的乘法运算,然后是一个加法运算 我正在研究一个线性变换库,其中a*b+…非常常见。当我为我的AffineVectorstruct I的点积引入mul\u add时,精度降低了 这是点积法: impl AffineVector { pub fn dot(self, v: AffineVector) -> f64 { self.x
(自身*a)+b
,只需一次四舍五入
错误。这将产生更精确的结果和更好的性能
而不是一个单独的乘法运算,然后是一个加法运算
我正在研究一个线性变换库,其中a*b+…
非常常见。当我为我的AffineVector
struct I的点积引入mul\u add
时,精度降低了
这是点积法:
impl AffineVector {
pub fn dot(self, v: AffineVector) -> f64 {
self.x * v.x + self.y * v.y + self.z * v.z + self.w * v.w
// self.x.mul_add(v.x, self.y.mul_add(v.y, self.z.mul_add(v.z, self.w * v.w)))
}
}
使用mul_add
实现且没有其他更改,由于上一次断言中的浮点精度问题,以下测试失败:
#[test]
fn inverse_rotation() {
// create a rotation matrix for 1 radian about the Z axis
let rotate = AffineMatrix::new(Primitives::RotationZ(1.));
// create a matrix that undoes the rotation of 'rotate'
let revert = rotate.inverse();
// apply the transformation to the vector <1,0,0>
let rotated = rotate.apply_vec3(KVector3::i_hat());
// assert that the result is <cos(1),sin(1),0>
let expected = KVector3::new(C, S, 0.0);
assert_eq!(rotated, expected);
// use the 'revert' matrix to undo the rotation
let returned = revert.apply_vec3(rotated);
// assert that the result is back to <1,0,0>
assert_eq!(returned, KVector3::i_hat());
}
使用
mul\u add
如何以及为什么会降低精度?我如何有效地使用它?为什么您认为您“失去”了精度?似乎更可能的是,之前您有两个舍入错误,恰好相互抵消。现在只有一个错误,你可以看到。在这个完全抵消的过程中,涉及到的操作数的舍入错误似乎极不可能。尽管如此,浮点计算是精确的情况很少,而且这个测试似乎不是其中之一。如果您不希望0.1+0.2
等于0.3
,那么您不应该期望该测试通过,除非是随机发生的。(如果你真的期望0.1+0.2==0.3
,你应该先阅读。)假设从左到右计算加法,重新编码改变了求和的顺序,这是改变舍入的高风险。我建议在你进行更复杂的转换时,将其作为一个副本关闭,答案仍然适用。如果你实际上在做一些浮点数学应该是精确的(只有二进制分数,没有舍入),那么这应该是问题所在。
#[test]
fn inverse_rotation() {
// create a rotation matrix for 1 radian about the Z axis
let rotate = AffineMatrix::new(Primitives::RotationZ(1.));
// create a matrix that undoes the rotation of 'rotate'
let revert = rotate.inverse();
// apply the transformation to the vector <1,0,0>
let rotated = rotate.apply_vec3(KVector3::i_hat());
// assert that the result is <cos(1),sin(1),0>
let expected = KVector3::new(C, S, 0.0);
assert_eq!(rotated, expected);
// use the 'revert' matrix to undo the rotation
let returned = revert.apply_vec3(rotated);
// assert that the result is back to <1,0,0>
assert_eq!(returned, KVector3::i_hat());
}