Согласно документации, f64::mul_add может использоваться для уменьшения количества возможностей для ошибок округления:
pub fn mul_add(self, a: f64, b: f64) -> f64Fused multiply-add. Computes
(self * a) + bwith only one rounding error. This produces a more accurate result with better performance than a separate multiplication operation followed by an add.
Я работаю над библиотекой линейных преобразований, где a * b + ... очень распространен. Когда я представил mul_add для скалярных произведений моей структуры AffineVector, точность потерянный.
Это метод скалярного произведения:
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());
}
panicked at 'assertion failed: `(left == right)`
left: `KVector3 { x: 1.0, y: 0.000000000000000023419586346110148, z: 0.0 }`,
right: `KVector3 { x: 1.0, y: 0.0, z: 0.0 }`',
Как и почему использование mul_add снизило точность? Как я могу использовать это эффективно?
что ошибки округления по количеству операций, задействованных в этом идеально компенсированном, казались крайне маловероятными
Как бы то ни было, есть очень несколько случаев, когда вычисления с плавающей запятой являются точными, и этот тест, похоже, не является одним из них. Если вы не ожидаете, что 0.1 + 0.2 будет соответствовать 0.3, вам не следует ожидать, что этот тест пройдет, кроме как по случайной случайности. (И если вы ожидаете 0.1 + 0.2 == 0.3, вам следует сначала прочитать этот другой вопрос.)
Предполагая, что оценка сложения выполняется слева направо, перекодирование изменило порядок суммы, что является высоким риском изменения округления.
Я собираюсь предложить закрыть это как обман Математика с плавающей запятой не работает?. Пока вы выполняете более сложные преобразования, ответы все еще применимы. Если вы действительно делаете что-то, где математика с плавающей запятой должна быть точной (только двоичные дроби и без округления), тогда это должно быть в вопросе.

Почему вы считаете, что «потеряли» точность? Кажется более вероятным, что раньше у вас были ошибки округления два, которые просто компенсировали друг друга. Теперь есть только одна ошибка, и вы ее видите.