你是否是感到很疑惑,浮點數的近似值到底是怎麼算出來的?浮點數的加法計算又是怎麼回事兒?在實踐應用中,咱們怎麼才用好浮點數呢?這一節,咱們就一塊兒來看這幾個問題java
實現這樣一個加法,也只須要位移。和整數加法相似的半加器和全加器的方法就可以實現,在電路層面,也並無引入太多新的複雜性。算法
你能夠試一下,我下面用一個簡單的Java程序,讓一個值爲2000萬的32位浮點數和1相加,你會發現,+1這個過程由於精度損失,被「徹底拋棄」了。
機器學習
public class FloatPrecision { public static void main(String[] args) { float a = 20000000.0f; float b = 1.0f; float c = a + b; System.out.println("c is " + c); float d = c - a; System.out.println("d is " + d); } }
對應的輸出結果就是:學習
c is 2.0E7 d is 0.0
那麼,咱們有沒有什麼辦法來解決這個精度丟失問題呢?雖然咱們在計算浮點數的時候,經常能夠容忍必定的精度損失,可是像上面那樣,
若是咱們連續加2000萬個1,2000萬的數值都會被精度損失丟掉了,就會影響咱們的計算結果。spa
咱們能夠作一個簡單的實驗,用一個循環相加2000萬個1.0f,最終的結果會是1600萬左右,而不是2000萬。這是由於,3d
加到1600萬以後的加法由於精度丟失都沒有了。這個代碼比起上面的使用2000萬來加1.0更具備現實意義。blog
public class FloatPrecision { public static void main(String[] args) { float sum = 0.0f; for (int i = 0; i < 20000000; i++) { float x = 1.0f; sum += x; } System.out.println("sum is " + sum); } }
對應的輸出結果是:ip
sum is 1.6777216E7
面對這個問題,聰明的計算機科學家們也想出了具體的解決辦法。他們發明了一種叫做Kahan Summation的算法來解決這個問題。ci
算法的對應代碼我也放在文稿中了。從中你能夠看到,一樣是2000萬個1.0f相加,用這種算法咱們獲得了準確的2000萬的結果數學
public class KahanSummation { public static void main(String[] args) { float sum = 0.0f; float c = 0.0f; for (int i = 0; i < 20000000; i++) { float x = 1.0f; float y = x - c; float t = sum + y; c = (t-sum)-y; sum = t; } System.out.println("sum is " + sum); } }
對應的輸出結果是:
sum is 1.6777216E7
其實這個算法的原理其實並不複雜,就是在每次的計算過程當中,都用一次減法,把當前加法計算中損失的精度記錄下來,而後在後面的循環中,把這個精度損失放在要加的小數上,再作一次運算。
若是你對這個背後的數學原理特別感興趣,能夠去看一看Wikipedia連接裏面對應的數學證實,也能夠生成一些數據試一試這個算法。這個方法在實際的數值計算中也是經常使用的,也是大量數據累加
中,解決浮點數精度帶來的「大數吃小數」問題的必備方案