39

我一直在与来自 SQL Decimal (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

如果小数位为奇数,则改为偶数。否则,它保持不变。

为什么我看到不一致的结果?额外的精度不会改变“小数位中的数字”......

4

4 回答 4

47

来自 MSDN:

ddecimals如果小数位右侧一个非零数字且其值为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

为什么我看到不一致的结果?

你不是。结果与文档完全一致。

于 2012-04-12T16:13:23.103 回答
26

来自 MSDN - Math.Round 方法(十进制,Int32)

如果 d 中小数点右侧有一个非零数字且其值为 5,则小数点位置的数字如果是奇数则四舍五入,如果是偶数则保持不变。如果 d 的小数位数少于小数,则 d 原样返回。

此方法的行为遵循 IEEE 标准 754 第 4 节。这种四舍五入有时称为四舍五入或银行家四舍五入。它可以最大限度地减少因在单个方向上持续舍入中点值而导致的舍入误差。

注意单个非零数字的使用。这对应于您的第一个示例,但不是第二个示例。

和:

要控制 Round(Decimal, Int32) 方法使用的舍入类型,请调用 Decimal.Round(Decimal, Int32, MidpointRounding) 重载。

于 2012-04-12T16:10:34.170 回答
4

“d 中小数点右侧的单个非零数字,其值为 5”部分解释了结果。只有当要舍入的部分正好是 0,5 时,舍入规则才起作用。

于 2012-04-12T16:12:23.997 回答
4

让我们将两个数字都向左移动 11 位:

38751876912.5
38751876912.50002636113061835

使用银行家四舍五入,我们将第一个四舍五入。在每个中点舍入系统下,我们将第二个数字向上舍入(因为它不在中点处)

.Net 正在做的正是我们所期望的。

于 2012-04-12T17:00:16.500 回答