双向长转换不正确

时间:2015-11-20 14:26:38

标签: c++ visual-c++

这主要是对this other question的跟进,这是关于奇怪的从长到双的转换,然后再转回到长值的大值。

我已经知道将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;
}

输出是(在旧的XP 32盒子上使用MSVC 2008 32位):

43ee000000000000
f000000000000000
1.72938e+019
8000000000000000

价值解释:

  • 0xf000000000000000是十进制的17293822569102704640,因此转换为double是正确的。
  • 43ee000000000000:尾数部分是e000000000000加上隐含的1它正确表示4位1后跟0 - 指数是43e,在移除3ff偏差后它给出了1.111 2 63 因此确切表示0xf000000000000000或17293822569102704640(ref

由于该值可以表示为无符号长long,我预计它转换为无符号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 

1 个答案:

答案 0 :(得分:1)

这主要是对问题的评论汇编。

旧的MSVC版本似乎错误地将64位整数的转换处理为64位双精度数。

2008年以下版本中存在的错误。

MSCV 2010在使用32位模式时出错并在64位模式下正确

从2012年开始的所有版本都是正确的。