C# 浮点数与定点数详细解析
C# 浮点数与定点数详细解析
在 C# 中,数值类型分为整数、浮点数和定点数。浮点数和定点数在计算机内部的表示方式不同,导致精度和使用场景也不同。
1️⃣ 浮点数(float / double)
1.1 表示方法
浮点数采用 IEEE 754 二进制科学计数法:
值 = (-1)^符号位 × 1.尾数 × 2^(指数 - 偏移量)
- 符号位(Sign bit):1 位,表示正负
- 指数位(Exponent):控制缩放大小
- 尾数位(Mantissa/Fraction):存储有效数字
例子:
类型 | 位数 | 符号位 | 指数位 | 尾数位 |
---|---|---|---|---|
float | 32 | 1 | 8 | 23 |
double | 64 | 1 | 11 | 52 |
浮点数能表示的范围很大,但精度有限,尤其是某些十进制小数在二进制里无法精确表示。
1.2 小数部分的二进制表示
浮点数小数部分用 2 的负次方表示:
二进制位 | 十进制值 |
---|---|
0.1₂ | 0.5 |
0.01₂ | 0.25 |
0.001₂ | 0.125 |
0.0001₂ | 0.0625 |
0.00001₂ | 0.03125 |
… | … |
每一位累加得到的值逼近原来的十进制小数,但不一定能精确等于。
1.3 为什么 0.1 在二进制里不能精确表示?
- 十进制 0.1 = 1/10
- 二进制只能用 2 的负次方表示分数
- 10 不是 2 的倍数 → 二进制表示是无限循环小数
0.1 十进制 = 0.00011001100110011...₂(无限循环)
累加前几位逼近 0.1:
二进制位 | 十进制值 | 累加 |
---|---|---|
2⁻⁴ | 0.0625 | 0.0625 |
2⁻⁵ | 0.03125 | 0.09375 |
2⁻⁸ | 0.00390625 | 0.09765625 |
2⁻⁹ | 0.001953125 | 0.099609375 |
… | … | … |
计算机只能存储有限位数 → 截断 → 得到近似值 → 浮点数运算可能有微小误差。
1.4 示例:0.1 + 0.2
Console.WriteLine(0.1 + 0.2 == 0.3); // false
Console.WriteLine(0.1 + 0.2); // 0.30000000000000004
原因:
- 0.1 和 0.2 在二进制里都是无限循环小数
- 存储时截断 → 相加结果只是接近 0.3
==
比较是精确位比较 → 返回 false
✅ 正确做法
- 近似比较(epsilon)
double a = 0.1 + 0.2;
double b = 0.3;
double epsilon = 1e-10;
Console.WriteLine(Math.Abs(a - b) < epsilon); // True
- 使用 decimal(高精度定点数)
decimal a = 0.1m + 0.2m;
decimal b = 0.3m;
Console.WriteLine(a == b); // True
- 仅用于显示匹配时,转字符串
double a = 0.1 + 0.2;
Console.WriteLine(a.ToString("F1") == "0.3"); // True
2️⃣ 定点数(decimal)
2.1 表示方法
-
总共 128 位存储
- 96 位存整数部分(有效数字)
- 16 位存小数位精度(scale,表示小数点位置)
-
以 十进制方式存储 → 能精确表示 0.1、0.2 等十进制小数
2.2 特点
- 高精度、适合金融、货币计算
- 范围比 double 小,但计算精确
- 不会出现 0.1+0.2==0.3 这种浮点误差
2.3 示例
decimal price1 = 0.1m;
decimal price2 = 0.2m;
Console.WriteLine(price1 + price2 == 0.3m); // True
3️⃣ 总结对比表
特性 | float / double(浮点数) | decimal(定点数) |
---|---|---|
存储方式 | 二进制科学计数法 | 十进制定点数 |
精度 | 有误差 | 高精度 |
范围 | 大 | 较小 |
适用场景 | 科学计算、物理模拟 | 金融、货币、账务 |
4️⃣ 小结
- 浮点数有范围大、速度快的优点,但十进制小数可能无法精确表示
- decimal 能精确表示十进制小数,适合对精度要求高的场景
- 1/3、π 在十进制中可能无限循环
- 0.1、0.2 在二进制中可能无限循环 → 浮点数加法出现微小误差
- 浮点数判断相等最好 用 epsilon 或 decimal