先前對(duì)于浮點(diǎn)數(shù)的理解,只是停留在不能精確的表示一個(gè)數(shù)上,對(duì)于浮點(diǎn)數(shù)的位組成,以及二進(jìn)制和十進(jìn)制之間的轉(zhuǎn)換并沒過深入了解過。
因?yàn)?Swift 是一門類型安全的語言,不允許不同類型之間的數(shù)值進(jìn)行運(yùn)算,整數(shù)溢出的時(shí)候也無法編譯通過。對(duì)于 C,如果對(duì)類型不敏感的話,可能就會(huì)引入一些「看不見」的 Bug。
下面來看一下浮點(diǎn)數(shù)的構(gòu)成。
IEEE 浮點(diǎn)數(shù)的定義標(biāo)準(zhǔn)用 V = (-1)^s × M × 2^(E) 的形式來表示一個(gè)數(shù)。
- 符號(hào)(sign)s 決定這數(shù)是負(fù)數(shù)(s=1)還是正數(shù)(s=0),對(duì)于數(shù)值 0 的符號(hào)解釋做特殊處理。
- 尾數(shù)(significand)M 是一個(gè)二進(jìn)制小數(shù),它的范圍是 1~2-ε,或者 01-ε。1ε 代表小數(shù)位全為 1 ,非小數(shù)為 0 的二進(jìn)制數(shù)。
- 階碼 (exponent)E 的作用是對(duì)浮點(diǎn)數(shù)加權(quán),這個(gè)權(quán)重是 2 的 E 次冪(可能是負(fù)數(shù))。
浮點(diǎn)數(shù)在計(jì)算機(jī)中的表示形式分為 符號(hào)、尾數(shù)、階碼 三部分,編碼形式:
- 一個(gè)單獨(dú)的符號(hào)位 s 直接編碼。
- k 位的階碼字段 exp = e(k-1)...e(1)e(0) 編碼為階碼 E。
- n 位小數(shù)字段 frac = f(n-1)...f(1)f(0) 編碼為尾數(shù) M。
單精度的浮點(diǎn)數(shù)的符號(hào)位、階碼位、尾數(shù)位字段分別為 1 位, 8 位和 23 位。雙精度的浮點(diǎn)數(shù)分別為 1 位、11 位和 52 位。
IEEE 定義的浮點(diǎn)數(shù)標(biāo)準(zhǔn)和計(jì)算機(jī)的位級(jí)表示方式相互轉(zhuǎn)換,需要分三種情況討論:
情況 1:規(guī)范化的值
當(dāng) exp 即不全為 0 也不全為 1 時(shí),屬于規(guī)范化的值。階碼的值 E = e - Bias,其中 e 是二進(jìn)制階碼的無符號(hào)數(shù),Bias 表示二進(jìn)制階碼能表示的最大的有符號(hào)數(shù)。例如:某個(gè)單精度浮點(diǎn)數(shù)的階碼為 00000101 ,則它的 e = 5,Bias = 127。
小數(shù)字段 frac 被解釋為描述小數(shù)值 f,其中 0 ≤ f < 1。尾數(shù)的定義為 M = 1 + f,默認(rèn)以隱式的 1 開頭。把所有的 frac 字段都用來表示小數(shù)部分,因?yàn)榧热晃矓?shù)的個(gè)位總是為 1,就直接省略,這樣可以提高一位精度。例如某個(gè)單精度的浮點(diǎn)數(shù)的小數(shù)字段為 101010101010101010101(23位),那么 M = 1.101010101010101010101。
判斷小數(shù)位是否包含隱式 1 開頭,看階碼是否不全為 0 而不全為 1 即可。
情況 2: 非規(guī)范化的值
當(dāng)階碼全為 0 時(shí),所表示的數(shù)是非規(guī)范化形式。這種情況下,階碼值 E = 1 - Bias,而尾數(shù)的值 M = f,直接就是小數(shù)字段的值,不包含隱式的開頭 1。例如某個(gè)單精度的浮點(diǎn)數(shù)的小數(shù)字段為 101010101010101010101(23位),那么 M = 0.101010101010101010101。
情況 3: 特殊值
在階碼全為 1 的時(shí)候,當(dāng)小數(shù)位全為 0 時(shí),得到的值表示無窮。當(dāng) s=0 時(shí)是 +∞,當(dāng) s=1 時(shí)是 -∞。如果我們把兩個(gè)非常大的數(shù)相乘,或者除以 0 時(shí),無窮能表示溢出的結(jié)果。
當(dāng)小數(shù)域?yàn)榉橇銜r(shí),結(jié)果表示為 NaN,即 “不是一個(gè)數(shù)(Not a Number)” 的縮寫。一些運(yùn)算的結(jié)果不能使無窮或?qū)崝?shù),就會(huì)返回 NaN 的值,比如 ${\sqrt{-1}}$。
練習(xí)
如果希望加深印象,可自行嘗試將某個(gè)浮點(diǎn)數(shù)在二進(jìn)制與十進(jìn)制之間轉(zhuǎn)換。我寫了一個(gè)將浮點(diǎn)數(shù)轉(zhuǎn)換為二進(jìn)制位表示的形式,點(diǎn)擊查看。
思考:表達(dá)式 print(0.1 + 0.2 == 0.3) 的輸出結(jié)果是?