一 定點數
實數怎麼表示,能用二進制表示所有的實數嘛,開始是用BCD編碼來表示
BCD編碼
用 4 個比特來表示 0~9 的整數,那麼 32 個比特就可以表示 8 個這樣的整數。然後我們把最右邊的 2 個 0~9 的整數,當成小數部分;把左邊 6 個 0~9 的整數,當成整數部分。這樣,我們就可以用 32 個比特,來表示從 0 到 999999.99 這樣 1 億個實數了。
BCD編碼缺點
1.此編碼表示的數不夠
32bit本來可以表示40億個不同的數,但在BCD編碼下,只能表示一億的數
2.這樣的表示方式沒辦法同時表示很大的數和很小的數
二 浮點數
用科學計數法表示實數,IEEE的標準有兩種表示浮點數的方式,單精度浮點數float或float32類型的和雙進度浮點數double或double64類型的
浮點數不管是表示還是計算,都是其近似值
單精度類型
單精度的32個比特可以分成三個部分
第一部分是一個符號位,用來表示正數還是負數,用字母s來表示
第二部分是8比特組成的指數位,用e表示,8比特表示的整數空間是0~255,用 1~254 映射到 -126~127 這 254 個有正有負的數上,這裏0和255表示一些特殊的數
第三部分是23比特組成有效位數,用f表示
浮點數的表示如下圖所示:
浮點數中表示特殊值的一些規定
以0.5爲例,可以表示成如下形式:
0.5=(−1)0×1.0×2(−1)=0.5
三 浮點數的加法和精度損失
兩個浮點數的指數可能是不一樣的,怎麼進行加法了,首先要把指數位調整成一樣,然後計算有效位就好了
例如0.5,表示成浮點數,對應的指數位是 -1,有效位是 00。0.125 表示成浮點數,對應的指數位是 -3,有效位也還是 00。
這兩個數相加時,首先把0.125的指數位-3更改爲-1,對應的有效位 1.00…也要對應右移兩位,因爲 f 前面有一個默認的 1,所以就會變成 0.01,計算兩者相加的有效位1.f,變成了有效位1.01,指數位是-1
從上面可以看出,對於指數較小的數,相加時,有效位需要右移,在右移的過程中,最右側的有效位就被丟棄掉了。這會導致對應的指數位較小的數,在加法發生之前,就丟失精度。兩個相加數的指數位差的越大,位移的位數越大,可能丟失的精度也就越大。
如下程序
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
可以看到,精度損失了,下面程序的精度損失更嚴重
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);
}
結果
sum is 1.6777216E7
Kahan Summation 算法
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 2.0E7
參考資料