Kalan's Blog

Kalan 頭像照片,在淡水拍攝,淺藍背景

四零二曜日電子報上線啦!訂閱訂起來

Software Engineer / Taiwanese / Life in Fukuoka
This blog supports RSS feed (all content), you can click RSS icon or setup through third-party service. If there are special styles such as code syntax in the technical article, it is still recommended to browse to the original website for the best experience.

Current Theme light

我會把一些不成文的筆記或是最近的生活雜感放在短筆記,如果有興趣的話可以來看看唷!

Please notice that currenly most of posts are translated by AI automatically and might contain lots of confusion. I'll gradually translate the post ASAP

數值穩定與誤差

數值穩定與誤差

(0.1 + 0.2) == 0.3 這個看起來非常理所當然的等式,然而由於計算機對浮點數的儲存方式,在很多程式語言當中這個等式並不成立,也就是答案為 false

不過另外一個問題是,為什麼 (0.5 + 0.25) == 0.75 這個等式又會成立呢?

在做浮點數運算時多少都會出現誤差。接下來我們討論為什麼會有誤差,以及在計算上可以做哪些事情來避免。

浮點數在計算機的儲存方式

浮點數在電腦當中會以 bit 排列表示,由 IEEE754 所規範。其中單精度浮點數為 32bit;雙精度則為 64bit。 其中分成表示正負數的符號位、小數部、指數部。

  • 符號位(1bit):0 為正數;1 為負數
  • 小數部
  • 指數部
型別大小指數部小數部
單精度32bit8bit23bit
雙精度64bit11bit52bit

以單精度為例,由於指數部為 8bit,可以表示的範圍為 0 ~ 255,但因為需要表示負數,在 IEEE754 的規範當中需要加上一個偏移量,才是最後儲存在指數部的數字。以單精度來說,偏移量為 127。

舉例來說,-14.75 在浮點數的表示會是:

  • 符號位:負數,所以為 1
  • 指數部:先將 14.75 用二進位表示 1110.11,正規化後為 1.11011* 2^3,指數部為 3 加上 127 後為 130,二進位表示為 10000010
  • 小數部:11011,其餘的位數為 0

所以 -14.75 的浮點數表示會是:1 10000010 11011000000000000000000000022

從這邊我們也可以知道誤差從何而來,由於指數為負數後,得到的數字都是 0 < x < 1 之間,因此如果是無法透過 2^-x 表示的數字,就只能盡可能趨近而無法做到相同。開頭提到的 0.5 + 0.25,由於這些數字可以寫成 2^-1+ 2^-2,因此計算上不會出現誤差。

為什麼要這樣儲存?

使用科學記數表示(Scientific notation)可以幫助我們處理各種 scale 的數字,並且保持一定的精度。像是 0.000000123451234567890000,透過科學記數可以表示為 1.2345E-71.23456789E12。在計算機當中通常是使用浮點數格式儲存,跟科學記數類似,只是將十進位改成了二進位。

實數有無限多個,然而電腦的儲存空間有限,因此不管如何儲存都一定會有誤差。我們能做的是在精度與可表達的數字範圍作 trade-off。

有效數字

有效數字是幫助我們判斷精度的指標。像是 0.0010.0135 這些數字,我們可以寫作 1×1031 \times 10^{-3}1.35×1021.35\times10^{-2}。這時 0.001 的有效數字為 1 位,1.35 的有效數字為 3 位。有效數字位數越多,精度越高。

在維基百科有個簡單的判斷規則:

  • 所有非零數字都是有效的
  • 非零數字間的零都是有效的
  • 前綴零始終無效
  • 對於需要小數點的數,後綴零(最後一個非零數字後的零)是有效的
  • 對於不需要小數點的數,後綴零可能有效也可能無效。需要根據額外的符號或者誤差訊息決定。

Cancellation of significant digits

兩個絕對值非常相近的浮點數,在做減法運算時,因為大部分的數字相同,會剩下很多 0,導致有效數字減少,這個現象就叫做 cancellation of significant digits。

舉例來說:(1.234567890 - 1.234567889),雖然結果為 0.000000001,但在精度不足的情況下有可能變為 0

這在數值計算當中是需要特別注意的事:舉例來說倍角與半角公式

sin2(θ2)=1cosθ2\sin^2(\frac{\theta}{2}) = \frac{1-\cos\theta}{2}

當角度比較低的時候,由於 cos 相當接近 1,因此與 1 相減很容易出現有效數字遺失的問題。舉例來說:角度為 1 度時,使用倍角公式:(有效數字為 6 位數的情況下)

1cos1°2=10.9998472=0.0000765=7.65×105\frac{1-\cos1\degree}{2} = \frac{1 - 0.999847}{2} = 0.0000765 = 7.65 \times 10^{-5}

如果採用右邊的公式直接查表的話:

sin2(1°2)=0.008726532=0.00007615232=7.61523×105\sin^2(\frac{1\degree}{2}) = 0.00872653^{2} = 0.00007615232 = 7.61523 \times 10^{-5}

可以發現兩者的誤差相當明顯。在數值計算中要特別小心。要避免有效數字遺失有幾個方法:

  • 盡可能避免絕對值相近的兩個數做運算

  • 改用其他公式計算(例如上述的倍角公式)

    • 其實就是避免讓絕對值相近的兩個數做運算
  • 提高精度

結論

比較有經驗的工程師應該多少都知道浮點數運算會有誤差,也知道 0.1 + 0.2 != 0.3 的原因為何,這篇文章深入探討了小數的儲存方式、IEEE754、以及有效數字的遺失。

倍角公式應該是在高中三角函數時常常出現的題目,對我來說就是套套公式而已。

然而實際的應用都是透過電腦在算的,現實生活也不會到處都是 30 度 60 度這種好算的數字,老師也不會跟你說倍角公式會有有效數字的問題。

Prev

興趣使然的研究之旅

Next

奇異值分解

If you found this article helpful, please consider buy me a drink ☕️ It'll make my ordinary day shine✨

Buy me a coffee