2016-09-13 65 views
3

How does DoubleUtil.DoubleToInt(double val) work?我們學習了.NET Framework有舍入浮點值的一種特殊的方式:爲什麼使用這種尷尬的方式將一個浮點數舍入爲一個整數?

public static int DoubleToInt(double val) 
{ 
    return (0 < val) ? (int)(val + 0.5) : (int)(val - 0.5); 
} 

爲什麼他們不只是使用(int)Math.Round(val)

或者:爲什麼Math.Round這種方式沒有定義,如果這是優越的?必須有一些權衡。

+0

因爲'Math.Round'有重載處理多少個小數位來捨去和不同類型,將是我的猜測。 – juharr

回答

1

我可以看到這是一個優化,因爲要從Round獲得相同的行爲,您需要使用MidpointRounding.AwayFromZero選項。從reference source這是實現通過:

private static unsafe double InternalRound(double value, int digits, MidpointRounding mode) { 
    if (Abs(value) < doubleRoundLimit) { 
     Double power10 = roundPower10Double[digits]; 
     value *= power10; 
     if (mode == MidpointRounding.AwayFromZero) {     
      double fraction = SplitFractionDouble(&value); 
      if (Abs(fraction) >= 0.5d) { 
       value += Sign(fraction); 
      } 
     } 
     else { 
      // On X86 this can be inlined to just a few instructions 
      value = Round(value); 
     } 
     value /= power10; 
    } 
    return value; 
} 

我只能猜測,該實用方法的作者做了一些性能對比。

+0

我只是基準變化:圓形(雙)是140單位(但使用四捨五入銀行家無人期望),Round(double,ToEven)是310單位,DoubleToInt是42單位和(int)截斷是9單位。基準包括將結果添加到累加器以確保結果被消耗。所以它更快(但不正確)。 – usr

3

Math.Round將導致創建具有所需確切值的double,然後需要將其轉換爲int。這裏的代碼避免了創建那個double。它還允許錯誤處理的細節,以及與其他類型的舍入模式或數字相關的代碼四捨五入。

+0

這也會創建一箇中間雙精度(是'val + 0.5'或'val-0.5' –

+0

@SimonByrne不同之處在於,執行常數值的添加要比執行計算以找出最接近的數字要容易得多整數不是「double」值的創建,而是計算出這個double值是多少。 – Servy

+0

嗯,硬件不應該同時支持截斷和舍入?我不明白爲什麼會有一個成本差異可能是由驗證引起的成本和檢測到的數字輪到 – usr

2

它們在小數部分1/2處具有不同的行爲。根據Math.Round

如果a的小數部分是兩個整數之間的一半,其中一個是偶數,另一個是奇數,則返回偶數。

所以,如果val == 0.5,然後Math.Round(val) == 0.0,而這DoubleToInt會給(int)(0.5+0.5) == 1。換句話說,DoubleToInt離零點一圈1/2(類似標準C round函數)。

還有潛在這裏不太期望的行爲:如果val實際上是double之前 0.5(即0.49999999999999994)然後,取決於C#如何處理中間精密度,它實際上可能得​​到1-(如val + 0.5是不可由double表示,並可以四捨五入爲1)。事實上,這在Java 6(及更早版本)中是an infamous specification bug

+1

請注意,使用MidpointRounding.AwayFromZero選項可以通過重載獲得相同的行爲。 –

+0

http://stackoverflow.com/a/24348037/122718是的,似乎是不正確的。 – usr

相關問題