2012-05-30 43 views
3

我創建了一個應用程序來計算64位範圍內的素數,所以當我試圖使用sqrt函數從math.h計算64位數的平方根時我發現答案並不準確例如,當輸入爲~0ull答案應該是~0u但一個我得到的是0x100000000這是不正確的,所以我決定創建我自己的版本使用匯編x86的語言,看看這是一個錯誤,這是我的功能:這是一個在sqrt函數中的錯誤

inline unsigned prime_isqrt(unsigned long long value) 
{ 
    const unsigned one = 1; 
    const unsigned two = 2; 

    __asm 
    { 
     test dword ptr [value+4], 0x80000000 
     jz ZERO 
     mov eax, dword ptr [value] 
     mov ecx, dword ptr [value + 4] 

     shrd eax, ecx, 1 
     shr ecx, 1 
     mov dword ptr [value],eax 
     mov dword ptr [value+4],ecx 

     fild value 
     fimul two 
     fiadd one 
     jmp REST 
ZERO: 
     fild value 
REST: 
     fsqrt 
     fisttp value 
     mov eax, dword ptr [value] 
    } 
} 

輸入是奇數,以獲取其平方根。當我測試我的功能相同的輸入結果是一樣的。

什麼,我不明白的是爲什麼圓的結果,或者這些功能要具體爲什麼sqrt指令一輪結果如何呢?

回答

8

sqrt不圓東西 - 當您轉換您的整數成雙你怎麼做。雙精度不能代表64位整數可以不失精度的所有數字。具體地從2 開始,有多個整數將被表示爲相同的double值。

因此,如果您將上述2 整數翻一番,你失去了一些最顯著位的,這就是爲什麼(double)(~0ull)是18446744073709552000.0,不18446744073709551615.0(或者更精確地說,後者實際上是等於前因爲它們代表相同的雙數)。

+0

謝謝你,所以我可以用低32位計算它的平方根 - 稱之爲(a) - 然後從64位數中減去低32位並得到該數的平方根 - 稱之爲(b) - 現在全部我需要做的就是(a)乘以(b),我們就完成了。 –

+1

@Muhammadalaa:呃,沒有。 64位可以用數學方式寫成(H * 2^32 + L),其中H和L各爲32位。你聲明'sqrt(H * 2^32 + L)'='sqrt(L)* sqrt(H * 2^32)'。事實並非如此。但是,您可以將其計算爲sqrt(H)* sqrt(2^32 + L/H)'。 – MSalters

+0

舍入會給你一個大約2^-53的錯誤,但是這裏的錯誤是一個因子2:'〜0u == UINT_MAX'與'0x80000000 = UINT_MAX/2'。我不認爲這解釋了它。 – MSalters

0

你是不是C++的功能,我們在調用很清楚。 sqrt是一個重載名稱。你可能想要sqrt(double(~0ull))。沒有sqrt超載,需要unsigned long long

+2

他意識到這一點。他明確地將數字轉換爲他的代碼中的雙倍數。 – sepp2k