C#的Math.Ceiling有bug吗?

10

我不明白你的问题,但请确保Math.Ceiling()需要一个decimal或double参数。 - Serkan Hekimoglu
这里有什么问题?processingFee等于131,看起来没问题。 - Pierre-Alain Vigeant
3
我自嘲地笑了笑。我尝试向右滚动。 - maxwellb
现在我明白了每个人都应该知道的常见浮点问题。那么这个问题就是之前许多其他问题的重复。 - Pierre-Alain Vigeant
可能是为什么C#中的浮点运算不精确?的重复问题。 - Pierre-Alain Vigeant
显示剩余3条评论
7个回答

25

你的百分比实际上不是为0.05,而是一个接近于0.05的值......并且可能略大于0.05。因此当它乘以2600时,你得到的值略大于130.0......然后被"ceilinged"(向上取整)到131.0。

使用我写过的一个小工具(可从此页面了解 .NET 二进制浮点类型),看起来最接近0.05的实际float值为0.0500000007450580596923828125。对于双精度数,则为0.05000000000000000277555756156289135105907917022705078125。

故事的寓意:不要为此类事情使用float——使用decimal。或者,如果你只想表示百分比,并且实际上只需要精确到1%,则使用0-100的整数值即可。


1
@pixel3cs: 不要使用 double,它和 float 有同样的问题。decimal 避免这些问题的原因是它是基于十进制浮点数,而不是基于二进制浮点数。一般来说,对于处理货币,decimal 将是最安全的选择,因为 decimal 的内部表示与您尝试表示的内容相匹配(尽管效率较低)。 - Brian

4
这是涉及数字的浮点表示的结果。请参见维基百科。也许0.05作为双精度数有无限的二进制表示,因此Math.Ceiling实际看到的值可能略大于130。

要绝对地准确无误:小的(数学上的)整数,比如130,在IEEE浮点数(32位或64位)中具有准确的有限表示。在浮点数中可能表示不出OP认为等于130的(数学上的)接近130的实数。 - High Performance Mark

2
您看到的是浮点数不精确。
实际的二进制表示形式 0.05 稍微大于 0.05,因此乘积稍微大于 130.0
因此,Math.Ceiling 向上舍入。
将您的 floatdouble 更改为 decimal

1

这是由于浮点数的内部存储格式在十进制表示时本质上是不精确的。在 Stack Overflow 上有很多关于此问题的提问。

你返回的数字可能类似于 130.000000000000001,因为你的计算中的数字无法准确地表示为二进制浮点数。


1
嗯,130肯定可以被表示为二进制浮点数。然而,0.05不能。 - Jon Skeet
130的二进制表示为10000010,可以准确地表示为IEEE754浮点数。 - Martin Smith

1

在我看来,这可能与浮点精度有关。换句话说,2600 × 0.05 的结果是130.0000...001而不是130。

如果您先尝试将结果四舍五入,然后调用Math.Ceiling会怎样呢?


0

如果您在大型银行操作中使用浮点数,请不要让我把我的钱漂浮在您的银行里。请使用最小公分母的整数或小数。

但是,如果您对计算量的大小有所假设,可以使用 Math.Round 来帮助您使用 doublefloat

double processingFee = Math.Ceiling( Math.Round( 
    Settings.PaymentProcessingFeeInPercentage * prizesSum, 2 ) );

1
请注意,C#中的decimal仍然是浮点类型。它只是一个浮动的十进制点而不是一个浮动的二进制点。 - Jon Skeet
仍然“有效”,但只限于一定数量的有效数字。很好的区分。 - maxwellb

0
在我的编译器中,当我查找乘积值时,它显示为130.00000193715096,因此math.ceiling的结果是正确的。问题在于浮点数据类型的精度有限。
如果可能的话,请尝试使用“double”代替。

@maxwellb:不是针对Settings.PaymentProcessingFeeInPercentage的值。然而,使用“double”仍会给出“错误”的答案。在这里使用“float”或“double”是不合适的。 - Jon Skeet
啊,是的,谢谢。对于第二点也是这样。我的意思是,“double与float一样都不太合适”。 - maxwellb
在这种情况下,double类型可以得到期望的结果,但是我们知道最好的选择是分析数据所需的精度。 - Diego Pereyra
问题不在于浮点数据类型的有限精度,而在于对浮点运算的使用缺乏理解。因此,Stack Overflow上也涵盖了这种缺乏理解的情况。 - High Performance Mark
我同意,这就是为什么我说需要进行分析的原因。你甚至可以发现,你可以使用整数进行必要的算术运算而不会失去精度(并且可能更快)。 - Diego Pereyra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接