浮點數(shù)精度問題原因以及各種現(xiàn)象的解釋

在知乎上看到有人提出類似的問題橄教,我總結(jié)表述一下琳拭,做一下筆記:

一步步解釋:

存儲問題

首先要了解刻伊,從根本上講露戒,計算機只識別二進制數(shù),無論我們使用何種編程語言捶箱,在何種編譯環(huán)境下工作智什,都需要將程序編譯成二進制機器碼才能讓計算機執(zhí)行
那么這樣會導(dǎo)致什么問題呢?會導(dǎo)致小數(shù)的十進制轉(zhuǎn)為二進制出現(xiàn)誤差丁屎,這就是所謂的精度缺失。

為什么會出現(xiàn)誤差?這就涉及到了計算機原理的問題:

我們知道數(shù)字在計算機中是以二進制存儲的。對于整數(shù)來說叫搁,只要不超過整數(shù)的表示范圍,一定都可以表示成二進制的形式惨奕,比如8是1000梨撞,88是1011000雹洗,可是小數(shù)小數(shù)部分就沒有那么幸運了,根據(jù)二進制小數(shù)轉(zhuǎn)換成十進制的規(guī)則(把該小數(shù)不斷乘2卧波,再取所得的整數(shù)部份时肿,直至沒有小數(shù)或足夠長度為止)(二進制整數(shù)換成十進制規(guī)則 除二取余倒記發(fā))。

由于二進制中所有的小數(shù)存儲的位數(shù)是有限,因此我們得知“任何十進制整數(shù)都可以精確轉(zhuǎn)換成一個二進制整數(shù),但任何十進制小數(shù)卻不一定能精確轉(zhuǎn)換成一個二進制小數(shù),只要轉(zhuǎn)換過程中乘積的小數(shù)部分滿足所需精度即可”。比如對于0.1來說就不能精確轉(zhuǎn)換為一個二進制小數(shù),在16位小數(shù)的限制條件下遥昧,離它最近的二進制小數(shù)是0.0001100110011覆醇,也就是十進制的0.0999755859375鞋仍。所以雖然你寫程序的時候?qū)懙氖?.1開始這個數(shù)存儲到b這個float變量里的時候就變成了0.0001100110011常摧,也就是0.0999755859375,因此你輸出它的時候就會出現(xiàn)精度誤差了威创,這種誤差是不可避免的。
這里我有個疑問肚豺,為什么我們不把小數(shù)也當做整數(shù)存儲呢溃斋?我懷疑是因為存儲方式的問題,然后我找到了原碼吸申、反碼梗劫、補碼的文章享甸,然后我又想到為什么要用補碼?然后找到了加法器梳侨,然后就想為什么只有加法器蛉威?到這感覺有點不清楚了,我要好好找一下走哺,有情況再回來更新蚯嫌。

再來,根據(jù)IEEE754(二進制浮點數(shù)算術(shù)標準) 來說:

V = (-1)^s × 2^E × M
 ”铩(1)(-1)^s表示符號位择示,當s=0,V為正數(shù)彼哼;當s=1对妄,V為負數(shù)湘今。

(2)2^E表示指數(shù)位敢朱。

(3)M表示有效數(shù)字,大于等于1摩瞎,小于2拴签。

IEEE 754規(guī)定,有四種精度的浮點數(shù):單精確度(32位)旗们、雙精確度(64位)蚓哩、延伸單精確度(43比特以上,很少使用)與延伸雙精確度(79比特以上上渴,通常以80位實現(xiàn))岸梨。
對于32位的浮點數(shù),最高的1位是符號位s稠氮,接著的8位是指數(shù)E曹阔,剩下的23位為有效數(shù)字M。

32位浮點數(shù)存儲格式

對于64位的浮點數(shù)隔披,最高的1位是符號位S赃份,接著的11位是指數(shù)E,剩下的52位為有效數(shù)字M奢米。

64位浮點數(shù)存儲格式

