在 C89 中, floor() 返回一个双精度值。以下是否保证工作?
double d = floor(3.0 + 0.5);
int x = (int) d;
assert(x == 3);
我担心的是 floor 的结果在 IEEE 754 中可能无法完全表示。所以 d 得到类似 2.99999 的结果,x 最终为 2。
对于这个问题的答案是肯定的,一个 int 范围内的所有整数都必须可以精确地表示为双精度数,并且 floor 必须始终返回该精确表示的值。
在 C89 中, floor() 返回一个双精度值。以下是否保证工作?
double d = floor(3.0 + 0.5);
int x = (int) d;
assert(x == 3);
我担心的是 floor 的结果在 IEEE 754 中可能无法完全表示。所以 d 得到类似 2.99999 的结果,x 最终为 2。
对于这个问题的答案是肯定的,一个 int 范围内的所有整数都必须可以精确地表示为双精度数,并且 floor 必须始终返回该精确表示的值。
如果您的浮点类型支持所需的尾数位,则所有整数都可以具有精确的浮点表示。由于尾数使用 53 位,因此可以准确double
存储所有 32 位。int
毕竟,您可以将值设置为指数为零的尾数。
如果 floor() 的结果不能完全表示,您期望 d 的值是多少?当然,如果您在变量中获得了浮点数的表示,那么根据定义,它是完全可表示的,不是吗?你有代表在d ...
(此外,Mehrdad 的答案对于 32 位整数是正确的。在具有 64 位双精度和64 位整数的编译器中,您当然会遇到更多问题......)
编辑:也许您的意思是“ floor() 的理论结果,即小于或等于参数的最大整数值,可能无法表示为 int”。这当然是真的。在 int 为 32 位的系统中显示这一点的简单方法:
int max = 0x7fffffff;
double number = max;
number += 10.0;
double f = floor(number);
int oops = (int) f;
我不记得当从浮点转换为整数溢出时 C 做了什么……但它会在这里发生。
编辑:还有其他有趣的情况需要考虑。这是一些 C# 代码和结果 - 我想至少在 C 中会发生类似的事情。在 C# 中,double
定义为 64 位,long
.
using System;
class Test
{
static void Main()
{
FloorSameInteger(long.MaxValue/2);
FloorSameInteger(long.MaxValue-2);
}
static void FloorSameInteger(long original)
{
double convertedToDouble = original;
double flooredToDouble = Math.Floor(convertedToDouble);
long flooredToLong = (long) flooredToDouble;
Console.WriteLine("Original value: {0}", original);
Console.WriteLine("Converted to double: {0}", convertedToDouble);
Console.WriteLine("Floored (as double): {0}", flooredToDouble);
Console.WriteLine("Converted back to long: {0}", flooredToLong);
Console.WriteLine();
}
}
结果:
原始值:4611686018427387903
转换为双倍:4.61168601842739E + 18
楼层(如双):4.61168601842739E + 18
转换回长达:4611686018427387904
原装值:9223372036854775805
转换为双倍:9.22337203685478e + 18
楼层(如双):9.22337203685478e + 18
转换回长:-9223372036854775808
换句话说:
(long) floor((double) original)
并不总是相同original
。这应该不足为奇 - long 值比双精度值(给定 NaN 值)更多,并且大量双精度值不是整数,因此我们不能期望每个 long 都可以精确表示。但是,所有 32 位整数都可以表示为双精度数。
我认为您对要问的内容有些困惑。floor(3 + 0.5)
这不是一个很好的例子,因为 3、0.5 和它们的总和都可以在任何现实世界的浮点格式中精确表示。floor(0.1 + 0.9)
将是一个更好的例子,这里真正的问题不是结果floor
是否完全可表示,而是如果所有数字都是准确的,调用之前floor
数字的不准确是否会导致返回值与您期望的不同。在这种情况下,我相信答案是肯定的,但这在很大程度上取决于您的特定数字。
(1.0+0x1p-52)
如果它不好,我邀请其他人批评这种方法,但一种可能的解决方法可能是在调用之前将您的号码乘以或类似的东西floor
(也许使用nextafter
会更好)。这可以补偿数字的最后一个二进制位中的错误导致它刚好低于而不是正好落在整数值的情况,但它不会考虑在多次操作中累积的错误。如果您需要那种级别的数值稳定性/精确性,您需要进行一些深入分析或使用可以正确处理您的数字的任意精度或精确数学库。