.NET double类型大数Math.Round 1位舍入与小数表现差异
差异产生的核心原因
这个行为和MidpointRounding.AwayFromZero的舍入逻辑本身没有关系,本质是双精度浮点数的精度限制导致的输入值偏差:
- .NET中不带类型后缀的小数字面量默认是
double类型,该类型遵循IEEE 754双精度浮点数标准,仅能精确表示15~17位有效十进制数字。你代码中写的1111_1111_1111_1111.5包含16位整数+1位小数,总有效位数已经超出了double的精确表示范围。 - 你以为传入方法的是精确的1111111111111111.5这个中点值,但实际上这个值在被编译为
double存储时,就已经被自动近似为最接近的可表示值——这个值略大于1111111111111111.5,本质已经不是舍入规则定义里的“中点”,Math.Round拿到的输入本身就不是你预期的精确值,自然会返回1111111111111111.6。你可以直接执行Console.WriteLine(1111_1111_1111_1111.5 == 1111_1111_1111_1111.6)验证,返回结果会是true,证明这两个字面量在double类型下是同一个存储值。 - 而
0.5是2的-1次幂,完全落在double的精确表示范围内,传入方法的就是精确的0.5,保留1位小数时本身不需要做进位处理,返回结果自然符合预期。
如果需要对这类大数值做精确的小数舍入,不要使用
double类型,改用decimal类型即可:将字面量加上m后缀写为1111_1111_1111_1111.5m再传入Math.Round,就能得到预期的1111111111111111.5结果。decimal是128位高精度十进制类型,可精确表示最多28~29位有效十进制数字,不会出现这类浮点数近似偏差。
内容的提问来源于stack exchange,提问作者rstreet
相关产品推荐
相关产品推荐

