你能解释一下 Ceiling 方法的行为吗?

Could you explain the behavior of Ceiling method?

我刚刚用 LINQPad 做了一个测试:

你能解释一下吗why/how天花板方法是这样反应的?注意中间的 123.12。

Math.Ceiling(123.121 * 100) / 100 'display 123.13
Math.Ceiling(123.1200000000001 * 100) / 100 'display 123.13
Math.Ceiling(123.12000000000001 * 100) / 100 'display 123.12
Math.Ceiling(123.12000000000002 * 100) / 100 'display 123.13

我在 VB.NET 中进行了测试,但在 C# 中应该是相同的。

上限法 returns 下一个更高的整数等价物。

所以天花板(123.01) = 124

& 上限(123.0) = 123

这是由于浮点舍入而不是 Math.Ceiling 本身,这是因为浮点值不能 100% 准确地表示所有值。

无论如何,您的示例有点做作,因为如果您尝试在 visual studio 中键入 123.12000000000001,则会将其更改为 123.12,因为它知道该值不能表示为双精度.

在此处阅读:What Every Computer Scientist Should Know About Floating-Point Arithmetic(顺便说一句,这不是特定于 .NET 的)

解决您的问题,您可以使用十进制值而不是双精度值。 Math.Ceiling 有一个接受小数的重载(所有这些都显示 123.13):

   Debug.WriteLine(Math.Ceiling(123.121D * 100) / 100) 
   Debug.WriteLine(Math.Ceiling(123.1200000000001D * 100) / 100)
   Debug.WriteLine(Math.Ceiling(123.12000000000001D * 100) / 100) 
   Debug.WriteLine(Math.Ceiling(123.12000000000002D * 100) / 100) 

此修复是否合适当然取决于您需要的准确度级别。

Ceiling returns 如果它们是整数,则传递给它的数字,否则是下一个最大的整数。所以 5.0 保持 5.05.00001 变成 6.0.

因此,在示例中,以下是显而易见的:

Math.Ceiling(123.121 * 100) / 100 // Obtain 12312.1, next highest is 12313.0, then divide by 100 is 123.13
Math.Ceiling(123.1200000000001 * 100) / 100 // Likewise
Math.Ceiling(123.12000000000002 * 100) / 100 // Likewise

比较混乱的是:

Math.Ceiling(123.12000000000001 * 100) / 100 //display 123.12

不过,我们来看一下:

123.12000000000001 * 100 - 12312.0 // returns 0

相比于:

123.1200000000001 * 100 - 12312.0 // returns 1.09139364212751E-11
123.12000000000002 * 100 - 12312.0 // returns 1.81898940354586E-12

后两次乘法的结果略高于12312.0,所以虽然(123.12000000000002 * 100).ToString() returns "12312" 123.12000000000002 * 100实际产生的数字在数学上是12312.000000000002 123.12000000000002 最接近的可能 double123.1200000000000181898940354586 所以这就是工作的内容。

如果你习惯于只做十进制运算,那么 123.12000000000002 是 "rounded" 到 123.1200000000000181898940354586 可能看起来很奇怪,但请记住,这些数字是以二进制值存储的,四舍五入取决于你工作的基础。

所以虽然字符串表示没有表明它,但它确实比 12312 稍微高一点,所以它的上限是 12313.

123.12000000000001 * 100 同时,数学上是 12312.000000000001,但最接近 double123.12000000000001 的可能是 123.12。这就是用于乘法的结果,当结果传递给后续调用 Ceiling() 时,其结果为 12312.

这是浮点数舍入。 C# 将 123.12000000000001 和 123.12 解析为具有相同的值。 123.12000000000002 被解析为下一个可用的 double。

 var bytes = BitConverter.ToString(BitConverter.GetBytes(123.12));
 // outputs 48-E1-7A-14-AE-C7-5E-40
 var bytes1 = BitConverter.ToString(BitConverter.GetBytes(123.12000000000001));
 // outputs 48-E1-7A-14-AE-C7-5E-40
 var bytes2 = BitConverter.ToString(BitConverter.GetBytes(123.12000000000002));
 // outputs 49-E1-7A-14-AE-C7-5E-40