В основном это вопрос о этом другом вопросе, который был о странном преобразовании из длинного в двойное и обратно к длинному для больших значений.
Я уже знаю, что преобразование 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