2012-01-19 52 views
8

在一款非常高性能的應用程序中,我們發現CPU可以計算長計算的速度明顯快於雙精度。但是,在我們的系統中,確定我們從不需要超過9位小數位的精度。所以我們對所有浮點運算都使用long,並且理解了9點精度。將尾數和指數轉換爲雙精度值

但是,在系統的某些部分,由於可讀性與雙打一起工作,因此更方便。所以我們必須將假設9位小數的長整數值轉換爲雙整數值。

我們發現簡單地取長整數除以10的冪乘以9或乘以1除以10的冪後得到不精確的表示。

爲了解決這個問題,我們使用Math.Round(value,9)來給出精確的值。

但是,Math.Round()的表現可怕的很慢。

因此,我們現在的想法是直接將尾數和指數轉換爲雙精度的二進制格式 - 這樣就不需要四捨五入。

我們已經在網上了解了如何檢查雙精度的位來得到尾數和指數,但是弄清楚如何通過使用這些位找出尾數和指數並且編制雙精度來反轉。

有什麼建議嗎?

[Test] 
public unsafe void ChangeBitsInDouble() 
{ 
    var original = 1.0D; 
    long bits; 
    double* dptr = &original; 
    //bits = *(long*) dptr; 
    bits = BitConverter.DoubleToInt64Bits(original); 
    var negative = (bits < 0); 
    var exponent = (int) ((bits >> 52) & 0x7ffL); 
    var mantissa = bits & 0xfffffffffffffL; 
    if(exponent == 0) 
    { 
     exponent++; 
    } 
    else 
    { 
     mantissa = mantissa | (1L << 52); 
    } 
    exponent -= 1075; 

    if(mantissa == 0) 
    { 
     return; 
    } 

    while ((mantissa & 1) == 0) 
    { 
     mantissa >>= 1; 
     exponent++; 
    } 

    Console.WriteLine("Mantissa " + mantissa + ", exponent " + exponent); 

} 
+3

您確定您擁有的價值完全可以用double來表示嗎? – Justin

+0

也許這將有所幫助,我不想全部閱讀它只是爲了幫助您:P http://steve.hollasch.net/cgindex/coding/ieeefloat.html – MrFox

回答

1

您不應該使用10^9的比例因子,而應該使用2^30來代替。

+0

Thansk我們只是意識到上面的代碼不是'完成。並且學習以位表示的雙指數使用二進制指數而不是十進制指數。所以你的回答讓世界變得有意義。 – Wayne

+0

這會使長時間表示無法讀取,但會大大提高性能,並且轉換回雙倍會快速減輕。所以這是一個很好的折衷。謝謝! – Wayne

+0

好吧,這似乎是合乎邏輯的,但在測試後,除以因子轉換回來雙倍它出來不精確:var convert = 1 << 30; 雙重價格= 45.454945768D; long result1 =(long)(price * convert); double result2 =((double)result1)/ convert; Assert.AreEqual(result2,price); – Wayne

0

正如你已經意識到,根據其他答案,雙工工作的浮點二進制而不是浮點小數,因此初始方法不起作用。

目前還不清楚它是否可以使用故意簡化的公式,因爲目前尚不清楚您需要的最大範圍是什麼,所以舍入變得不可避免。

這樣做很快,但精確的問題已得到充分研究,並且通常由CPU指令支持。您唯一可能擊敗內置轉換的機會是:

  1. 您碰到一個數學突破,值得一些嚴肅的論文被寫入。
  2. 您排除了不會在您自己的示例中出現的足夠情況,而內置插件通常更適合您自己的使用。

除非您使用的值範圍非常有限,否則雙精度IEEE 754與長整數之間轉換的快速切換的可能性會越來越小。

如果您現在需要覆蓋IEEE 754涵蓋的大部分案例,或者相當大一部分案例,那麼您最終會讓事情變得更慢。

我建議要麼保持與你有什麼,移動的情況下,double更便利,儘管不方便仍然堅持,或者如果有必要使用decimal。您可以從long創建decimal輕鬆地:

private static decimal DivideByBillion (long l) 
{ 
    if(l >= 0) 
    return new decimal((int)(l & 0xFFFFFFFF), (int)(uint)(l >> 32), 0, false, 9); 
    l = -l; 
    return new decimal((int)(l & 0xFFFFFFFF), (int)(uint)(l >> 32), 0, true, 9); 
} 

現在,decimal是幅度較慢的算術使用比double(恰恰是因爲它實現了在開放的問題與你類似的做法,但有不同的指數和更大的尾數)。但是,如果您只需要一種方便的方式來獲取顯示值或呈現字符串的值,那麼手動轉換爲decimal比手動轉換爲double具有優勢,因此可能是值得關注的。

+0

在使用雙打的代碼中,性能仍然非常重要。這只是它們是用戶插件,用戶期望用戶使用浮點數。使用小數對於用戶來說當然是實用的,但對於他們所做的任何事情的表現令人失望。由於我們所有的輸入都來自雙打(從字符串或雙打錄製爲二進制),所以我正在考慮提取尾數......用數學做...然後簡單地替換輸出的尾數。這將有希望避免四捨五入和缺乏精確性。 – Wayne