2012-04-12 52 views
37

我一直在争取C#中的十进制精度来自一个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

如果小数位置中的数字为奇数,它被改变成偶数位。否则,保持不变。

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

+4

它向下舍入的值<= 0.5,> 0.5的值。 (即“0.50000”被取整,“0.50002 ...”被取整) - 这不是你期望的吗?请参阅http://msdn.microsoft.com/en-us/library/ms131274.aspx – 2012-04-12 16:13:36

+0

@Jason Williams:不完全。如果小于0.5,则向下舍入,如果> 0.5,则向上舍入,然后如果== 0.5则使用银行家舍入。 – jason 2012-04-12 16:21:58

+18

您期待的圆形函数可以舍入*更远的数字*?你能解释为什么你会期望在任何情况下? (这不是一个反问题的问题,我有兴趣了解为什么人们会相信很奇怪的东西,这个问题其实很常见,但我仍然不明白为什么那么多人认为* round *应该围绕* number更远*) – 2012-04-12 16:35:18

回答

42

从MSDN:

如果存在单个非零数字在d向右侧decimals小数点位置并且它的值是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 

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

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

+2

只是为了确保我在这里能够理解,ToEven或AwayFromZero会根据数字的精度而改变默认值? – 2012-04-12 16:48:06

+0

我喜欢你的答案,我认为它显示了MSDN文档中缺少的东西,也就是说,如果它不是一个非零数字 – 2012-04-12 17:42:30

+4

@BennettDill“单个非零数字等于5”是一种混淆的方式,表示“方法的参数恰好在两个可能的返回值之间的一半”。如果*没有*右边的一个数字指定的地方,或者如果只有一个不是5的数字,那么参数并不完全在可能的返回值之间,所以你选择了更接近参数的返回值。啊,如果我们只使用了基数3数字,我们不必担心这个! – phoog 2012-04-12 18:56:54

26

从MSDN - Math.Round Method (Decimal, Int32)

如果有一个非零数字在d到小数的权小数点位置,其值为5,小数点位置的数字为奇数时为四舍五入,如果为偶数则保持不变。如果d的小数位数小于小数位数,则d不变。

此方法的行为遵循IEEE标准754第4部分。这种舍入有时称为四舍五入到舍入,或银行家舍入。它最大限度地减少了在一个方向上始终舍入中点值所导致的舍入误差。

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

和:

为了控制舍入所使用的轮(十进制,Int32)方法的类型,调用Decimal.Round(十进制,的Int32,MidpointRounding)过载。

+0

我不认为我的问题是使用哪种类型的舍入。我相信它不理解MidpointRounding.ToEven本身的行为。 :-(虽然这个问题的答案和评论已经明确地说明了,但是要感谢所有人! – 2012-04-12 20:39:48

4

该部分“小数点右侧小数点后面的单个非零数位,其值为5”说明结果。只有当轮的部分恰好为0,5时,舍入规则才会起作用。

+0

从MSDN的文档中,我不清楚这一点。感谢您的解释。 – 2012-04-12 17:45:09

4

让我们这两个数字超过11位左移:

38751876912.5
38751876912.50002636113061835

用银行家舍,我们轮第一个下来。在中点舍入系统,我们围绕第二个数字(因为它不在中点)

.net正在做我们期望的。