面試官瘋了嗎,問我為什麼浮點數不精確?

HollisChuang發表於2021-11-15

很多人都知道,Java 中的浮點數並不精確,需要用 BigDecimal進行精確計算,但是,很少有人知道為什麼浮點數不精確呢?不精確為什麼還要用呢?本文就來展開分析一波;

我們知道,計算機的數字的儲存和運算都是通過二進位制進行的,對於,十進位制整數轉換為二進位制整數採用"除2取餘,逆序排列"法

具體做法是:

  • 用2整除十進位制整數,可以得到一個商和餘數;
  • 再用2去除商,又會得到一個商和餘數,如此進行,直到商為小於1時為止
  • 然後把先得到的餘數作為二進位制數的低位有效位,後得到的餘數作為二進位制數的高位有效位,依次排列起來。

如,我們想要把127轉換成二進位制,做法如下:

-w624

那麼,十進位制小數轉換成二進位制小數,又該如何計算呢?

十進位制小數轉換成二進位制小數採用"乘2取整,順序排列"法。

具體做法是:

  • 用2乘十進位制小數,可以得到積
  • 將積的整數部分取出,再用2乘餘下的小數部分,又得到一個積
  • 再將積的整數部分取出,如此進行,直到積中的小數部分為零,此時0或1為二進位制的最後一位。或者達到所要求的精度為止。

如嘗試將0.625轉成二進位制:

-w624

但是0.625是一個特列,用同樣的演算法,請計算下0.1對應的二進位制是多少:

-w624

我們發現,0.1的二進位制表示中出現了無限迴圈的情況,也就是(0.1)10 = (0.000110011001100…)2

這種情況,計算機就沒辦法用二進位制精確的表示0.1了。

所以,為了解決部分小數無法使用二進位制精確表示的問題,於是就有了IEEE 754規範。

IEEE二進位制浮點數算術標準(IEEE 754)是20世紀80年代以來最廣泛使用的浮點數運算標準,為許多CPU與浮點運算器所採用。

浮點數和小數並不是完全一樣的,計算機中小數的表示法,其實有定點和浮點兩種。因為在位數相同的情況下,定點數的表示範圍要比浮點數小。所以在電腦科學中,使用浮點數來表示實數的近似值。

IEEE 754規定了四種表示浮點數值的方式:單精確度(32位)、雙精確度(64位)、延伸單精確度(43位元以上,很少使用)與延伸雙精確度(79位元以上,通常以80位實現)。

其中最常用的就是32位單精度浮點數和64位雙精度浮點數。

單精度浮點數在計算機儲存器中佔用4個位元組(32 bits),利用“浮點”(浮動小數點)的方法,可以表示一個範圍很大的數值。

比起單精度浮點數,雙精度浮點數(double)使用 64 位(8位元組) 來儲存一個浮點數。

IEEE並沒有解決小數無法精確表示的問題,只是提出了一種使用近似值表示小數的方式,並且引入了精度的概念。

一個浮點數a由兩個數m和e來表示:a = m × b^e。

在任意一個這樣的系統中,我們選擇一個基數b(記數系統的基)和精度p(即使用多少位來儲存)。m(即尾數)是形如±d.ddd...ddd的p位數(每一位是一個介於0到b-1之間的整數,包括0和b-1)。

如果m的第一位是非0整數,m稱作規格化的。有一些描述使用一個單獨的符號位(s 代表+或者-)來表示正負,這樣m必須是正的。e是指數。

最後,由於計算機中儲存的小數其實是十進位制的小數的近似值,並不是準確值,所以,千萬不要在程式碼中使用浮點數來表示金額等重要的指標。

建議使用BigDecimal或者Long(單位為分)來表示金額。

相關文章