floor()函数返回的结果是否是可以被准确表示的数值?

10

在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 必须总是返回该准确表示的值。


还可以查看https://dev59.com/UGjWa4cB1Zd3GeqPut3K - Kristian Spangsege
3个回答

14

如果您的浮点类型支持所需的尾数位数,所有整数都可以具有精确的浮点表示。由于double使用53位尾数,因此它可以完全存储所有32位int. 毕竟,您只需要将值设置为带有零指数的尾数。


尾数为零指数不包含任何超过2^(exp-bias)的数字。我认为你的意思是可以将对小数部分有贡献的位清零,如果指数<=52,则可以确保该数字的精确表示。 - MSN
MSN:我只是为了简单起见在理论上说话。从指数中,我所指的是无偏的指数,而不是存储在指数位中的实际内容。基本上,如果我在谈论实际位,双精度将具有52位尾数,而不是53位 ;) - Mehrdad Afshari

3
如果floor()的结果不能精确地表示,您希望d的值是什么?如果您已经在变量中拥有浮点数的表示,那么根据定义,它肯定是可以精确表示的,不是吗?您在d中已经拥有了表示...
(此外,Mehrdad的答案对于32位int是正确的。在具有64位double和64位int的编译器中,您当然会遇到更多问题...)
编辑:也许您的意思是“ 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
转换为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位整数都可以作为双精度浮点数来表示。


2
我认为你对自己想要问什么有些困惑。floor(3 + 0.5)不是一个很好的例子,因为在任何现实世界的浮点格式中,3、0.5及其和都可以精确表示。更好的例子是floor(0.1 + 0.9),而真正的问题并不是floor的结果是否可以被精确表示,而是在调用floor之前数字的不精确性是否会导致返回值与您预期的不同。在这种情况下,我认为答案是肯定的,但它非常依赖于您特定的数字。
我邀请其他人批评这种方法是否不好,但一个可能的解决方法是,在调用floor之前将您的数字乘以(1.0+0x1p-52)或类似的数字(也许使用nextafter会更好)。这可以弥补最后一位二进制中数字错误导致它刚好低于整数值的情况,但它不能解决多个操作中累积的误差。如果您需要那种级别的数值稳定性/精确性,您需要进行深入分析或使用一个可以正确处理您的数字的任意精度或精确数学库。

不,我认为人们正确地解释了我的问题。我想知道floor的(double中的整数)结果是否保证转换为它应该表示的整数。我明白浮点数运算可能是不精确的,如果括号内操作的结果与预期略有不同,floor可能会让你感到惊讶。 - Jim Hunziker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接