你能解释一下天花板方法的行为吗?
本文关键字:方法 天花板 一下 能解释 | 更新日期: 2023-09-27 18:24:26
我刚刚用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# 中应该是一样的。
浮点舍入。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
Ceiling
返回传递给它的数字,如果它们是整数,或者下一个最高的整数。所以5.0
保持5.0
但5.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
但123.12000000000002
最接近的可能double
是123.1200000000000181898940354586
所以这就是工作的内容。
如果您习惯于只做十进制算术,那么123.12000000000002
被"四舍五入"到 123.1200000000000181898940354586
似乎很奇怪,但请记住,这些数字以二进制值存储,舍入取决于您正在使用的基数。
因此,虽然字符串表示没有指示它,但它确实略高于12312
,因此它的上限是12313
。
同时对于123.12000000000001 * 100
,这在数学上是12312.000000000001
但最接近123.12000000000001
的可能double
是它可以适应是123.12
。所以这就是用于乘法的,当结果传递给后续调用以Ceiling()
时,它的结果是 12312
。
由于浮点舍入而不是 Math.Ceiling 本身,这是因为浮点值不能以 100% 的准确度表示所有值。
无论如何,您的示例有点做作,因为如果您尝试在 Visual Studio 中键入 123.12000000000001
会将其更改为 123.12
,因为它知道该值不能表示为双精度值。
在这里阅读: 每个计算机科学家都应该知道的关于浮点运算的知识(顺便说一句,这不是特定于.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 方法返回下一个较高的整数等效项。
所以天花板(123.01( = 124
天花板(123.0( = 123