【问题标题】:Does floor() return something that's exactly representable?floor() 是否返回完全可表示的东西?
【发布时间】:2010-10-01 05:04:08
【问题描述】:

在 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 必须始终返回该精确表示的值。

【问题讨论】:

标签: c floating-point ieee-754 c89 floor


【解决方案1】:

我认为您对要问的内容有些困惑。 floor(3 + 0.5) 不是一个很好的例子,因为 3、0.5 和它们的总和都可以在任何现实世界的浮点格式中精确表示。 floor(0.1 + 0.9) 会是一个更好的例子,这里真正的问题不是floor 的结果是否可以精确表示,而是在调用floor 之前 的数字的不精确性是否会导致如果所有数字都是准确的,则返回值与您的预期不同。在这种情况下,我相信答案是肯定的,但这很大程度上取决于您的具体数字。

如果这种方法不好,我会邀请其他人批评这种方法,但一种可能的解决方法是在调用floor 之前将您的号码乘以(1.0+0x1p-52) 或类似的值(也许使用nextafter 会更好)。这可以补偿数字的最后一个二进制位中的错误导致它刚好低于而不是正好落在整数值的情况,但它不会考虑在多次操作中累积的错误。如果您需要这种级别的数值稳定性/精确性,您需要进行一些深入分析或使用可以正确处理您的数字的任意精度或精确数学库。

【讨论】:

  • 不,我认为人们正确地解释了我的问题。我想知道 floor 的(双精度整数)结果是否保证转换为它应该表示的整数。我知道浮点数学可能不精确,如果括号内的运算结果与预期略有不同, floor 可能会让您大吃一惊。
【解决方案2】:

如果您的浮点类型支持所需的尾数位,则所有整数都可以具有精确的浮点表示。由于double 使用 53 位作为尾数,它可以准确地存储所有 32 位的ints。毕竟,您可以将值设置为指数为零的尾数。

【讨论】:

  • 指数为零的尾数不包含任何超过 2^(exp-bias) 的数字。我认为您的意思是您可以将有助于数字小数部分的位清零,如果指数
  • MSN:为了简单起见,我说的是理论上的。从指数开始,我的意思是无偏的,而不是存储在指数位中的实际事物。基本上,如果我在谈论实际位,double 将有 52 位尾数,而不是 53 ;)
【解决方案3】:

如果 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 位整数可以表示为双精度数。

【讨论】:

    猜你喜欢
    • 2012-01-25
    • 1970-01-01
    • 2013-08-23
    • 1970-01-01
    • 2012-05-20
    • 1970-01-01
    • 2010-10-22
    • 1970-01-01
    • 2014-07-07
    相关资源
    最近更新 更多