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

时间:2015-11-26 11:15:58

标签: c# .net vb.net floating-accuracy

我刚用LINQPad进行了测试:

你能解释一下天花板方法为何如此反应?请注意中间的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#中应该是相同的。

4 个答案:

答案 0 :(得分:2)

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

 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

答案 1 :(得分:0)

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

无论如何,你的例子有点人为,因为如果你试图在visual studio中输入123.12000000000001,则将其更改为123.12,因为它知道该值不能表示为double。

在此处阅读: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) 

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

答案 2 :(得分:0)

Ceiling如果是整数,则返回传递给它的数字,否则返回下一个最高整数。因此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()返回"12312"时,123.12000000000002 * 100生成的实际数字在数学上12312.000000000002最近的double { {1}} 123.12000000000002的{​​1}}是123.1200000000000181898940354586,这就是正在进行的工作。

如果您习惯于只进行十进制算术,123.12000000000002被“舍入”到123.1200000000000181898940354586似乎很奇怪,但请记住,这些数字是以二进制值的形式存储的,舍入取决于你工作的基地。

因此虽然字符串表示没有表明它,但确实略高于12312,因此其上限为12313

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

答案 3 :(得分:-1)

Ceiling方法返回下一个更高的等效整数。

所以天花板(123.01)= 124

&安培;天花板(123.0)= 123