C#/.NET中Math.Round舍入结果不一致问题咨询
为什么Math.Round(9.995, 2, MidpointRounding.AwayFromZero)返回9.99而非预期的10?
嘿,这个问题其实是二进制浮点数的经典精度陷阱,不是.NET框架的舍入逻辑出问题了!让我给你拆解清楚:
核心原因:double类型存不下精确的9.995
咱们用的double是二进制浮点数,它只能精确表示那些能拆成x除以2的n次方的数。像9.995这种十进制小数,转换成二进制后是无限循环的,根本没法精确存储——内存里实际存的是比9.995略小一丢丢的近似值(具体是9.994999999999998181010596454143524169921875)。
当你调用Math.Round的时候,这个实际值并不是严格的“中点”(也就是刚好在9.99和10.00正中间),而是更靠近9.99。所以按照MidpointRounding.AwayFromZero“远离零舍入”的规则,自然就舍到9.99了。
那为什么9.95能得到10呢?巧了,9.95转成double后,实际存的是比9.95略大一点的近似值(9.9500000000000010658141036401502788066864013671875),这个值超过了中点,所以舍入到1位小数时就向上进成10.0了。
解决方案:用Decimal类型搞定精确十进制舍入
要解决这个问题,直接换用.NET里的decimal类型就行——它是专门为十进制小数设计的,能精确表示像9.995这种数,完全避开二进制浮点数的精度问题。
代码示例:
// 注意加m后缀,告诉编译器这是decimal字面量 var correctResult1 = Decimal.Round(9.995m, 2, MidpointRounding.AwayFromZero); Console.WriteLine(correctResult1); // 输出 10.00 var correctResult2 = Decimal.Round(9.95m, 1, MidpointRounding.AwayFromZero); Console.WriteLine(correctResult2); // 输出 10.0
小提醒:
- 一定要给字面量加
m后缀,不然编译器会先把它当成double处理,再转成decimal的时候已经带精度误差了,那还是白搭。 decimal精度高但数值范围小,适合货币计算、需要严格精确的小数场景;double更适合科学计算这种不需要十进制精确的场景。
怎么验证浮点数的实际值?
如果你想亲眼看看double存的到底是什么,可以用这段代码打印高精度的数值:
double d = 9.995; Console.WriteLine(d.ToString("F20")); // 输出:9.99499999999999818101
这下就能明白为啥舍入结果和你预期的不一样了吧?
内容的提问来源于stack exchange,提问作者biscuitcleaver
相关产品推荐
相关产品推荐

