我知道浮點精度如何在常規情況下工作,但我偶然發現了我的C#代碼中的奇怪情況。爲什麼不同的C#中的浮點精度由派生詞分隔,並由語句分隔?
爲什麼result1和result2中的浮點值完全相同?
const float A; // Arbitrary value
const float B; // Arbitrary value
float result1 = (A*B)*dt;
float result2 = (A*B);
result2 *= dt;
從this page我計算浮動算術被左結合和,這意味着值進行評估,並在左到右的方式計算的。
完整的源代碼涉及XNA的四元數。我認爲這與我的常量以及VectorHelper.AddPitchRollYaw()的作用無關。測試通過就好如果我計算增量俯仰/翻滾/偏轉以相同的方式角度,但作爲代碼低於它不通過:
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");
}
}
誠然,誤差小並且僅在大量的迭代之後呈現自己,但它已經給我帶來了一些很好的揹負。
有趣的是,一位朋友提出這樣一個想法,即可能存在硬件指令直接採用兩次浮點乘法以提高精度,並且將兩個語句分開可防止編譯器使用此特殊指令? – angularsen 2010-03-22 10:12:43
@Andreas,AFAIK沒有x86指令來做到這一點。 – 2010-03-22 10:15:38
+1。我想補充一點,x86處理器的本地精度是80位的兩倍,x64的本地精度是64位的兩倍。通過允許編譯器在寄存器中保留部分計算,可以獲得最準確的結果。告訴編譯器將部分產品轉換回float _will_導致精度損失。 – 2010-03-22 10:30:00