这主要是对 另一个问题 的跟进,那个问题涉及到对于大值从 long 转换为 double 再转回 long 时出现的奇怪情况。
我已经知道将浮点数转换为整数类型会进行截断。如果被截断的值不能在目标类型中表示,则行为未定义:
我已经知道将浮点数转换为整数类型会进行截断。如果被截断的值不能在目标类型中表示,则行为未定义:
以下是我的代码来演示这个问题,假设使用 little endian 架构,其中 long long 和 long double 都使用 64 位:4.9 浮点-整数转换 [conv.fpint]
浮点类型的 prvalue 可以转换为整数类型的 prvalue。转换会截断;也就是说,小数部分会被舍去。如果被截断的值不能在目标类型中表示,则行为未定义。
#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:尾数部分是e000000000000,加上隐含的1,它正确表示了带有
1
和0
的 4 位 - 指数为43e,去除3ff偏差后,它给出了一个二进制表示式1.111 263,所以精确表示了0xf000000000000000或17293822569102704640(参考)。
由于该值可以表示为 unsigned long long,我期望将其转换为 unsigned long long 会得到原始值,而 MSVC 给出的结果是0x8000000000000000或9223372036854775808。
问题是:这个转换是否导致未定义行为,正如另一个问题的被接受答案所建议的那样,还是这真的是 MSVC 的一个 bug?
(注意:在 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
43ee000000000000
,f000000000000000
,1.72938e+019
,f000000000000000
- Simon Kraemerf00...
- Petr