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

时间:2015-11-20 10:57:04

标签: c++ casting floating-point

在编写C ++代码时,我突然意识到我的号码从double错误地投放到unsigned 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;
}

Ideone live example

在我的电脑上执行此代码时,我有以下输出:

18446744073709551615
1.84467e+019
9223372036854775808
Press any key to continue . . .

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

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

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

3 个答案:

答案 0 :(得分:12)

{p} 18446744073709551615double(IEEE754中)中并不完全具有代表性。这并不意外,因为64位浮点显然不能代表所有64位代表的整数。

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

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

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

  

浮点类型的prvalue可以转换为整数类型的prvalue。转换截断;也就是说,丢弃小数部分。如果截断的值无法在目标类型中表示,则行为未定义

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

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

答案 1 :(得分:1)

这个问题非常简单。这就是你的案例中发生的事情:

转换为18446744073709551615时,

double向上舍入到浮点可以表示的最接近的数字。 (最接近的可表示数字更大)。

当它转换回unsigned long long时,它会大于max()。在形式上,将其转换回unsigned long long的行为是 undefined ,但在您的情况下似乎发生的事情是环绕。

观察到的数字显着减少是这样的结果。

答案 2 :(得分:1)

归因于double近似long long。它的精度意味着10 ^ 19时约100个单位误差;当您尝试将值转换为长长范围的上限时,它会溢出。请尝试转换10000较低的值:)

顺便说一下,在Cygwin,第三个印刷值为零