天天看點

浮點數的有效數字位數後記

過去有一種很普遍的說法是單精度浮點數的有效數字是6到7位。同時也有一個很普遍的問題就是:“6到7位是什麼意思?到底是6位還是7位?”。現在似乎主流認知已經變成了單精度浮點數的有效數字就是7位。事實究竟是怎麼樣的?

先說結論

單精度浮點數可以保證7位10進制有效數字。如果一個數字用10進制表示時有效數字位數大于等于7位,那麼用單精度浮點數記錄的話,能確定至少正确記錄前7位。

為什麼說“至少”?比如,4294967296有10位10進制有效數字,但隻有1位2進制有效數字(2進制表示是1後面32個0)。我們可以驗證單精度浮點數是可以正确記錄所有10位有效數字的。但上面隻是特殊情況,對于随便給出的一個數,隻有第7位和之前的有效數字是能确信正确的。

憑什麼說就是7位,為什麼不是6位、8位?

最簡單的2進制的情況

首先,我假設我們知道一個單精度浮點數種有24位2進制的有效數字(不知道的同學,請先自行搜尋

IEEE 754

)。很顯然,對于有24位或者以上2進制有效數字的數,單精度浮點數能保證前24位。

再看看16進制

我們知道16進制的1位對應2進制的4位(不知道的同學,……姑且4位2進制數剛好有16種不同的情況)。2進制的24位剛好對應16進制的6位,也就是能保證6位16進制的有效數字。

但是假設我們隻有23位2進制有效數字的話,那麼我們就隻能保證5位了。

接下來我們更具體地看一下。假設有16進制數 8765432 1 ( 16 ) 87654321_{(16)} 87654321(16)​,我們可以寫成 8.765432 1 ( 16 ) × 1 6 7 8.7654321_{(16)} \times 16^7 8.7654321(16)​×167。2進制的話,可以寫成 1000.011 1 ′ 011 0 ′ 010 1 ′ 010 0 ′ 001 1 ′ 001 0 ′ 000 1 ( 2 ) × 2 28 1000.0111'0110'0101'0100'0011'0010'0001_{(2)} \times 2^{28} 1000.0111′0110′0101′0100′0011′0010′0001(2)​×228如果我們隻有24位2進制有效數字,則後面一部分有效數字無法記錄,就成了 1000.011 1 ′ 011 0 ′ 010 1 ′ 010 0 ′ 001 1 ( 2 ) × 2 28 1000.0111'0110'0101'0100'0011_{(2)} \times 2^{28} 1000.0111′0110′0101′0100′0011(2)​×228也就是 8.7654 3 ( 16 ) × 1 6 7 8.76543_{(16)} \times 16^7 8.76543(16)​×167。如果再減少1位2進制有效數字的話 1000.011 1 ′ 011 0 ′ 010 1 ′ 010 0 ′ 00 1 ( 2 ) × 2 28 1000.0111'0110'0101'0100'001_{(2)} \times 2^{28} 1000.0111′0110′0101′0100′001(2)​×228我們将隻有3位2進制數001能用來表示最後1位16進制有效數字。我們知道,3位2進制數隻有8種不同情況,無法區分16進制1位數的16種情況。更具體地,比如這樣我們無法區分 8.7654 3 ( 16 ) × 1 6 7 8.76543_{(16)} \times 16^7 8.76543(16)​×167和 8.7654 2 ( 16 ) × 1 6 7 8.76542_{(16)} \times 16^7 8.76542(16)​×167,因為它們的最後一位都會被表示成001。

可以看到,無法保證有效數字的某一位,意味着我們沒辦法區分這一位可能出現的所有情況。反過來,如果說我們能保證某一位,說明去掉這一位之前所用掉的2進制有效數字位數後,我們還能剩下足夠的2進制位數來區分這一位可能出現的所有情況。

回過頭看前面我們用1位2進制表示10位10進制有效數字的情況。1位2進制有效數字有可能表示出10位的10進制有效數字,但是要能區分出10位10進制數的所有情況,我們還是需要更多的2進制位。

萬惡的10進制

如果能見到神的話,我一定要問他人為什麼要長10個指頭而不是8個——如果不能是16個的話……

下面我們要解決的問題是:去掉10進制某一位之前用掉的2進制有效數字後,我們怎麼計算還剩下多少2進制位數。

高能預警:這将不是個整數。

