Если вы впервые сделаете это:
printf("%f\n", maxFloat);
Вывод, который вы получите:
4294967296.000000
Предполагая, что float
реализован как тип с плавающей запятой IEEE754 с одинарной точностью, значение 4294967295.0 не может быть точно представлено этим типом, поскольку недостаточно битов точности. Ближайшее значение, которое он может хранить, составляет 4294967296.0.
Если предположить, что int
(а также unsigned int
) - 32 бита, значение 4294967296.0 находится вне диапазона обоих этих типов. Преобразование типа с плавающей запятой в целочисленный тип, когда значение не может быть представлено в данном целочисленном типе, вызывает неопределенное поведение .
Это подробно описано в разделе 6.3.1.4 стандарта C , который предписывает преобразование типов с плавающей запятой в целочисленные типы:
1 Когда конечное значение действительного плавающего типа преобразуется в целочисленный тип, отличный от _Bool, дробная часть отбрасывается (т.е.
значение усекается до нуля). Если значение неотъемлемой части
не может быть представлен целочисленным типом, поведение не определено. 61)
...
61) Остаточная операция выполняется при значении целочисленного типа
преобразуется в беззнаковый тип не требуется, когда значение
реальный плавающий тип преобразуется в тип без знака. Таким образом, диапазон
переносимые действительные плавающие значения - (-1, Utype_MAX + 1).
Сноска в вышеприведенном отрывке ссылается на раздел 6.3.1.3, в котором подробно описываются преобразования целых чисел в целые:
1 Когда значение с целочисленным типом преобразуется в другой целочисленный тип, отличный от _Bool, если значение может быть представлено новым
тип, это без изменений.
2 В противном случае, если новый тип является беззнаковым, значение преобразуется путем многократного сложения или вычитания на единицу больше максимального значения, которое
может быть представлен в новом типе, пока значение не будет в диапазоне
новый тип.
3 В противном случае новый тип подписывается и значение не может быть представлено в нем; либо результат определяется реализацией или
определенный реализацией сигнал повышен.
Поведение, которое вы видите в первом фрагменте кода, согласуется с преобразованием вне диапазона в тип без знака , когда рассматриваемое значение является целым числом , однако, поскольку преобразовываемое значение имеет плавающее значение Тип точки это неопределенное поведение.
То, что одна реализация делает это, не означает, что все будет. На самом деле, gcc дает другой результат, если вы измените настройки оптимизации.
Например, на моей машине, использующей gcc 5.4.0, с кодом:
float n = 4294967296;
printf("n=%f\n", n);
unsigned int a = (unsigned int) n;
int b = (signed int) n;
unsigned int c = (unsigned int) (signed int) n;
printf("a=%u\n", a);
printf("b=%d\n", b);
printf("c=%u\n", c);
Я получаю следующие результаты с -O0:
n=4294967296.000000
a=0
b=-2147483648
c=2147483648
И это с -O1:
n=4294967296.000000
a=4294967295
b=2147483647
c=2147483647
Если, с другой стороны, n
определен как long
или long long
, вы всегда получите такой вывод:
n=4294967296
a=0
b=0
c=0
Преобразование в unsigned хорошо определено стандартом C, как указано выше, а преобразование в unsigned определяется реализацией, которая gcc определяет следующим образом:
Результат или сигнал, возникающий при преобразовании целого числа в целочисленный тип со знаком, когда значение не может быть представлено в объекте
этого типа (C90 6.2.1.2, C99 и C11 6.3.1.3).
Для преобразования в тип ширины N значение уменьшается по модулю 2 ^ N
находиться в пределах досягаемости типа; сигнал не подается.