title: How is additional rounding error introduced when using f64::mul_add?
body: <p>According to the docs, <a href="https://doc.rust-lang.org/std/primitive.f64.html#method.mul_add" rel="nofollow noreferrer"><code>f64::mul_add</code></a> can be used to reduce the number of opportunities for rounding errors:</p>
  
  <blockquote>
  <pre><code>pub fn mul_add(self, a: f64, b: f64) -&gt; f64
  </code></pre>
    
    <p>Fused multiply-add. Computes <code>(self * a) + b</code> with only one rounding
    error.  This produces a more accurate result with better performance
    than a separate multiplication operation followed by an add.</p>
  </blockquote>
  
  <p>I am working on a linear transforms library where <code>a * b + ...</code> is very common. When I introduced <code>mul_add</code> for the dot products of my <code>AffineVector</code> struct I <strong>lost</strong> precision.</p>
  
  <p>This is the dot product method:</p>
  
  <pre><code>impl AffineVector {
      pub fn dot(self, v: AffineVector) -&gt; 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)))
      }
  }
  </code></pre>
  
  <p><a href="https://github.com/KelsonBall/Rust.Positron.Transforms/tree/master/src" rel="nofollow noreferrer">complete source here</a></p>
  
  <p>With the <code>mul_add</code> implementation <strong>and no other changes</strong>, the following test fails because of a floating point precision issue on the last assert:</p>
  
  <pre><code>#[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 &lt;1,0,0&gt;
      let rotated = rotate.apply_vec3(KVector3::i_hat());        
  
      // assert that the result is &lt;cos(1),sin(1),0&gt;
      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 &lt;1,0,0&gt;   
      assert_eq!(returned, KVector3::i_hat());
  }
  </code></pre>
  
  <pre class="lang-none prettyprint-override"><code>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 }`',
  </code></pre>
  
  <p>How and why did using <code>mul_add</code> decrease the precision? How can I use it effectively?</p>