2010-03-28 149 views
17

這個問題。C++浮點精度損失:3015/0.00025298219406977296

Microsoft Visual C++ 2005編譯器,32位windows xp sp3,amd 64 x2 cpu。

代碼:

double a = 3015.0; 
double b = 0.00025298219406977296; 
//*((unsigned __int64*)(&a)) == 0x40a78e0000000000 
//*((unsigned __int64*)(&b)) == 0x3f30945640000000 
double f = a/b;//3015/0.00025298219406977296; 

計算的結果(即 「F」)是11917835.000000000(((無符號__int64)(& F))== 0x4166bb4160000000)儘管它應該是11917834.814763514(即((unsigned __int64)(& f))== 0x4166bb415a128aef)。
I.e.小數部分丟失。
不幸的是,我需要小數部分是正確的。

問題:
1)爲什麼會發生這種情況?
2)我該如何解決這個問題?

附加信息:
0)的結果被直接「手錶」窗口(這是不打印,我沒有忘記設置打印精度)。我也提供了浮點變量的十六進制轉儲,所以我對計算結果非常肯定。
1)F = A/B的拆卸是:

fld   qword ptr [a] 
fdiv  qword ptr [b] 
fstp  qword ptr [f] 

2)F = 3015/0.00025298219406977296;產生正確的結果(F == 11917834.814763514,((無符號__int64)(& F))== 0x4166bb415a128aef),但它看起來像在這種情況下,結果在編譯時簡單地計算:

fld   qword ptr [[email protected] (828EA0h)] 
fstp  qword ptr [f] 

所以,我該如何解決這個問題?

P.S.我找到了一個臨時解決方法(我只需要除法的小數部分,所以我現在簡單地使用f = fmod(a/b)/ b),但我仍然想知道如何正確解決這個問題 - double精度應該是十六進制數字,所以這樣的計算不應該導致問題。

回答

15

你在你的程序中使用DirectX的任何地方,導致浮點單元獲得切換爲單精度模式,除非你明確告訴它不是在創建設備,並會造成正是這種

+1

這是一個正確的答案。程序使用Direct3D,當然,計算髮生在設備創建後。有趣的是,我知道D3D調整FPU精度,但我完全忘了它,因爲我在過去幾年沒有看到這個錯誤。 問題解決。 – SigTerm 2010-03-28 19:24:48

+1

創建設備時應該使用哪個標誌? Direct2D存在同樣的問題嗎? – dalle 2010-08-04 16:22:19

1

我想你會打印出數字而不指定精度。試試這個:

#include <iostream> 
#include <iomanip> 

int main() { 
    double a = 3015.0; 
    double b = 0.00025298219406977296; 
    double f = a/b; 

    std::cout << std::fixed << std::setprecision(15) << f << std::endl; 
    return 0; 
} 

這將產生:

11917834.814763514000000

它看起來正確的給我。我使用的是VC++ 2008而不是2005,但我猜想它們的區別在於你的代碼,而不是編譯器。

+0

不,我不打印號碼,結果是直接從「觀察」窗口中取出。 – SigTerm 2010-03-28 17:14:38

+0

您是否嘗試過打印它?也許這個bug是在看窗口! – 2010-03-28 17:29:49

+0

@Martin監視窗口顯示完整的精度。 – 2010-03-28 17:37:55

4

有趣的是,如果你聲明a和b都是浮點數,那麼你將得到正好11917835.000000000。所以我的猜測是,在某個地方發生了單精度轉換,無論是常量的解釋方式還是以後的計算方式。

但是,考慮到代碼的簡單性,任何一種情況都有點令人驚訝。你沒有使用任何異乎尋常的編譯器指令,強制所有浮點數的單精度?

編輯:您是否確實已確認編譯的程序生成不正確的結果?否則,(錯誤的)單精度轉換的最可能候選者將是調試器。

+0

反彙編清晰顯示,沒有單一的精度。 – 2010-03-28 17:52:51

+0

無論如何,不​​在這三條線上。 – 2010-03-28 17:58:06

2

如果你需要精確的數學,不要使用浮點數。

幫你一個忙,並得到一個BigNum庫有理性的數字支持。

+3

他不需要11917834.814763514100059144562708,他只需要11917834.814763514。爲了獲得機器內置的精確度而放棄性能和內存量級似乎有點不合理(赦免雙關語)。 – Gabe 2010-03-28 17:58:27

+0

當然,我們無權期待正確性,但我們仍有權要求浮點規範保證我們的正確性。 – AakashM 2010-03-28 18:15:57

+0

沒有冒犯性,但我認爲僅僅爲一次計算使用bignum就有點過分了,至少在這種情況下。 – SigTerm 2010-03-28 20:03:24

0

您確定您正在檢查fstp指令之後的f值嗎?如果你已經開啓了優化,那麼觀察窗口可能會顯示稍後的值(這似乎有點合理,因爲你說你稍後在看f的小數部分 - 是否有一些指令會掩蓋它出不知何故?)