1. 程式人生 > 實用技巧 >浮點數運算相關

浮點數運算相關

讀了一些 IEEE 754 實現的浮點數運算相關的文章

寫一寫讀後感 (以下如無特殊說明, 所有表達時中使用的符號均為相應英文的首字母;浮點數也專指二進位制浮點數

)

名稱 radix Significand bits (包括1位隱含的整數位) Decimal digits (精度 = lg2^Significand bits) 指數位 固定偏移值 E min E max
binary16 半精度浮點數 2 1 + 10 = 11 lg2^11 ≈ 3.31 5 2^(5-1) - 1 = 15 -14 = 1 - +15 2^(5-1) - 1 = +15
binary32 單精度浮點數 2 24 7.22 8 127 −126 +127
binary64 雙精度浮點數 2 53 15.95 11 1023 −1022 +1023
binary128 四精度浮點數 2 113 34.02 15 16383 −16382 +16383
binary256 八精度浮點數 2 237 71.34 19 262143 -262142 +262143
                   31
                   |
                   | 30    23 22                    0
                   | |      | |                     |
             type -+-+------+-+---------------------+ value
             特殊值 * 00000000 00000000000000000000000 ±0.0
              
min subnormal number * 00000000 00000000000000000000001 ±2^−23 × 2^−126 = ±2−149 ≈ ±1.4×10^-45
max subnormal number * 00000000 11111111111111111111111 ±(1−2^−23) × 2^−126 ≈ ±1.18×10^-38

 min normal number * 00000001 00000000000000000000000 ±2^−126 ≈ ±1.18×10^-38
              ±1.0 * 01111111 00000000000000000000000 ±1.0
 max normal number * 11111110 11111111111111111111111 ±(2−2^-23) × 2^127 ≈ ±3.4×10^38
 
              特殊值 * 11111111 00000000000000000000000 ±∞
              特殊值 0 11111111 10000000000000000000000 qNaN
              特殊值 0 11111111 01000000000000000000000 sNaN
               -----+-+------+-+---------------------+
                    | |      | |                     |
                    | +------+-+---------------------+
                    |    |    |           |
                    |    |    v           |
                    |    |the implicit bit|
                    |    v                v
                    | exponent         fraction
                    v 
                   sign

32 位單精度浮點數

浮點數儲存結構由三部分組成

  1. s符號位 sign
    • 0 為正
    • 1 為負
  2. e指數位 exponent
    • 指數位 (偏移指數, 也稱階碼) 使用無符號整數表示, 範圍: [0, 2^e - 1] (偏移指數 = 實際指數 + 固定偏移值. 固定偏移值 = 2^(e-1) - 1)
      1. 偏移指數: 0 表示非規約形式的浮點數特殊值 ±0

        1. 如果尾數的小數部分非 0, 表示非規約的浮點數
        2. 如果尾數的小數部分是 0, 表示特殊值 ±0 (和符號位相關)
      2. 偏移指數: (0, 2^(e-1) - 1) 表示負指數

      3. 偏移指數: 2^(e-1) - 1 表示 ±0 指數

      4. 偏移指數: (2^(e-1) - 1, 2^e - 1) 表示正指數

      5. 偏移指數: 2^e - 1 表示特殊值 ±∞特殊值NaN

        1. 如果尾數的小數部分是 0, 表示特殊值 ±∞ (和符號位相關)
        2. 如果尾數的小數部分非 0, 表示特殊值 NaN
          • qNaN (quiet NaN) 尾數的小數部分最高位為 1
            • 將該最高位更改為 0 時, 可能得到特殊值 ±∞ (和符號位相關)
          • sNaN (signaling NaN) 尾數的小數部分最高位為 0
            • 將該最高位更改為 1 時, 得到 qNaN
          • 通常 qNaN 用於使運算正常進行, sNaN 用於引發異常 (是否引發異常取決於 floating-point unit FPU 的狀態), 具體見 qNaN 與 sNaN 的區別
    • 使用偏移指數的優點: 可以用長度為 e 個單位的無符號整數來表示所有的實際指數, 這使得兩個浮點數的指數大小的比較更為容易
  3. m尾數位 mantissa / 有效數 significand (significand 也被叫做 mantissa, 它等於 the implicit bit + fraction)
    • 規約與非規約浮點數
      • 偏移指數: (0, 2^e - 1), 也即 [1, 2^e - 2], 表示規約形式的浮點數. 規約形式的浮點數隱含整數位為 1
      • 偏移指數為 0 且尾數的小數部分非 0, 表示非規約形式的浮點數. 非規約形式的浮點數隱含整數位為 0
      • 非規約形式的浮點數的偏移指數比規約形式的浮點數的偏移指數小 1
        • 例如: 最小規約形式的單精度 (32位 = 1s + 8e + 23f) 浮點數的偏移指數為 1: (-126 + 127), 實際指數為 -126; 而非規約的單精度浮點數的偏移指數為 0: (-126 + 127 - 1), 對應的實際指數也是 -126 而不是 -127
    • 使用隱含整數位的優點: 增加了 1 位浮點數的有效數長度
    • 使用非規約形式的浮點數的優點 (漸進式下溢位 gradual underflow 的優點): 避免了突然式下溢位 abrupt underflow, 使得每個浮點數之間的距離 gap 一致 = 2^(-f + (1 - (2^(e-1) - 1)))

