2013-02-15 47 views
4

我有以下代碼行。浮點計算給出不同的結果與浮動比雙倍

hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0 - hero->getDefensePercent())); 
  • void onBeingHit(int decHP)方法接受整數和更新健康點。
  • float getDefensePercent()方法是返回英雄防禦百分比的getter方法。
  • ENEMY_ATTACK_POINT是定義爲#define ENEMY_ATTACK_POINT 20的宏常數因子。

比方說hero->getDefensePercent()返回0.1。因此,計算

20 * (1.0 - 0.1) = 20 * (0.9) = 18 

每當我用下面的代碼試了一下(不f追加1.0

hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0 - hero->getDefensePercent())); 

我。

但對於下面的代碼(1.0f附加)

hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0f - hero->getDefensePercent())); 

我。

這是怎麼回事?儘管hero->getDefensePercent()已經浮動,但是f有意義嗎?

+1

您不能保存'.9'或'.1'準確(浮點數據類型)。可能是雙精度較低會導致類似'18.00x'而不是'17.999xxx'。請注意,'浮點 - > int'總是停下來。 – Zeta 2013-02-15 07:55:17

+0

不太瞭解C++,但我期望你的文字被解釋爲'double',從而迫使計算以'double'而不是'float'來完成。 – 2013-02-15 07:57:00

+0

'ENEMY_ATTACK_POINT'是一個整數。由於您使用整數數學,因此您會遇到四捨五入錯誤。 – Pubby 2013-02-15 07:58:00

回答

10

發生了什麼事?在兩種情況下,爲什麼不是整數結果18

的問題是,浮點表達式的結果向零取整時被轉換爲整數值(在這兩種情況下)。

0.1不能精確表示爲浮點值(在兩種情況下)。編譯器將轉換爲二進制IEEE754浮點數,並決定是向上舍入還是向下舍入到可表示值。處理器然後在運行時將該值相乘,並將結果四捨五入以獲得整數值。

好了,但因爲這兩個doublefloat那樣做的,爲什麼會在兩種情況下一個18,但在其他情況下17?我很困惑。

你的代碼採用的功能,0.1f(浮子)的結果,然後計算20 * (1.0 - 0.1f)這是一個雙表達,而20 * (1.0f - 0.1f)是float表達。現在浮版本碰巧大於18.0稍大,被四捨五入至18,而雙表現略小於18.0和被四捨五入到17

如果您不確切知道IEEE754二進制浮點數是如何由十進制數構成的,那麼它幾乎是隨機的,如果它稍微小於或稍大於您在代碼中輸入的十進制數。所以你不應該指望這一點。不要試圖通過附加f的數字之一來解決這樣的問題,並說:「現在的作品,所以我離開這個f存在」,因爲另一個不同的值再次表現。

爲什麼取決於表達對這個f的precence類型?

這是因爲C和C++中的浮點文字的默認類型爲double。如果你添加f,這是一個浮動。浮點epxression的結果是「更大」的類型。 double表達式和整數的結果仍然是一個雙重表達式,以及int和float將是一個float。所以你的表達結果是一個浮點數或一個雙精度值。

好,但我不希望舍入到零。我想四捨五入到最近的數字。

要解決這個問題,一個半將其轉換成一個整數之前添加到結果:

hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0 - hero->getDefensePercent()) + 0.5); 

在C++ 11,有std::round()爲該。在以前版本的標準中,there was no such function to round to the nearest integer.(請參閱註釋以瞭解詳細信息。)

如果您沒有std::round,可以自己寫。處理負數時要小心。當轉換爲整數時,數字將爲截斷(四捨五入爲零),這意味着負值將向上舍入,而不是向下舍入。因此,我們必須一半,如果數字爲負:

int round(double x) { 
    return (x < 0.0) ? (x - .5) : (x + .5); 
} 
+0

感謝您的洞察解釋。 – haxpor 2013-02-15 08:10:34

+0

@haxpor很高興能幫到你。請參閱我的編輯。 – leemes 2013-02-15 08:13:09

+3

您不必添加'0.5',或編寫自己的函數。只需使用'std :: round()'(在''中)。一般來說,我建議你總是使用標準函數('round','floor','ciel'或'trunc');這使你的意圖清晰(並讓你思考你實際想要做什麼)。 – 2013-02-15 08:50:01

4

1.0被解釋爲double,而不是1.0f,這被編譯器視爲float。

f後綴只是告訴編譯器哪個是浮點數,哪個是雙精度浮點數。

顧名思義,雙精度浮點數的精度爲2倍。一般而言,雙具有精度15〜16位十進制數字,而浮動只有7

這種精度損失可能導致截斷誤差更容易上浮

See MSDN (C++)

+3

'double'至少和'float'一樣精確。是否具有相同的,少於兩次,兩次或兩次以上的是實現定義的。在通常的IEEE格式中,'double'具有52位精度,'float'具有24位,'double' 53,所以'double'具有兩倍以上的精度。但是有些處理器(並不是大多數C++程序員都會看到它們),其中'double'和'float'是相同的。 (還要注意的是,在大多數大型機上,機器浮點不是二進制的,而是以16爲基數或基數爲8)。 – 2013-02-15 08:55:29

+0

另外(再次選擇nits ---答案確實沒有問題):小數精度的含義是不同的。對於往返(轉換爲十進制和後退,保證獲得相同的值),使用IEEE時,需要9('float')或17('double')數字。或者,你可以考慮在其他方向,這給6和15(這些對應於''中numeric_limits' digits10'和'max_digits10'。) – 2013-02-15 09:08:25

+0

那好吧:)我喜歡新的相關信息:) – 2013-02-15 10:40:40

4

之所以是這樣的當使用double時,發生更準確的結果,即1.0

試圖把你的結果,轉換後,這將導致更精確的積分結果:

hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0 - hero->getDefensePercent()) + 0.5); 

注意,到時候加入0.5並截斷爲int之後就會造成結果四捨五入,所以你的結果將是17.999...,它將變成18.499...,它將被截斷爲18