2008-11-27 63 views
0

標準化Vector3對象的兩種方法;通過從頭歸調用Vector3.Normalize()和其他:Microsoft.DirectX.Vector3.Normalize()不一致

class Tester { 
    static Vector3 NormalizeVector(Vector3 v) 
    { 
     float l = v.Length(); 
     return new Vector3(v.X/l, v.Y/l, v.Z/l); 
    } 

    public static void Main(string[] args) 
    { 
     Vector3 v = new Vector3(0.0f, 0.0f, 7.0f); 
     Vector3 v2 = NormalizeVector(v); 
     Debug.WriteLine(v2.ToString()); 
     v.Normalize(); 
     Debug.WriteLine(v.ToString()); 
    } 
} 

上述代碼產生這樣的:

X: 0 
Y: 0 
Z: 1 

X: 0 
Y: 0 
Z: 0.9999999 

爲什麼呢?

(積分:爲什麼是我?)

+0

這很容易笑,但你能生產出更好的版本嗎? – shoosh 2008-11-27 08:45:18

+0

我已經提供了一個更好的版本。再次閱讀。 – 2008-11-27 08:53:43

回答

0

不關心這一點。使用浮點數時總會有一些錯誤。如果你很好奇,嘗試改變一倍,看看是否仍然發生。

0

您在這裏有關於浮點數字符串格式化的interesting discussion

僅供參考:

你的數量需要24位表示,這意味着使用的是一個浮子(23bits + 1隱含位)的整尾數。 Single.ToString()最終由一個本地函數實現,所以我不能確定發生了什麼,但我的猜測是它使用最後一位數字來捨去整個尾數。
這背後的原因可能是你經常得到的數字不能完全用二進制表示,所以你會得到一個長尾數;舉例來說,0.01是內部表示爲0.00999 ...,你可以通過書面看到:

float f = 0.01f; 
Console.WriteLine ("{0:G}", f); 
Console.WriteLine ("{0:G}", (double) f); 

通過在第七位四捨五入,你會得到「0.01」,這是你會有預料。

對於上面所看到的,只有7位數字的數字不會顯示此問題,正如您已經看到的。

要清楚的是:舍入僅在將數字轉換爲字符串時發生:如果有計算,則計算將使用所有可用位。

浮點數的外部精度爲7位數(內部爲9位),所以如果超出這個範圍,那麼四捨五入(帶有潛在的怪癖)是自動的。
如果你將浮點數降到7位數(例如,左邊1,右邊6),那麼它將工作,並且字符串轉換也將如此。

至於獎勵積分:

爲什麼?因爲這個代碼「急於打擊你」。
(火神... ...吹好的。 最爛。 踢。 永遠)

0

使用浮點數時,你應該想到這一點,基本原因是計算機程序二進制這並不地圖精確到十進制。

對於不同基礎之間問題的直觀示例,考慮分數1/3。它不能完全用十進制表示(它是0.333333 .....),但可以在Terniary(如0.1)中表示。

一般來說,這些問題對於雙打來說並不那麼明顯,其代價是計算成本(操縱位數的兩倍)。然而,考慮到浮動水平的精確度足以讓人去月球,那麼你真的不應該迷戀:-)

這些問題是一種計算機理論101(而不是編程101 - 它你顯然已經遠遠超過了),如果你朝着Direct X代碼的方向前進,那麼我會建議你拿一本基本的計算機理論書並快速閱讀它,這可能是個好主意。

2

看看他們是如何實現它(例如在asm中)。

也許他們想更快,產生類似:

l = 1/v.length(); 
return new Vector3(v.X * l, v.Y * l, v.Z * l); 

交易2個師對3個乘法(因爲他們認爲mults比div的速度(這是現代的FPU最常無效)) 。這引入了更多的操作,所以精度更低。

這會被經常引用的「不成熟的優化」。

0

如果你的代碼被細分浮點舍入錯誤破壞,那麼恐怕你需要修復它,因爲它們只是生活中的一個事實。