在C89中,floor()返回一个double类型的值。以下代码是否保证可行?
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类型的值。以下代码是否保证可行?
double d = floor(3.0 + 0.5);
int x = (int) d;
assert(x == 3);
我的担忧是 floor 的结果可能无法精确地在 IEEE 754 中表示。因此,d 得到类似于 2.99999 的结果,最终 x 变成了 2。
要回答这个问题是肯定的,所有 int 范围内的整数必须能够被准确地表示为双精度浮点数,并且 floor 必须总是返回该准确表示的值。
如果您的浮点类型支持所需的尾数位数,所有整数都可以具有精确的浮点表示。由于double
使用53位尾数,因此它可以完全存储所有32位int
. 毕竟,您只需要将值设置为带有零指数的尾数。
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
转换为double类型: 4.61168601842739E+18
向下取整(作为double类型):4.61168601842739E+18
转换回long类型: 4611686018427387904
原始值:9223372036854775805
转换为double类型: 9.22337203685478E+18
向下取整(作为double类型):9.22337203685478E+18
转换回long类型: -9223372036854775808
换句话说:
(long) floor((double) original)
在IT技术中,original
并不总是与原始值相同。这并不奇怪 - 由于存在NaN值,长整型的数量比双精度浮点数更多,而且很多双精度浮点数不是整数,因此我们不能期望每个长整型都能够完全表示。然而,所有32位整数都可以作为双精度浮点数来表示。
floor(3 + 0.5)
不是一个很好的例子,因为在任何现实世界的浮点格式中,3、0.5及其和都可以精确表示。更好的例子是floor(0.1 + 0.9)
,而真正的问题并不是floor
的结果是否可以被精确表示,而是在调用floor
之前数字的不精确性是否会导致返回值与您预期的不同。在这种情况下,我认为答案是肯定的,但它非常依赖于您特定的数字。floor
之前将您的数字乘以(1.0+0x1p-52)
或类似的数字(也许使用nextafter
会更好)。这可以弥补最后一位二进制中数字错误导致它刚好低于整数值的情况,但它不能解决多个操作中累积的误差。如果您需要那种级别的数值稳定性/精确性,您需要进行深入分析或使用一个可以正确处理您的数字的任意精度或精确数学库。