我一直在与来自SQL十进制(38,30)的C#中的十进制精度作斗争,我终于做到了四舍五入的奇怪之处。 我知道我可能忽略了这里显而易见的事情,但我需要一点洞察力。
我遇到的问题是 C# 不会产生我认为一致的输出。
decimal a = 0.387518769125m;
decimal b = 0.3875187691250002636113061835m;
Console.WriteLine(Math.Round(a, 11));
Console.WriteLine(Math.Round(b, 11));
Console.WriteLine(Math.Round(a, 11) == Math.Round(b, 11));
收益 率
0.38751876912
0.38751876913
False
呃,0.38751876913?真? 我在这里错过了什么?
从 MSDN:
如果小数位的数字是奇数,则将其更改为偶数。否则,它将保持不变。
为什么我看到的结果不一致? 额外的精度不会改变"小数位的数字"......
来自 MSDN:
如果在小数点
decimals
位右侧d
有一个非零数字,并且其值为5
,则小数点位置的数字如果为奇数,则向上舍入,如果为偶数,则保持不变。如果d
的小数位数少于decimals
,则返回d
不变。
在您的第一种情况下
decimal a = 0.387518769125m;
Console.WriteLine(Math.Round(a, 11));
第 11 位右侧有一个数字,该数字是 5
.因此,由于位置 11 是偶数,因此保持不变。因此,你得到
0.38751876912
在第二种情况下
decimal b = 0.3875187691250002636113061835m;
Console.WriteLine(Math.Round(b, 11));
第 11 位右侧没有一位数。因此,这是直接的小学四舍五入;如果下一个数字大于 4,则向上舍入,否则向下舍入。由于第 11 位右侧的数字大于 4(是 5),因此我们四舍五入,以便您看到
0.38751876913
为什么我看到的结果不一致?
你不是。结果与文档完全一致。
来自 MSDN - Math.Round Method (十进制, int32):
如果小数点小数位右侧的 d 中有一个非零数字,并且其值为 5,则小数点位置的数字如果为奇数,则向上舍入,如果为偶数,则保持不变。如果 d 的小数位数少于小数位数,则返回 d 不变。
此方法的行为遵循 IEEE 标准 754 第 4 节。这种四舍五入有时称为四舍五入到最接近,或银行家四舍五入。它最大限度地减少了在单个方向上一致舍入中点值而导致的舍入误差。
请注意使用单个非零数字。这对应于您的第一个示例,但不是第二个示例。
和:
若要控制 Round(十进制,Int32)方法使用的舍入类型,请调用 Decimal.Round(十进制,Int32,中点舍入)重载。
"小数点位置右侧 d 中的单个非零数字及其值为 5"部分解释了结果。只有当要舍入的部分正好是 0,5 时,舍入规则才会发挥作用。
两个数字都向左移动 11 位:
38751876912.5
38751876912.50002636113061835
使用银行家的四舍五入,我们将第一个四舍五入。 在每个中点舍入系统下,我们将第二个数字向上舍入(因为它不在中点)。
.Net正在做我们所期望的。