Why differs floating-point precision in C# when separated by parantheses and when separated by state
- by Andreas Larsen
I am aware of how floating point precision works in the regular cases, but I stumbled on an odd situation in my C# code.
Why aren't result1 and result2 the exact same floating point value here?
const float A; // Arbitrary value
const float B; // Arbitrary value
float result1 = (A*B)*dt;
float result2 = (A*B);
result2 *= dt;
From this page I figured float arithmetic was left-associative and that this means values are evaluated and calculated in a left-to-right manner.
The full source code involves XNA's Quaternions. I don't think it's relevant what my constants are and what the VectorHelper.AddPitchRollYaw() does. The test passes just fine if I calculate the delta pitch/roll/yaw angles in the same manner, but as the code is below it does not pass:
X
Expected: 0.275153548f
But was: 0.275153786f
[TestFixture]
internal class QuaternionPrecisionTest
{
[Test]
public void Test()
{
JoystickInput input;
input.Pitch = 0.312312432f;
input.Roll = 0.512312432f;
input.Yaw = 0.912312432f;
const float dt = 0.017001f;
float pitchRate = input.Pitch * PhysicsConstants.MaxPitchRate;
float rollRate = input.Roll * PhysicsConstants.MaxRollRate;
float yawRate = input.Yaw * PhysicsConstants.MaxYawRate;
Quaternion orient1 = Quaternion.Identity;
Quaternion orient2 = Quaternion.Identity;
for (int i = 0; i < 10000; i++)
{
float deltaPitch =
(input.Pitch * PhysicsConstants.MaxPitchRate) * dt;
float deltaRoll =
(input.Roll * PhysicsConstants.MaxRollRate) * dt;
float deltaYaw =
(input.Yaw * PhysicsConstants.MaxYawRate) * dt;
// Add deltas of pitch, roll and yaw to the rotation matrix
orient1 = VectorHelper.AddPitchRollYaw(
orient1, deltaPitch, deltaRoll, deltaYaw);
deltaPitch = pitchRate * dt;
deltaRoll = rollRate * dt;
deltaYaw = yawRate * dt;
orient2 = VectorHelper.AddPitchRollYaw(
orient2, deltaPitch, deltaRoll, deltaYaw);
}
Assert.AreEqual(orient1.X, orient2.X, "X");
Assert.AreEqual(orient1.Y, orient2.Y, "Y");
Assert.AreEqual(orient1.Z, orient2.Z, "Z");
Assert.AreEqual(orient1.W, orient2.W, "W");
}
}
Granted, the error is small and only presents itself after a large number of iterations, but it has caused me some great headackes.