这主要是对另一个问题的跟进,即关于从长到双的奇怪转换,再回到长期的大值转换.
我已经知道将float转换为整数类型会截断,如果截断的值无法在目标类型中表示,则行为是未定义的:
4.9浮动积分转换[conv.fpint]
可以将浮点类型的prvalue转换为整数类型的prvalue.转换截断; 也就是说,丢弃小数部分.如果截断的值无法在目标类型中表示,则行为未定义.
但这是我的代码来演示这个问题,假设有一个小的endian架构,其中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;
}
Run Code Online (Sandbox Code Playgroud)
输出是(在旧的XP 32机箱上使用MSVC 2008 32位):
43ee000000000000
f000000000000000
1.72938e+019
8000000000000000
Run Code Online (Sandbox Code Playgroud)
价值解释:
1跟随0- 指数是43e后删除3ff偏差它给出了1.111 2 63的二进制表示所以精确表示0xf000000000000000或17293822569102704640(ref)由于该值可以表示为无符号长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
Run Code Online (Sandbox Code Playgroud)
并且_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
Run Code Online (Sandbox Code Playgroud)
这主要是质疑评论的汇编。
看来旧的 MSVC 版本过去常常错误地处理 64 位整数到 64 位双精度数的转换。
该错误存在于 2008 年以下的版本中。
MSCV 2010 在 32 位模式下错误,在 64 位模式下正确
从 2012 年开始的所有版本都是正确的。