你能解释一下 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.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()
returns "12312"
123.12000000000002 * 100
实际产生的数字在数学上是12312.000000000002
123.12000000000002
最接近的可能 double
是 123.1200000000000181898940354586
所以这就是工作的内容。
如果你习惯于只做十进制运算,那么 123.12000000000002
是 "rounded" 到 123.1200000000000181898940354586
可能看起来很奇怪,但请记住,这些数字是以二进制值存储的,四舍五入取决于你工作的基础。
所以虽然字符串表示没有表明它,但它确实比 12312
稍微高一点,所以它的上限是 12313
.
与 123.12000000000001 * 100
同时,数学上是 12312.000000000001
,但最接近 double
到 123.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
我刚刚用 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.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()
returns "12312"
123.12000000000002 * 100
实际产生的数字在数学上是12312.000000000002
123.12000000000002
最接近的可能 double
是 123.1200000000000181898940354586
所以这就是工作的内容。
如果你习惯于只做十进制运算,那么 123.12000000000002
是 "rounded" 到 123.1200000000000181898940354586
可能看起来很奇怪,但请记住,这些数字是以二进制值存储的,四舍五入取决于你工作的基础。
所以虽然字符串表示没有表明它,但它确实比 12312
稍微高一点,所以它的上限是 12313
.
与 123.12000000000001 * 100
同时,数学上是 12312.000000000001
,但最接近 double
到 123.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