浮點數的特點

  • 只能精確表示可由二進位制科學計數法 (-1)^s*m*2^e 表示的數值, m 超出精度的部分自動進一舍零

    這也是 0.1, 1.1 等浮點數無法被精確儲存的原因

    // 以下使用 JavaScript 實現的雙精度浮點數, 精度為 15.95, 約 16 位有效數字
    有效數字是指在一個數中,從該數的第一個非零數字起,直到末尾數字為止的長度
    
    (0.1).toPrecision(16);  // "0.1000000000000000" 對於0.1, 有效數為16位
    (0.1).toPrecision(17);  // "0.10000000000000001" 對於0.1, 有效數為17位
    (0.1).toPrecision(18);  // "0.100000000000000006"
    (0.1).toPrecision(22);  // "0.1000000000000000055511"
    
    (1.1).toPrecision(16);  // "1.100000000000000" 對於1.1, 有效數為16位
    (1.1).toPrecision(17);  // "1.1000000000000001" 對於1.1, 有效數為17位
    (1.1).toPrecision(18);  // "1.10000000000000009"
    (1.1).toPrecision(22);  // "1.100000000000000088818"
    
    1.000000000000001;  // 1.000000000000001 有效位數為16位
    1.0000000000000001;  // 1 第17位的1被捨去了
    
  • 規約形式浮點數的最大值: ±(1 + (2^-1 + 2^-2 + ... + 2^-f)) * 2^(2^(e-1) - 1) <=> ±(2 - 2^-f) * 2^(2^(e-1) - 1).

    對於雙精度浮點數來說, 其規約最大值為: ±(2- 2^-52) * 2^1023 === ±1.7976931348623157e+308, 1.7976931348623157e+308 也是 JavaScript 中 Number 物件靜態屬性 MAX_VALUE 的值 (注意它不是一個安全整數), 大於該值即表示 ∞ (Number.MAX_VALUE * 1.000000000000001 === Infinity; Number.MAX_VALUE + 1e+292 === Infinity)

  • 非規約形式浮點數的最小值: ±2^(-f + (1 - (2^(e-1) - 1))).

    對於雙精度浮點數來說, 其非規約最小值為: ±2^(-52-1022) === ±5e-324, 5e-324 也是 JavaScript 中 Number 物件靜態屬性 MIN_VALUE 的值, 小於該值即表示 0

  • 浮點數的安全整數範圍 (安全整數範圍指浮點數與整數可以一對一): [-(2^m - 1), 2^m - 1]. 對於雙精度浮點數來說, 安全整數為: ±2^53 - 1 === ±9007199254740991, 共有 16 位有效數字. 非安全整數的特點是: 一個浮點數對應多個實數, 如下圖所示:

    這也是 JavaScript 中 Number 物件靜態屬性 MAX_SAFE_INTEGERMIN_SAFE_INTEGER 的值

    2^53 + 1 用二進位制表示為: 1000...0001 (共 54 位, 兩個一分別是 2^53 和 2^0), 轉為二進位制科學表示法為: 1.000...0001 * 2^53 (尾數的小數部分共 53 位), 由於雙精度浮點數的尾數最多能儲存 52 位二進位制, 因此最後的 1 註定被捨去. 2^53 + 12^53 儲存一致, 也即 2^53 === 2^53 + 1, 2^53 不是一個安全整數

  • 浮點數可以準確表示的數, 以雙精度浮點數為例: 由於尾數的小數部分最多隻能儲存 52 位, 因此大於浮點數的安全整數範圍並且還要精確表示的數有兩類

    • 一類是在指數的範圍內增加指數的大小, 且保持尾數始終為 1.0 的數: 2^54, 2^55, 2^56, ..., 2^1023, 這些都是精確的數

    • 另一類是指數與尾數同時更改的數: 對於 [2^53, 2^54) 之間的數, 因為其尾數的小數部分共有 53 位, 第 53 位註定會被捨去, 那我們只要保證數的第 53 位為 0, 那麼該數即可精確保證, 也即在 [2^53, 2^54) 之間的偶數才能保證第 53 位為 0, 才能精確表示;

      同理, [2^54, 2^55) 之間的數, 第 53 位和第 54 位註定被捨去, 那我們只要保證數的第 53 位和第 54 位都為 0, 那麼該數即可精確保證, 也即在 [2^54, 2^55) 之間, 間距變為 4 的倍數, 這樣才能保證第 53 位和第 54 位都為 0, 才能精確表示, 以此類推