приведите float к unsigned int в C с помощью gcc - PullRequest
0 голосов
/ 02 мая 2018

Я использую gcc для проверки некоторых простых приведений между float и unsigned int.

Следующий фрагмент кода дает результат 0.

const float maxFloat = 4294967295.0;
unsigned int a = (unsigned int) maxFloat;
printf("%u\n", a);

0 напечатано (что я считаю очень странным).

С другой стороны, следующий фрагмент кода:

const float maxFloat = 4294967295.0;
unsigned int a = (unsigned int) (signed int) maxFloat;
printf("%u\n", a);

печатает 2147483648, что, я считаю, правильные результаты.

Что происходит, когда я получаю 2 разных результата?

Ответы [ 2 ]

0 голосов
/ 02 мая 2018

Если вы впервые сделаете это:

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 находиться в пределах досягаемости типа; сигнал не подается.

0 голосов
/ 02 мая 2018

Если предположить, что числа с плавающей точкой IEEE 754, число 4294967295.0 не может быть сохранено точно в float. Вместо этого он будет сохранен как 4294967296.0 (то есть 2 32 ).

Кроме того, если предположить, что ваш unsigned int имеет 32 бита значения, это на единицу слишком большое, чтобы поместиться в unsigned int, поэтому результат преобразования не определен в соответствии со стандартом C - 0 представляет собой " разумный "исход.

Во втором случае у вас также есть неопределенное поведение , и у меня нет теории, что происходит здесь на уровне представления. Факт в том, что число много слишком велико для 32-битного со знаком int (все еще предполагается, что это то, что использует ваша машина).


Из этого замечания в вашем вопросе:

отпечатки 2147483648, которые, как я верю, являются правильными.

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

int main(void) {
    const float maxFloat = 4294967295.0;
    unsigned char *floatBytes = &maxFloat;
    for (int i=0; i < sizeof maxFloat; ++i)
    {
        printf("0x%02x ", floatBytes[i]);
    }
    puts("");
}

онлайн пример

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...