所有整数值都完美地表示为双精度值吗?

Are all integer values perfectly represented as doubles?

我的问题是是否保证所有整数值都具有完美的双精度表示。

考虑以下打印 "Same" 的代码示例:

// Example program
#include <iostream>
#include <string>

int main()
{
  int a = 3;
  int b = 4;
  double d_a(a);
  double d_b(b);

  double int_sum = a + b;
  double d_sum = d_a + d_b;

  if (double(int_sum) == d_sum)
  {
      std::cout << "Same" << std::endl;
  }
}

这是否保证对任何体系结构、任何编译器、ab 的任何值都是正确的?将任何整数 i 转换为 double,总是表示为 i.0000000000000 而不是,例如 i.000000000001

我尝试了一些其他数字,它总是正确的,但无法找到关于这是巧合还是设计的任何信息。

注意:这不同于 this question(语言除外),因为我将两个整数相加。

答案是否定的。这仅在 int 是 32 位的情况下有效,虽然在大多数平台上都是如此,但标准并不能保证。

两个整数可以共享相同的双精度表示。

例如,this

#include <iostream>
int main() {
    int64_t n = 2397083434877565865;
    if (static_cast<double>(n) == static_cast<double>(n - 1)) {
        std::cout << "n and (n-1) share the same double representation\n";
    }
}    

将打印

n and (n-1) share the same double representation

即2397083434877565865 和 2397083434877565864 都将转换为相同的 double.

请注意,我在这里使用 int64_t 来保证 64 位整数,这取决于您的平台 - 也可能是 int

免责声明(如 Toby Speight 所建议):虽然 IEEE 754 表示非常普遍,但允许实现使用满足语言要求的任何其他表示。


双精度数以mantissa * 2^exponent的形式表示,即一些位用于双精度数的非整数部分。

             bits        range                       precision
  float        32        1.5E-45   .. 3.4E38          7- 8 digits
  double       64        5.0E-324  .. 1.7E308        15-16 digits
  long double  80        1.9E-4951 .. 1.1E4932       19-20 digits

分数中的部分也可以用指数来表示整数,指数去掉点后的所有数字。

例如2,9979 · 10^4 = 29979。

因为一个普通的 int 通常是 32 位的,你可以将所有的 int 表示为双精度,但是对于 64 位整数当然这不再是真的。更准确地说(正如 LThode 在评论中指出的那样):IEEE 754 双精度最多可以保证 53 位(有效数的 52 位 + 隐式前导 1 位)。

答案:32 位整数是,64 位整数不是。

(这对于 server/desktop 通用 CPU 环境是正确的,但其他体系结构的行为可能不同。)

实用答案 正如 Malcom McLean 所说:对于几乎所有可能在现实生活中计数的整数,64 位双精度整数类型都是合适的。


对于经验倾向的人,请尝试 this:

#include <iostream>
#include <limits>
using namespace std;

int main() {
    double test;
    volatile int test_int;
    for(int i=0; i< std::numeric_limits<int>::max(); i++) {
        test = i;
        test_int = test;

        // compare int with int:
        if (test_int != i)
            std::cout<<"found integer i="<<i<<", test="<<test<<std::endl;
    }
    return 0;
}

Success time: 0.85 memory: 15240 signal:0


子问题: 关于分数差异的问题。是否有可能将一个整数转换为 double,它与正确的值相差一个分数,但由于四舍五入而转换回相同的整数?

答案是否定的,因为任何整数来回转换为相同的值,实际上在double中表示相同的整数值。对我来说,最简单的解释(由 ilkkachu 建议)是使用指数 2^exponent 步长必须始终是 2 的幂。因此,除了最大的52(+1符号)位整数之外,绝不会出现距离小于2的两个double值,这就解决了舍入问题。

没有。假设您有一个 64 位整数类型和一个 64 位浮点类型(这是典型的 double)。该整数类型有 2^64 个可能值,该浮点类型有 2^64 个可能值。但是其中一些浮点值(实际上是大多数)并不表示整数值,因此浮点类型可以表示的整数值少于整数类型。

简短的回答是 "possibly"。便携式答案是 "not everywhere".

这真的取决于您的平台,尤其是

  • double
  • 的大小和表示
  • 范围int

对于使用 IEEE-754 双精度的平台,如果 int 为 53 位或更小,则 可能 为真。对于int大于double的平台,显然是假的

您可能希望能够使用 std::numeric_limitsstd::nextafter 来调查运行时主机上的属性。

您有 2 个不同的问题:

Are all integer values perfectly represented as doubles?

其他人已经回答了(TL;DR:这取决于 intdouble 的精度)。

Consider the following code sample that prints "Same": [...] Is this guaranteed to be true for any architecture, any compiler, any values of a and b?

您的代码添加两个 int,然后 然后 将结果转换为双精度。 int 的总和 溢出某些值,但两个单独转换的 double 的总和不会(通常)。对于这些值,结果会有所不同。