我們知道3位2進制數有8種不同情況,4位2進制數有16種不同情況。而每位有10種情況的10進制要對應2進制的多少位?咱們姑且直接用這結果 l o g 2 10 ≈ 3.322 log_210 \approx 3.322 log2​10≈3.322對于單精度浮點數,第N位10進制有效數字後還有 24 − N × l o g 2 10 24 - N \times log_210 24−N×log2​10個2進制位可用。如果說單精度浮點數能保證N位有效數字,意味着N位後剛好不再有足夠的2進制位數能區分10種不同情況。可以很容看出,這個N應該是讓 N × l o g 2 10 N \times log_210 N×log2​10不大于24的最大的整數。也就是 N = ⌊ 24 l o g 2 10 ⌋ = 7 N = \lfloor {24 \over log_210} \rfloor = 7 N=⌊log2​1024​⌋=7最終,我們得出N是7。( ⌊ x ⌋ \lfloor x \rfloor ⌊x⌋表示對 x x x向下取整)

至此為止,我們知道了該如何計算有限位數的2進制有效數字能保證的10進制有效數字位數。比如我們還可以計算53位2進制有效數字的雙精度浮點數可以保證的10進制位數是 ⌊ 53 l o g 2 10 ⌋ = 15 \lfloor {53 \over log_210} \rfloor = 15 ⌊log2​1053​⌋=15

正篇到此結束。

後記

這裡開始,我不再保證能盡量說人話。

換個角度

因為有 M l o g 2 10 = l o g 2 2 M l o g 2 10 = l o g 10 2 M {M \over log_210} = {log_22^M \over log_210} = log_{10}2^M log2​10M​=log2​10log2​2M​=log10​2M是以單精度浮點能保證的有效數字位數等價于 2 24 2^{24} 224(也就是16777216)去掉最高位後的位數。很多人會說這個就是單精度浮點數能表示7位有效數字的原因。從這個角度解釋也是可以的,但是直接這麼下結論跳過的步驟太多了。而且從這角度解釋鋪墊起來會麻煩很多。

“至少”的問題

為了友善,我們看16進制。

這個問題用24位有效數字不太好說,我們看23位。上面說了2進制23位是可以確定16進制的5位的。但是 1.234567 8 ( 16 ) × 1 6 7 = 1.001 0 ′ 001 1 ′ 010 0 ′ 010 1 ′ 011 0 ′ 011 1 ′ 100 0 ( 2 ) × 2 28 1.2345678_{(16)} \times 16^7 = 1.0010'0011'0100'0101'0110'0111'1000_{(2)} \times 2^{28} 1.2345678(16)​×167=1.0010′0011′0100′0101′0110′0111′1000(2)​×228保留23位有效數字是 1.001 0 ′ 001 1 ′ 010 0 ′ 010 1 ′ 011 0 ′ 0 1 ( 2 ) × 2 28 = 1.234564 × 1 6 7 1.0010'0011'0100'0101'0110'01_{(2)} \times 2^{28} = 1.234564 \times 16^7 1.0010′0011′0100′0101′0110′01(2)​×228=1.234564×167好像出來了6位。應該很容易注意到,這裡16進制的最高位隻占用了2進制的1位,是以後面多了3位可用。

是以計算第N位還能使用的2進制位數,不是簡單的用總的2進制位數,減去 ( N − 1 ) × 4 (N - 1) \times 4 (N−1)×4。而應該是減去第1位使用的2進制位數後,再減 ( N − 2 ) × 4 (N - 2) \times 4 (N−2)×4。

你可能覺得這樣我們前面的方法就不準确的了,其實不然。因為我們要計算的是位數最少的情況,而這樣隻會讓位數變多。

除了最開始提到的特殊情況,這是單精度浮點數正确表示超過7位10進制有效數字的更普遍的一種特殊情況。當然,這樣最多隻能表示出8位。

非整數的位數是什麼鬼

拿10進制來講,我們可以說第N位和 1 0 N 10^N 10N是對應的。比如我們想把3往左移動2位,就可以 3 × 1 0 2 = 300 3 \times 10^2 = 300 3×102=300。于是往左移動半位也就是 3 × 1 0 0.5 = 3 10 ≈ 9.487 3 \times 10^{0.5} = 3 \sqrt {10} \approx 9.487 3×100.5=310 ​≈9.487

前面10進制1位對應2進制 l o g 2 10 ≈ 3.322 log_210 \approx 3.322 log2​10≈3.322。很顯然 2 l o g 2 10 = 10 2^{log_210} = 10 2log2​10=10