1. 前言
你可能覺得,電腦這麼「聰明」,那 0.1 + 0.2 應該就等於 0.3 啊。但事實不是這樣。來,直接看個簡單例子。
double x = 0.1;
double y = 0.2;
double sum = x + y;
Console.WriteLine(sum); // 程式會印出什麼?
現在你試試跟 0.3 比較一下:
Console.WriteLine(sum == 0.3); // 這裡會是 true 還是 false?
如果你看到 False,別太驚訝啦!
原因藏在數字在記憶體裡的表示方式
電腦是用二進位來處理數字的。但不是每個十進位小數都能用有限的二進位小數來表示,就像 1/3 沒辦法精確寫成十進位小數(0.333...)。舉例來說,0.1 在二進位裡是無窮小數,所以只能「四捨五入」存起來。
如果用白話來說,double 有時候「假裝」能精確存你的數字,其實只是存了個很接近的近似值而已。
2. 用 double 算數會遇到哪些「怪現象」?
來看幾個實際例子。
例子 1. 經典「魔法」0.1 + 0.2
double a = 0.1;
double b = 0.2;
double sum = a + b;
Console.WriteLine(sum); // 0.30000000000000004
Console.WriteLine(sum == 0.3); // False
電腦印出來不是 0.3,而是 0.30000000000000004。差一點點,但如果你在做金融相關的東西,這就很嚴重了。
例子 2. 疊加小數
double result = 0;
for (int i = 0; i < 10; i++)
{
result += 0.1;
}
Console.WriteLine(result); // 0.9999999999999999
你想要 1.0,結果卻少了一點點。又是 double 內部四捨五入的問題。
為什麼這在實務上很重要
很多人會想:「差一點點有差嗎?沒什麼大不了吧!」來看個支付世界的例子。
假設你的網銀要把 100 筆每筆 0.1 歐元的交易加總。如果你的程式每次都「掉」個十萬分之一歐元,銀行規模一大就真的會「不見」錢。這時會計馬上來問你:「我們的錢去哪了?!」
3. 怎麼正確比較浮點數
因為 double 很常沒辦法存你想要的精確值,直接用 == 比較很容易出包。正確做法是比「兩個數的差的絕對值」有沒有小於某個「超小的數」(epsilon)。
用誤差容忍值比較的例子
double a = 0.1 + 0.2;
double b = 0.3;
double epsilon = 0.000001;
if (Math.Abs(a - b) < epsilon)
{
Console.WriteLine("幾乎一樣啦!"); // 這樣比比較安全
}
這裡我們的意思是:「如果兩個數的差小於一百萬分之一,就當作一樣。」
4. double 的特殊值:Infinity, NaN, -Infinity
double 不只可以存數字,還有一些很特別的值。這些值會在數學老師平常叫你千萬別做的情況下出現。
無限大(Infinity)
如果你拿 1 去除以 0 會怎樣?
double result = 1.0 / 0.0;
Console.WriteLine(result); // Infinity
在 C#(還有很多語言)裡,double 除以 0 不會丟出例外!而是直接給你一個特殊值「正無限大」。
負無限大(-Infinity)
如果你拿負數除以 0,就會得到負無限大:
double result = -1.0 / 0.0;
Console.WriteLine(result); // -Infinity
「不是數字」(NaN — Not a Number)
如果你做了很奇怪的事,比如要算負數的平方根:
double result = Math.Sqrt(-1);
Console.WriteLine(result); // NaN
或者 0.0 / 0.0 的結果:
double result = 0.0 / 0.0;
Console.WriteLine(result); // NaN
NaN 就是「在現實生活裡根本不是數字的東西」。
檢查特殊值的方法
C# 有內建函數可以檢查這些特殊值:
Console.WriteLine(double.IsInfinity(result)); // true 代表是無限大
Console.WriteLine(double.IsNaN(result)); // true 代表是 NaN
表格:double 遇到奇怪運算時的反應
| 運算 | 結果 | double 裡存什麼 |
|---|---|---|
|
Infinity | +∞ |
|
-Infinity | -∞ |
|
NaN | 不是數字 |
|
NaN | 不是數字 |
GO TO FULL VERSION