从无符号long long转换为double,反之亦然,可以更改值

ale*_*in0 13 c++ floating-point casting

在编写C++代码时,我突然意识到我的数字被错误地转换doubleunsigned long long.

具体来说,我使用以下代码:

#define _CRT_SECURE_NO_WARNINGS

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

int main()
{
  unsigned long long ull = numeric_limits<unsigned long long>::max();
  double d = static_cast<double>(ull);
  unsigned long long ull2 = static_cast<unsigned long long>(d);
  cout << ull << endl << d << endl << ull2 << endl;
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

Ideone现场例子.

在我的计算机上执行此代码时,我有以下输出:

18446744073709551615
1.84467e+019
9223372036854775808
Press any key to continue . . .
Run Code Online (Sandbox Code Playgroud)

我期望第一个和第三个数字完全相同(就像在Ideone上一样),因为我确信它long double占用了10个字节,并将尾数存储在其中的8个中.我会理解,如果第三个数字与第一个数字相比被截断 - 只是因为浮点数格式的错误.但这里的价值是两倍不同!

所以,主要的问题是:为什么?我怎样才能预测出这种情况呢?

一些细节:我在Windows 7上使用Visual Studio 2013,在x86上使用Visual Studio 2013,sizeof(long double) == 8在我的系统中使用.

M.M*_*M.M 13

18446744073709551615double(在IEEE754中)并不完全具有代表性.这并不是意料之外的,因为64位浮点显然不能代表64位中可表示的所有整数.

根据C++标准,它是实现定义的,是使用下一个最高double值还是下一个最低值.显然在您的系统上,它选择下一个最高值,这似乎是1.8446744073709552e19.您可以通过输出具有更多精度数字的double来确认这一点.

请注意,这大于原始数字.

将此double转换为整数时,行为将由[conv.fpint]/1覆盖:

可以将浮点类型的prvalue转换为整数类型的prvalue.转换截断; 也就是说,丢弃小数部分.该行为是未定义的,如果截断值不能在目标类型来表示.

因此,此代码可能会导致未定义的行为.当发生未定义的行为时,可能发生任何事情,包括(但不限于)伪造输出.


这个问题最初发布的是long double,而不是double.在我的gcc上,long double案例表现正确,但在OP的MSVC上它给出了同样的错误.这可以用gcc使用80位来解释long double,但MSVC使用64位long double.