Подтвердить что ты не робот

Неверное преобразование с двойным или длинным преобразованием

В основном это вопрос о этом другом вопросе, который был о странном преобразовании из длинного в двойное и обратно к длинному для больших значений.

Я уже знаю, что преобразование float в интегральный тип усекает, если это усеченное значение не может быть представлено в типе цели, поведение undefined:

4.9 Преобразования с плавающим интегралом [conv.fpint]

Значение значения типа с плавающей запятой может быть преобразовано в prvalue целочисленного типа. Преобразование усекает; т.е. дробная часть отбрасывается. Поведение undefined, если усеченное значение не может быть представленный в типе адресата.

Но вот мой код, чтобы продемонстрировать проблему, предполагая небольшую архитектуру endian, где и длинные, и длинные двойные используют 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;
}

Выход (с использованием MSVC 2008 32bits в старом окне XP 32):

43ee000000000000
f000000000000000
1.72938e+019
8000000000000000

Объяснения для значений:

  • 0xf000000000000000 - это значение 17293822569102704640 в десятичной форме, поэтому преобразование в double правильное.
  • 43ee000000000000: mantissa part is e000000000000, добавив подразумеваемый 1, он правильно представляет 4 бита с 1, за которым следует 0 - показатель составляет 43e после удаления смещения 3ff, он дает двоичное представление 1.111 2 63 так точное представление 0xf000000000000000 или 17293822569102704640 (ref)

Поскольку это значение может быть представлено как unsigned long long, я ожидал, что его преобразование в unsigned long long дает исходное значение, а MSVC дает 0x8000000000000000 или 9223372036854775808

Возникает вопрос: является ли преобразование, вызванное undefined поведением, предложенным принятым ответом на другой вопрос, или это действительно ошибка MSVC?

(Примечание: тот же код в компиляторе CLang в поле FreeBSD 10.1 дает правильные результаты)

В качестве ссылок я мог бы найти сгенерированный код:

  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 
4b9b3361

Ответ 1

Это в основном компиляция комментариев к вопросу.

Похоже, что старые версии MSVC использовались для неправильной обработки конверсий из 64-битных целых чисел в число бит с двойной точностью до 64 бит.

Ошибка, присутствующая в версиях ниже 2008 года.

MSCV 2010 неправильно использует 32-битный режим и исправляет в режиме 64 бит

Все версии, начиная с 2012 года, верны.