Является ли (uint64_t) -1 гарантией получения 0xffffffffffffffff?

Я знаю, что он хорошо определен стандартом C, что (unsigned)-1 должен давать 2 ^ n-1, i. е. целое число без знака со всеми его битами. То же самое касается (uint64_t)-1ll. Однако я не могу найти что-то в стандарте C11, который указывает, как интерпретировать (uint64_t)-1.

Итак, вопрос: есть ли какая-либо гарантия в стандарте C, какое из следующих утверждений верно?

(uint64_t)-1 == (uint64_t)(unsigned)-1   //0x00000000ffffffff
(uint64_t)-1 == (uint64_t)(int64_t)-1    //0xffffffffffffffff

Ответ 1

Да. См. C11 6.3.1.3 Целочисленные и беззнаковые целые числа:

1 Когда значение с целым типом преобразуется в другой целочисленный тип, отличный от _Bool, если значение может быть представлено новым типом, оно не изменяется.

2 В противном случае, если новый тип без знака, значение преобразуется путем многократного добавления или вычитания более чем максимального значения, которое может быть представлено в новом типе, пока значение не будет в диапазоне нового типа. 60)

3 В противном случае новый тип будет подписан и значение не может быть представлено в нем; либо результат определяется реализацией, либо генерируется сигнал, определяемый реализацией.

60) Правила описывают арифметику по математическому значению, а не значение данного типа выражения.

применяется случай 2, поэтому -1 уменьшается по модулю 0x10000000000000000, чтобы получить 0xffffffffffffffffff.

Ответ 2

Выражения 1 и -1 имеют тип int. При преобразовании в uint64_t принцип, что 2 n добавляется или вычитается до тех пор, пока значение не находится в диапазоне, поэтому результат всегда равен 2 n -1, в этом случай с n = 64. Следовательно, (uint64_t)-1 всегда 2 64 -1..

Выражение (int64_t)-1 оценивается как -1, поэтому одно и то же рассуждение относится к выражению (uint64_t)(int64_t)-1, которое тоже всегда оценивается как 2 64 -1.

С другой стороны, (unsigned)-1 является положительным значением типа unsigned int, которое может быть 2 16 -1, 2 32 -1, 2 64 -1 или различные другие значения в зависимости от платформы компиляции. Эти значения могут не вызывать 2 64 -1 при преобразовании в uint64_t.

Ответ 3

Я предполагаю, что вы пишете (uint64_t)-1 вместо -1ULL, потому что не хотите делать предположения о размере unsigned long long? Если да, то хорошо. Однако есть альтернатива, которая еще не упоминалась (и на самом деле не отвечает на ваш вопрос), но может сэкономить много беспокойства, поставив вопрос:

Альтернативный

Хорошая привычка находиться в том, чтобы всегда использовать UINT64_C(x) вместо (uint64_t)x. Это макрос, определенный в <stdint.h>, который автоматически добавляет U, UL или ULL по мере необходимости. Таким образом, UINT64_C(-1) разрешается либо -1U, -1UL, либо -1ULL, в зависимости от вашей цели. Это гарантировано всегда работает правильно.

Опасности литья типов

Обратите внимание, что (uint64_t)x фактически вообще не работает нормально. Например,

(uint64_t)2147483648                // RISKY

генерирует предупреждение для некоторых компиляторов, потому что значение 2147483648 (2 ^ 31) слишком велико для ввода в 32-битное целое число, и следующее не работает даже удаленно:

(uint64_t)1000000000000000000       // RISKY

Однако, если вместо этого вы используете UINT64_C(), тогда все будет золотым:

UINT64_C(2147483648)                // GOOD

UINT64_C(1000000000000000000)       // GOOD

UINT64_C(-1)                        // GOOD

Примечания:

  • Суффикс _C означает "константа".
  • В <stdint.h> есть также 8-, 16- и 32-разрядные версии для значений с подписью и без знака.
  • Для частного случая -1 вы также можете просто написать UINT64_MAX.

Ответ 4

Это вопрос, на который можно ответить несколькими строками кода.

#include <stdio.h>

main()
{
    int          x;
    unsigned int y;

    x = -1;
    printf("\n0x%08x", x);

    y = (unsigned int) -1;
    printf("\n0x%08x", y);

}

Запуск этого кода в компиляторе Eclipse/Microsoft C создает:

0xffffffff
0xffffffff

Аналогичная программа может показать вам, что производит uint64_t.

Наконец, если вы понимаете, как компьютеры используют 2 номера дополнений для добавления чисел, тогда вы поймете, что -1 для слов любого количества бит (8, 32, 64 и т.д.) всегда все ff для каждого байта в слово/двойное слово и т.д.