这主要是后续这另一个问题 https://stackoverflow.com/q/33824954/3545273,那是关于一个weird对于大值,从 long 转换为 double,然后再转换回 long。
我已经知道将浮点数转换为整数类型会截断,如果截断的值无法在目标类型中表示,则行为未定义:
4.9 浮点积分转换 [conv.fpint]
浮点类型的纯右值可以转换为整数类型的纯右值。转换截断;
也就是说,小数部分被丢弃。如果无法截断值,则行为未定义
以目标类型表示。
但这里是我的代码来演示该问题,假设采用小端架构,其中 long long 和 long double 使用 64 位:
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
unsigned long long ull = 0xf000000000000000;
long double d = static_cast<long double>(ull);
// dump the IEE-754 number for a little endian system
unsigned char * pt = reinterpret_cast<unsigned char *>(&d);
for (int i = sizeof(d) -1; i>= 0; i--) {
cout << hex << setw(2) << setfill('0') << static_cast<unsigned int>(pt[i]);
}
cout << endl;
unsigned long long ull2 = static_cast<unsigned long long>(d);
cout << ull << endl << d << endl << ull2 << endl;
return 0;
}
输出是(在旧的 XP 32 机器上使用 MSVC 2008 32 位):
43ee000000000000
f000000000000000
1.72938e+019
8000000000000000
值的解释:
- 0xf000000000000000 十进制为 17293822569102704640,因此转换为 double 是正确的。
- 43ee000000000000 : mantissa part is e000000000000 adding the implied 1 it correctly represents 4 bits with
1
followed with 0
- exponent is 43e after removing the 3ff bias it gives a binary representation of 1.111 263 so the exact representation of 0xf000000000000000 or 17293822569102704640 (ref https://en.wikipedia.org/wiki/Floating_point)
由于该值可以表示为 unsigned long long,因此我期望将其转换为 unsigned long long 给出原始值,并且 MSVC 给出 0x8000000000000000 或 9223372036854775808
问题是:这种转换是由另一个问题的接受答案所建议的未定义行为引起的,还是真的是 MSVC 错误?
(注意:FreeBSD 10.1 机器上的 CLang 编译器上的相同代码给出了正确的结果)
作为参考,我可以找到生成的代码:
unsigned long long ull2 = static_cast<unsigned long long>(d);
0041159E fld qword ptr [d]
004115A1 call @ILT+490(__ftol2) (4111EFh)
004115A6 mov dword ptr [ull2],eax
004115A9 mov dword ptr [ebp-40h],edx
_ftol2 的代码似乎是(在执行时从调试器获得):
00411C66 push ebp
00411C67 mov ebp,esp
00411C69 sub esp,20h
00411C6C and esp,0FFFFFFF0h
00411C6F fld st(0)
00411C71 fst dword ptr [esp+18h]
00411C75 fistp qword ptr [esp+10h]
00411C79 fild qword ptr [esp+10h]
00411C7D mov edx,dword ptr [esp+18h]
00411C81 mov eax,dword ptr [esp+10h]
00411C85 test eax,eax
00411C87 je integer_QnaN_or_zero (411CC5h)
00411C89 fsubp st(1),st
00411C8B test edx,edx
00411C8D jns positive (411CADh)
00411C8F fstp dword ptr [esp]
00411C92 mov ecx,dword ptr [esp]
00411C95 xor ecx,80000000h
00411C9B add ecx,7FFFFFFFh
00411CA1 adc eax,0
00411CA4 mov edx,dword ptr [esp+14h]
00411CA8 adc edx,0
00411CAB jmp localexit (411CD9h)
00411CAD fstp dword ptr [esp]
00411CB0 mov ecx,dword ptr [esp]
00411CB3 add ecx,7FFFFFFFh
00411CB9 sbb eax,0
00411CBC mov edx,dword ptr [esp+14h]
00411CC0 sbb edx,0
00411CC3 jmp localexit (411CD9h)
00411CC5 mov edx,dword ptr [esp+14h]
00411CC9 test edx,7FFFFFFFh
00411CCF jne arg_is_not_integer_QnaN (411C89h)
00411CD1 fstp dword ptr [esp+18h]
00411CD5 fstp dword ptr [esp+18h]
00411CD9 leave
00411CDA ret