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)); // 如果是NaN就true
表格:double遇到奇葩操作会咋样
| 操作 | 结果 | double里存的内容 |
|---|---|---|
|
Infinity | +∞ |
|
-Infinity | -∞ |
|
NaN | 不是数字 |
|
NaN | 不是数字 |
GO TO FULL VERSION