Неявно листинг постоянной поплавка
Пожалуйста, посмотрите на этот код:
#include <stdio.h>
int main(void)
{
short s;
int i = 65696;
float f = 65696.0F;
printf("sizeof(short) = %lu\n", sizeof(short));
s = i;
printf("s = %hd\n", s);
s = f;
printf("s = %hd\n", s);
s = 65696;
printf("s = %hd\n", s);
s = 65696.0F;
printf("s = %hd\n", s);
return 0;
}
Он дал результат как:
sizeof(short) = 2
s = 160
s = 160
s = 160
s = 32767
В последней строке почему это 32767, а не 160? Какая разница между словами f = 65696.0F; s = f;
и s = 65696.0F;
?
Ответы
Ответ 1
Потому что, если интегральная часть значения float не является представимой в новом типе, преобразование будет undefined.
В вашем случае SHRT_MAX
, вероятно, 32767, и поэтому неотъемлемая часть 65696.0F
тогда не представляется в объекте short
.
Ответ 2
Это "undefined поведение", что означает, что компилятор свободен делать то, что он хочет. Но "undefined" не означает "необъяснимый".
Что делает компилятор в случае s = f
, преобразование f
сначала в значение int
65696, а затем назначение 65696 в s, которое переполняет и покидает 160. Компилятор делает это, потому что есть CPU для преобразования числа с плавающей запятой в 32-разрядное целое число, но не непосредственно в 16-разрядное целое число
Что делает компилятор с s = 65696.0F
проще: он знает, что 65696.0 вне диапазона, поэтому он присваивает наивысшее значение, доступное для s
, которое составляет 2 ^ 15-1 = 32767.
Это можно проверить, если вы читаете код сборки, который генерирует компилятор для s = f (например, используя ключ -S с gcc):
movss -4(%rbp), %xmm0 # Load float from memory into register xmm0
cvttss2si %xmm0, %eax # Convert float in xmm0 into signed 32 bit, store in eax
movw %ax, -10(%rbp) # Store lower 16 bits of eax into memory
movswl -10(%rbp), %eax # Load those 16 bits into eax, with sign extend
Последняя команда сжимает высокие 16 бит% eax, устанавливая в этом случае все 0s.
То, что он генерирует для s = 65696.0F, проще:
movw $32767, -10(%rbp) # Store the lower 16 bits of 32767 into memory
movswl -10(%rbp), %eax # Load those 16 bits into eax, with sign extend