双精度到长整型转换不正确

2024-04-05

这主要是后续这另一个问题 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 

这主要是质疑评论的汇编。

看来旧的 MSVC 版本过去常常错误地处理 64 位整数到 64 位双精度数的转换。

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

MSCV 2010 在 32 位模式下错误,在 64 位模式下正确

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

双精度到长整型转换不正确 的相关文章

随机推荐