2014-05-21 29 views
3

我必須從文本文件中讀取一些數據(精度爲double)。有時,當從string轉換爲doubleatof時,我會得到奇怪的結果。以下是演示該問題的代碼片段 - 請注意,我使用的是GNU C++ 4.8.1編譯器。代碼簡單地打印出-0.1和0之間的數字,步長爲0.01;所以只有,預計9個數字。在C++中將字符串轉換爲雙精度的奇異性

#include <iostream> 
    #include <cstdlib> 

    int main() { 
    int y; 
    double a, b = 0.0, x = 1e-2, z; 

    double a_string = atof("-0.1"); 
    double a_inline = -0.1; 

    std::cout << "Inline:: "; 
    a = a_inline; 
    for (y = 1 ; (z = a + y * x) < b; ++y){ 
    std::cout << y << ": " << z << " | "; 
    } 

    std::cout << "\nString:: "; 
    a = a_string; 
    for (y = 1 ; (z = a + y * x) < b; ++y){ 
    std::cout << y << ": " << z << " | "; 
    } 

    return 0; 
    } 

結果是

Inline:: 1: -0.09 | 2: -0.08 | 3: -0.07 | 4: -0.06 | 5: -0.05 | 6: -0.04 | 7: -0.03 | 8: -0.02 | 9: -0.01 | 
    String:: 1: -0.09 | 2: -0.08 | 3: -0.07 | 4: -0.06 | 5: -0.05 | 6: -0.04 | 7: -0.03 | 8: -0.02 | 9: -0.01 | 10: -3.46945e-18 | 

注意,基於字符串賦值for環(a_string)運行一次比基於內嵌分配(a_inline)環路更多 - 注意最後號碼-3.46945e-18。閱讀了其他相關的帖子後,我仍然無法弄清楚爲什麼會發生這種情況。

+1

注意:它在我的鑽機上表現得如預期(每個循環9個值)。 (Apple LLVM版本5.1(clang-503.0.40)(基於LLVM 3.4svn)),編譯爲x64。有趣的是,不同的結果來自[** Coliru **](http://coliru.stacked-crooked.com/a/c918637b84884a8d)與[** ideone.com **](https://ideone.com/ LO4yrb)。 – WhozCraig

+0

它也適用於我,GNU C++ 4.3.2,x64 – blackmesa

+0

在我看來,atof(「 - 0.1」)必須作爲字符串解析器工作,所以它不能像從-0.1獲得-0.1值一樣工作「字符串,它通過將某些值相加並將其乘以-1.0(在您的情況下爲-1.0 * 0.1)來獲得最終值。我認爲這種增加是原因。在它之後,你的字符串值將會像-0.100000000000001一樣。 –

回答

2

你的代碼達...

std::atof("-0.1") - 10 * 1e-2 

...不是正好等於0這就是生活,實數表示,這是能夠編碼在其範圍內傳播點的有限數量的僅僅是近似值,並且生成/處理它們的某些函數可能會在最後一位數字或兩位數字中引入錯誤,即使是對於數字而言,也可能會發生錯誤,而不是恰好可以完美表示。在你的情況下,-0.1不能完美表示。爲了得到這個感覺,在this online IEEE 754 Converter的「Decimal Representation」文本框中輸入「-0.1」,你會發現在32位float s中可能的二進制表示只是近似的...在這種情況下,它是平等的真正的可能在64位double s ...你只是有一個更好的近似值。

通常的建議是去閱讀What Every Computer Scientist Should Know About Floating Point Values

什麼是數學「X - X」可能不是0時每個x的計算已經做不同或在不同的時間,以及它到底是如何平移出可以改變與優化級別,周圍的代碼,硬件,編譯器等。這是一個藝術形式設計代碼,處理這些問題的優雅。在你的情況下,如果你需要9次循環,那麼計數到9,如果你想在比較中有一些容差,那麼在比較中寫一些允許誤差小的值(一個「epsilon」值)。您還可以使用函數將浮點數一次一位地反轉到下一個可表示的值,這樣可以避免縮放epsilon值時出現問題,因此它對應於最後一位有效數字或者具有特定指數的兩位數,但這有點一個遊戲,因爲很難知道在產生價值時積累了多少表示和舍入錯誤。

4

二進制浮點數通常不是十進制小數的精確表示。不同計算結果或文字計算結果的直接比較必然會不直觀地失敗。比較時必須使用間隔。這個問題與atof無關。

相關問題