其實看看上圖你就應(yīng)該明白抓韩,還有一種精度丟失就是長度丟失(不同類型存儲結(jié)構(gòu)不同),但那種比較簡單鬓长,就不做討論谒拴。

繼續(xù)來講,上圖到底是什么意思呢涉波。咱們先慢慢來講:

正規(guī)化(規(guī)約形式)

如果浮點數(shù)中指數(shù)部分的編碼值在[圖片上傳失敗...(image-c806de-1554705679571)]之間彪薛,且在科學表示法的表示方式下茂装,分數(shù) (fraction) 部分最高有效位(即整數(shù)字)是1,那么這個浮點數(shù)將被稱為規(guī)約形式的浮點數(shù)善延。
如上存儲格式中所示:

  1. 首先對于第一段sign少态,就是符號位,0代表正易遣,1代表負彼妻。。

  2. 第二段exponent指數(shù)位豆茫,實際也是有正負的侨歉,但是沒有單獨的符號位,在計算機的世界里揩魂,進位都是二進制的幽邓,指數(shù)表示的也是2的N次冪,8位指數(shù)表達的范圍是0到255火脉,而對應(yīng)的實際的指數(shù)是-127到128牵舵。也就是說實際的指數(shù)等于指數(shù)位表示的數(shù)值減127。這里特殊說明倦挂,-127和+128這兩個指數(shù)數(shù)值在IEEE當中是保留的用作多種用途的畸颅,
    關(guān)于指數(shù)位與實際的指數(shù)之差叫做移碼,移碼的值

精度 M(階/指數(shù)數(shù)位) 移碼 二進制表示
單精度 8 127 0111 1111
雙精度 11 1023 011 1111 1111
長雙精度 15 16383 011 1111 1111 1111
  1. 第三段fraction尾數(shù)位(也叫有效數(shù)字)方援,只代表了二進制的小數(shù)點后的部分没炒,小數(shù)點前的那位被省略了,當指數(shù)位全部為0時省略的是0否則省略的是1犯戏。

舉個栗子(以32位浮點數(shù)為例送火,這樣誤差比較大):

比如有個浮點數(shù)17.35需要保存。
17.35轉(zhuǎn)為二進制:17轉(zhuǎn)為10001(這就不用說了吧)
0.35呢先匪?(乘2取整法)
0.35 * 2 = 0.7 記為 0
0.70 * 2 = 1.4 記為 1
0.40 * 2 = 0.8 記為 0
0.80 * 2 = 1.6 記為 1
.......這么算有結(jié)束嗎种吸?當然沒有...所以就按照能保存的極限位數(shù)保存相對精度。  

這樣我們17.35就變成了10001.0101100110011001100共預(yù)留24位數(shù)  
然后把這串數(shù)字右移至小數(shù)點前只剩1位:  
1.00010101100110011001100---》 右移了4位  
這樣得到了指數(shù)位和尾數(shù)位:  
指數(shù):實際為4胚鸯,必須加上127(轉(zhuǎn)出的時候骨稿,減去127),所以為131姜钳。也就是10000011坦冠。  
尾數(shù):00010101100100100100100 (1就不用保存了)。  

到此哥桥,十進制浮點數(shù)就變成了二進制浮點數(shù)存儲了辙浑。

非正規(guī)化:0的表示(非規(guī)約形式)(-127)

如果浮點數(shù)的指數(shù)部分的編碼值是0,分數(shù)部分非零拟糕,那么這個浮點數(shù)將被稱為非規(guī)約形式的浮點數(shù)判呕。分數(shù)部分全為0倦踢,就是表示浮點數(shù)0

比如說我們要存儲0.35這個浮點數(shù),按上述正規(guī)化的形式就無法存儲侠草,因為不知道指數(shù)位寫多少辱挥,于是就有了約定,約定指數(shù)位為0就表明非正規(guī)化边涕。
所以晤碘,當見到指數(shù)部分為0是,尾數(shù)部分就不再是1.bbbbb...而是0.bbbbb...了功蜓。

無窮大與NAN

