Как возникает дополнительная ошибка округления при использовании f64 :: mul_add?

Согласно документации, f64::mul_add может использоваться для уменьшения количества возможностей для ошибок округления:

pub fn mul_add(self, a: f64, b: f64) -> f64

Fused multiply-add. Computes (self * a) + b with 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 снизило точность? Как я могу использовать это эффективно?

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

Shepmaster 19.05.2018 00:14

что ошибки округления по количеству операций, задействованных в этом идеально компенсированном, казались крайне маловероятными

Kelson Ball 19.05.2018 00:16

Как бы то ни было, есть очень несколько случаев, когда вычисления с плавающей запятой являются точными, и этот тест, похоже, не является одним из них. Если вы не ожидаете, что 0.1 + 0.2 будет соответствовать 0.3, вам не следует ожидать, что этот тест пройдет, кроме как по случайной случайности. (И если вы ожидаете 0.1 + 0.2 == 0.3, вам следует сначала прочитать этот другой вопрос.)

trentcl 19.05.2018 00:48

Предполагая, что оценка сложения выполняется слева направо, перекодирование изменило порядок суммы, что является высоким риском изменения округления.

Patricia Shanahan 19.05.2018 01:35

Я собираюсь предложить закрыть это как обман Математика с плавающей запятой не работает?. Пока вы выполняете более сложные преобразования, ответы все еще применимы. Если вы действительно делаете что-то, где математика с плавающей запятой должна быть точной (только двоичные дроби и без округления), тогда это должно быть в вопросе.

trentcl 23.05.2018 15:37
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
3
5
182
0

Другие вопросы по теме