用 double 算钱会出错,因其二进制浮点表示无法精确存储多数十进制小数(如 0.1),导致加减偏差;金融等需“精确到分”的场景必须全程使用 decimal,并确保字面量带 m 后缀、DB 字段为 DECIMAL、JSON 解析不走 double 路径。

为什么 double 算钱会出错?
因为 double 是二进制浮点数,无法精确表示很多十进制小数(比如 0.1 或 19.99),加减后常出现 0.1 + 0.2 == 0.30000000000000004 这类偏差。金融、计费、库存扣减等场景只要涉及“精确到分”,就必须换类型。
常见错误现象:decimal total = 19.99m + 0.01m; 看似没问题,但若从 double 赋值:decimal d = 19.99;(没加 m 后缀)——编译器先按 double 解析再转 decimal,精度已在前一步丢失。
- 所有字面量必须显式加
m或M后缀,例如123.45m,否则默认是double - 从数据库读取数值时,确认字段类型是
DECIMAL/NUMERIC,不是FLOAT或REAL;ADO.NET 中用GetDecimal()而非GetDouble() - 第三方 API 返回的 JSON 数值若没指定类型,反序列化时可能走
double路径,需手动干预(如用JsonSerializerOptions.NumberHandling = JsonNumberHandling.AllowReadingFromString配合自定义转换器)
decimal 的性能和边界在哪?
decimal 是 128 位结构体,比 double(64 位)慢,但这是为精度付出的合理代价。它不是“万能高精度类型”——最大值约 ±7.9×10²⁸,最小非零值约 1×10⁻²⁸,超出就抛 OverflowException。
容易踩的坑:Math.Pow(10, 30) 返回 double,直接转 decimal 会溢出或失真;decimal 不支持 Math.Sqrt、Math.Log 等函数,需自己实现或接受 double 中间结果(此时精度已降级)。
- 避免在循环中频繁做
decimal开方、三角运算;如必须,先转double计算,再根据业务容忍度决定是否转回 - 大数乘法(如
99999999999999999999m * 99999999999999999999m)可能触发溢出,建议用checked块捕获异常 - EF Core 中映射
decimal属性,默认生成decimal(18,2),若需更高精度(如汇率保留 6 位),得用[Column(TypeName = "decimal(18,6)")]
和 double/float 混用时最危险的操作
隐式转换不存在:decimal 和 double 之间没有隐式转换,但显式强制转换((decimal)myDouble)会静默丢精度,且不报错。
典型翻车现场:前端传 {"price": 19.99} 到 ASP.NET Core API,控制器参数是 decimal price,但模型绑定底层用了 double 解析字符串再转 —— 这个过程可能引入误差(尤其在某些文化环境下小数点解析异常)。
- 永远不要写
(decimal)someDoubleVariable,除非你明确知道该double值本身可被精确表示为十进制有限小数(比如整数或 0.5/0.25 这类) - 从字符串转
decimal用decimal.TryParse(s, out var d),别用Convert.ToDecimal(s)—— 后者在区域设置不一致时可能因千分位符崩溃 - 日志里打印
decimal时别用$"{d}",它依赖当前线程文化;固定格式用d.ToString("G29")(最多 29 位有效数字,保证不丢精度)
事情说清了就结束。关键就一条:只要业务语义要求“绝对精确到某一位”,就锁死 decimal,从输入源头(JSON、DB、UI)到中间计算再到输出,全程守住 m 后缀和无损转换路径。其他地方,该用 double 还用 double。