上面說了园爷,指數(shù)位預(yù)留了兩個值(-127和128),-127是做非規(guī)約形式的式撼,那么128呢童社?

當指數(shù)位達到當前浮點數(shù)最大指數(shù)值時:

  • 尾數(shù)位全為0就表示無窮大。
  • 尾數(shù)位不全為0就表示NaN著隆。

舍入規(guī)則

還是以32位單精度浮點數(shù)為例扰楼。
32位單精度浮點數(shù)存儲23位尾數(shù)位,那么就以第24位位判斷依據(jù)

  • 如果 24位為1 24位之后都是0 :如果23位為0旅东,則舍去不管灭抑;如果23位為1十艾,則24位向23位進1抵代,使23位還是0。
  • 如果 24位為1 24位之后不全0 :24位向23位進1忘嫉。
  • 如果 24位為0 舍

** 因為位數(shù)荤牍、算法、規(guī)則之類的約定庆冕,得知計算機浮點數(shù)并不是嚴格存儲這就是為什么浮點數(shù)運算出現(xiàn)問題的原因 **


有事情要做康吵,先寫到這。
待說明問題:

  1. 關(guān)于漸進式下溢出的理論與由來访递。
  2. 關(guān)于以下代碼出現(xiàn)情況的說明:
System.out.println(0.1 * 1);
System.out.println(0.1 * 2);
System.out.println(0.1 * 3);
System.out.println(0.1 * 4);
System.out.println(0.1 * 5);
System.out.println(0.1 * 6);
System.out.println(0.1 * 7);
System.out.println(0.1 * 8);
System.out.println(0.1 * 9);
System.out.println("=====================");
System.out.println(0.0999999999999999999999999999999999999999999999999);
double f1 = 9.7;
double f2 = 0.7;
double f3 = 9;
System.out.println(f1-f2-f3);
System.out.println(f1-f3-f2);

輸出結(jié)果:
0.1
0.2
0.30000000000000004
0.4
0.5
0.6000000000000001
0.7000000000000001
0.8
0.9
=====================
0.1
-6.661338147750939E-16

問題:

  1. 為什么有的輸出正確晦嵌,有的不行
  2. 為什么0.0999--的輸出為0.1
  3. 為什么f1-f2-f3得到0.0,而f1-f3-f2卻得到那樣的結(jié)果

這些問題我現(xiàn)在也并沒有思考清楚拷姿,等待后續(xù)思考惭载。
當然,有問題可以在評論中討論指出响巢。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末描滔,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子踪古,更是在濱河造成了極大的恐慌含长,老刑警劉巖券腔,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異拘泞,居然都是意外死亡纷纫,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門陪腌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涛酗,“玉大人,你說我怎么就攤上這事偷厦∩烫荆” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵只泼,是天一觀的道長剖笙。 經(jīng)常有香客問我,道長请唱,這世上最難降的妖魔是什么弥咪? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮十绑,結(jié)果婚禮上聚至,老公的妹妹穿的比我還像新娘。我一直安慰自己本橙,他們只是感情好扳躬,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著甚亭,像睡著了一般贷币。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上亏狰,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天役纹,我揣著相機與錄音,去河邊找鬼暇唾。 笑死促脉,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的策州。 我是一名探鬼主播瘸味,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼抽活!你這毒婦竟也來了硫戈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤下硕,失蹤者是張志新(化名)和其女友劉穎丁逝,沒想到半個月后汁胆,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡霜幼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年嫩码,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片罪既。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡铸题,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出琢感,到底是詐尸還是另有隱情丢间,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布驹针,位于F島的核電站烘挫,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏柬甥。R本人自食惡果不足惜饮六,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望苛蒲。 院中可真熱鬧卤橄,春花似錦、人聲如沸臂外。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽寄月。三九已至辜膝,卻和暖如春无牵,著一層夾襖步出監(jiān)牢的瞬間漾肮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工茎毁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留克懊,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓七蜘,卻偏偏與公主長得像谭溉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子橡卤,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容