浮點數精度問題

2021-08-16 17:21:09 字數 2507 閱讀 1381

一、例子

首先我們去編譯器試試  double a=1.9;通過新增監視檢視a的值

會發現a的值是1.8999999…

二、開始今天的學習

在最開始學c++的時候並沒有對浮點數進行很深入的學習,認為浮點不就是小數嘛,首先在c++的巨集裡面有

flt_max 和

flt_min

的定義,float是四位元組的浮點數,後面我麼會發現,四位元組的浮點數有效數字為6-7位,能保證的是6位,與浮點數的個數相比,四位元組能表示的浮點數簡直微不足道,也就是說存在小數根本就不能按照浮點數的這一套標準來進行「完美」儲存,用有限的位元組數去儲存無限的浮點數,因此就會發生精度丟失。

c/c++採用的是ieee浮點標準,它以「二進位制的科學表示法」表示乙個小數:

其中m是乙個整數部分僅有一位的二進位制小數,例如1.011,表示十進位制下的1.375。e表示該小數以2為底時的階數。基於以上的表示方式,小數需要對三部分進行編碼:表示符號的s及階碼e尾數碼m。c++中的double型別三種編碼所佔的位數如圖所示。

現在讓我們按照ieee浮點數表示法,一步步的將float型浮點數12345.0f轉換為十六進製制**。在處理這種不帶小數的浮點數時,直接將整數部轉化為二進位制表示:

1 11100010 01000000

也可以這樣表示:

11110001001000000.0

然後將小數點向左移,一直移到離最高位只有1位,就是最高位的

1:1.11100010010000000

一共移動了16位,在布耳運算中小數點每向左移一位就等於在以2為底的科學計算法表示中指數+1,所以原數就等於這樣:1.11100010010000000 * ( 2 ^ 16)

好了,現在我們要的尾數和指數都出來了。顯而易見,最高位永遠是1,這樣尾數的二進位制就變成了:11100010010000000最後在尾數的後面補0,一直到補夠23位:

11100010010000000000000

再回來看指數,一共8位,可以表示範圍是0 - 255的無符號整數,也可以表示-128 - 127的有符號整數。但因為指數是可以為負的,所以為了統一把十進位制的整數化為二進位制時,都先加上127,在這裡,我們的16加上127後就變成了143,二進位制表示為:10001111

12345.0f

這個數是正的,所以符號位是0,那麼我們按照前面講的格式把它拼起來:

0 10001111 11100010010000000000000

0 100011111110001 00100000 00000000

再轉化為16進製為:47 f1 20 00,最後把它翻過來,就成了:00 20 f1 47。

按照ieee浮點數表示法,將float型浮點數123.456f轉換為十六進製制**。對於這種帶小數的就需要把整數部和小數部分開處理。整數部直接化二進位制:100100011。小數部的處理比較麻煩一些,比如有乙個十進位制純小數0.57826,

5是十分位,位階是1/10;

7是百分位,位階是1/100;

8是千分位,位階是1/1000……,

這些位階分母的關係是10^1、10^2、10^3……,現假設每一位的序列是,在這裡就是5、7、8、2、6,而這個純小數就可以這樣表示:n = s1 * ( 1 / ( 10 ^ 1 ) ) + s2 * ( 1 / ( 10 ^ 2 ) ) + s3 * ( 1 / (10 ^ 3 ) ) + …… + sn * ( 1 / ( 10 ^ n ) )。把這個公式推廣到b進製純小數中就是這樣:

n = s1 * ( 1 / ( b ^ 1 ) ) + s2 * ( 1 / ( b ^ 2 ) ) + s3 * ( 1 / ( b ^ 3 ) ) + …… + sn * ( 1 / ( b ^ n ) )

現在乙個二進位制純小數比如0.100101011就應該比較好理解了,這個數的位階序列就因該是1/(2^1)、1/(2^2)、1/(2^3)、1/(2^4),即0.5、0.25、0.125、0.0625……。乘以s序列中的1或著0算出每一項再相加就可以得出原數了。現在你的基礎知識因該足夠了,再回過頭來看0.45這個十進位制純小數,化為該如何表示呢?現在你動手算一下,最好不要先看到答案,這樣對你理解有好處。

來看一下步驟:1 / 2^1位(為了方便,下面僅用2的指數來表示位),0.456小於位階值0.5故為0;2位,0.456大於位階值0.25,該位為1,並將0.45減去0.25得0.206進下一位;3位,0.206大於位階值0.125,該位為1,並將0.206減去0.125得0.081進下一位;4位,0.081大於0.0625,為1,並將0.081減去0.0625得0.0185進下一位;5位0.0185小於0.03125,為0……問題出來了,即使超過尾數的最大長度23位也除不盡!這就是著名的浮點數精度問題了。

到這裡就可以解釋為什麼最前面double a 的問題了。

浮點數精度丟失問題

c 中的浮點數,分單精度 float 和雙精度 double float 是 system.single 的別名,介於 3.402823e38 和 3.402823e38 之間的32位數字,符合二進位制浮點演算法的 iec 60559 1989 ieee 754 標準 double 是 system...

浮點數的精度問題

float 1bit 符號位 8bits 指數字 23bits 尾數字 double 1bit 符號位 11bits 指數字 52bits 尾數字 對於二進位制的小數 1.1 1 20 1 2 1 1 1 2 1.5 1.01 1 20 0 2 1 1 2 2 1 1 4 1.25 1.0011 1...

golang 浮點數精度問題

把所有需要精確計算的資料先轉成decimal,用decimal進行精確計算 1,兩個浮點數相加減,可能不準確 1 相減 x 74.96 y 20.48 b x y fmt.println b output 54.47999999999999 2 相加 var a 0.6 fmt.println a ...