C# 浮点数与定点数详细解析
在 C# 中,数值类型主要分为:
- 整数型(
int
,long
等) - 浮点型(
float
,double
) - 定点型(
decimal
)
浮点数和定点数在内部的表示方式不同,导致它们的 精度、范围、性能、使用场景 都有明显区别。本文将详细剖析两者的原理与对比。
1️⃣ 浮点数(float / double)
1.1 表示方法(IEEE 754)
浮点数采用 二进制科学计数法 存储:
值 = (-1)^符号位 × 1.尾数 × 2^(指数 - 偏移量)
- 符号位:1 位,表示正负
- 指数位:控制缩放大小
- 尾数位:存储有效数字
类型 | 位数 | 符号位 | 指数位 | 尾数位 |
---|---|---|---|---|
float | 32 | 1 | 8 | 23 |
double | 64 | 1 | 11 | 52 |
👉 范围极大,但精度有限。某些小数在二进制中无法精确表示。
1.2 小数的二进制表示
浮点数小数用 2 的负次方来组合:
0.101₂ = 2⁻¹ + 2⁻³ = 0.5 + 0.125 = 0.625
但像 0.1 这样的十进制分数,在二进制中是无限循环小数:
0.1₁₀ = 0.0001100110011...₂
只能近似存储 → 运算会出现误差。
1.3 常见误差示例
Console.WriteLine(0.1 + 0.2 == 0.3); // False
Console.WriteLine(0.1 + 0.2); // 0.30000000000000004
原因:二进制截断 → 加法结果不是精确的 0.3。
1.4 特殊值
浮点数里其实还藏着几个特别的数,它们在实际开发里经常会遇到:
-
正无穷 / 负无穷
当结果太大,已经超过浮点数能表示的范围时,就会变成无穷大:double.PositiveInfinity
→ 正无穷double.NegativeInfinity
→ 负无穷
-
NaN(Not a Number,意思是“不是一个数”)
一些数学上不合法的运算会得到 NaN,比如:0.0 / 0.0
Math.Sqrt(-1)
特别注意:NaN
和任何值比较(甚至跟自己比)都会是false
。
Console.WriteLine(double.NaN == double.NaN); // false
-
+0 和 -0
浮点数里其实区分正零和负零,不过在大多数运算里,它们没区别:-
+0 == -0
结果是true
-
但在某些场景(比如除法)下会有区别:
Console.WriteLine(1.0 / +0); // Infinity Console.WriteLine(1.0 / -0); // -Infinity
-
1.5 浮点数比较正确姿势
- 近似比较(epsilon 容差)
bool IsEqual(double a, double b, double eps = 1e-10)=> Math.Abs(a - b) < eps;
- 游戏开发常见做法(Godot / Unity)
Mathf.Approximately(a, b); // Unity
Mathf.IsEqualApprox(a, b); // Godot
- 避免直接用
==
除非数据本身是整数运算得来的,或者特别确定是完全相等。
2️⃣ 定点数(decimal)
2.1 表示方法
-
总共 128 位存储:
- 96 位存有效数字
- 16 位存小数位精度(scale,小数点位置)
-
十进制方式存储 → 可以精确表示
0.1
、0.2
、0.3
。
2.2 特点
- 高精度(28-29 位有效数字)
- 范围比
double
小,但能避免浮点误差 - 计算速度比浮点数慢(大约 5~20 倍),因为是软件实现而不是硬件指令
2.3 示例
decimal a = 0.1m + 0.2m;
decimal b = 0.3m;
Console.WriteLine(a == b); // True
👉 在货币、金融计算中,应优先使用 decimal
。
3️⃣ 性能对比
特性 | float/double(浮点数) | decimal(定点数) |
---|---|---|
存储方式 | 二进制科学计数法 | 十进制定点数 |
精度 | 有误差(约 15~16 位) | 高精度(28~29 位) |
范围 | 极大 | 较小 |
运算速度 | 硬件支持,速度极快 | 慢(软件实现) |
适用场景 | 科学计算、物理模拟 | 金融、货币 |
4️⃣ 小结
- 浮点数 → 范围大、速度快,但存在精度误差
- decimal → 精度高,适合金融,但性能较差
- 判断浮点数相等时,应使用 近似比较 而不是
==
- 在游戏或物理模拟 → 用
float/double
- 在账务、价格计算 → 用
decimal