關于這個筆記
也許你已經學會了一門計算機語言,也許你會寫一段不錯的程序。
也許你在嘈雜的環境用不費力的就可以用熟悉的IDE完成一個項目。
不過,你還在用+ - * /和%做所有的計算么?
這個筆記就是為了寫出更加正確的程序,小小的一個學習過程。
我不總結復雜的道理,只記錄用得上的方案。
出現的一切代碼都使用Java語言。
正確的計算過程導致錯誤的計算結果
關于基本數值計算中的一些陷阱
浮點數和實數
當早期的Fortran和algol創造出real這個關鍵字時,你還真以為那么是實數,不過說真的,你被騙了。
所以到了C,我們擁有了float。
那么他們到底有什么不同呢?
簡單地說
System.out.println(”Value of 1/3: ” + (1/3f))
將輸出
Value of 1/3: 0.33333334
很簡單,這是很早之前您就知道的四舍五入。當時我們只是那無法寫完的循環在紙上劃下一個”≈”,然后非常開心可以少寫幾個字符。如果你有一個負責任的數學老師,他(她)就會反復的叮嚀你,要小心,計算時候要保留結果精度往后一位的數值。
當這一切輪到了計算機,我們叫它:
舍入誤差
舍入誤差有時相當驚人。對1/3連加840次,你會得到279.9915(本應當為280),如何驚人,我們會在下面看到。
不過有意思的事情,和小時候的情形不一樣,舍入誤差并不是由于不能除盡或者是無理數造成的,類似于1/5的數字連加之后也有誤差。這是因為絕大多數
計算機是基于2進制體系的,所以IBM有用于商業計算的10進制計算機。簡單的事實是利用float表達的2的冪次,在精度范圍是沒有舍入誤差的。
給出常用定義:
作如下計算,你會期待怎樣的結果呢?
別偷懶,用你久遠的口算能力,很容易就可以得出,它本來應該是0。而事實上,我們偉大的計算機的答案是3222784.0
為什么?
因為,10000001/20000000的時候,答案本應該是0.50000005,由于舍入誤差,我們得到了0.5000006。
接下來我們減去0.5的時候,得到的應當是0.00000006,這樣,我們就丟失了所有的正確的數。
更糟糕的是,計算機中實際得到的值是5.9604645e-8。
因此,您的存款戶頭上可能會平白無故減少3222784元。只因為銀行決定向你支付利息之后,調整了匯率。
呃,這一次,我們叫它:
相消誤差——兩個非常相近的值相減時,如果消去了絕大多數有效數值,那么就會發生相消誤差。
舍入誤差——是精確值和其可表達值之間的差值。
如果還有什么要說的,那就是:
Double比float 更接近實數,貌似不需要我說。
浮點運算不遵守代數定理
比如:a = 1.0, b = 3.0e-8, c = 4.0e-8
那么: (a+b)+c = 1.0
a+(b+c) = 1.0000001
不過加法和乘法還是滿足交換律的。
下一次:輪到了整數。