Если x имеет битовое представление 0xffffffff на 1
дополняют машины или 0x80000000 на машинах знаковых величин, что
стандарт говорит о представлении (без знака) x?
Преобразование в unsigned
определяется в терминах значений , а не представлений. Если вы конвертируете -1
в unsigned
, вы всегда получаете UINT_MAX
(поэтому, если ваш unsigned
равен 32 битам, вы всегда получаете 4294967295
). Это происходит независимо от представления чисел со знаком, которые использует ваша реализация.
Аналогично, если вы конвертируете -0
в unsigned
, тогда вы всегда получаете 0
. -0
численно равно 0.
Обратите внимание, что для поддержки отрицательных нулей не требуется дополнения единицами или реализации величины знака; если это не так, то доступ к такому представлению приводит к неопределенному поведению программы.
Пройдя все функции по очереди:
int logicalrightshift_v1(int x, int n)
{
return (unsigned)x >> n;
}
Результат этой функции для отрицательных значений x
будет зависеть от UINT_MAX
и будет дополнительно определяться реализацией, если (unsigned)x >> n
не находится в диапазоне int
. Например, logicalrightshift_v1(-1, 1)
вернет значение UINT_MAX / 2
независимо от того, какое представление машина использует для чисел со знаком.
int logicalrightshift_v2(int x, int n)
{
int msb = 0x4000000 << 1;
return ((x & 0x7fffffff) >> n) | (x & msb ? (unsigned)0x80000000 >> n : 0);
}
Почти все в этом отношении может быть определено реализацией. Предполагая, что вы пытаетесь создать значение в msb
с 1 в бите знака и нулями в битах значения, вы не можете сделать это с помощью сдвигов - вы можете использовать ~INT_MAX
, но это разрешено иметь неопределенное значение поведение на машине со знаком, которая не допускает отрицательных нулей и может давать результат, определенный реализацией, на двух машинах дополнения.
Типы 0x7fffffff
и 0x80000000
будут зависеть от диапазонов различных типов, что будет влиять на продвижение других значений в этом выражении.
int logicalrightshift_v2a(int x, int n)
{
return ((x & 0x7fffffff) >> n) | (x & (unsigned)0x80000000 ? (unsigned)0x80000000 >> n : 0);
}
Если вы создаете значение unsigned
, которое не находится в диапазоне int
(например, с учетом 32-битного int
, значения> 0x7fffffff
), тогда неявное преобразование в операторе возврата создает реализацию - определенное значение. То же самое относится к v3 и v4.
int logicalrightshift_v5(int x, int n)
{
unsigned y;
*(int *)&y = x;
y >>= n;
*(unsigned *)&x = y;
return x;
}
Это все еще определяется реализацией, потому что не определено, соответствует ли бит знака в представлении int
биту значения или биту заполнения в представлении unsigned
. Если он соответствует биту заполнения, это может быть представление прерывания, и в этом случае поведение не определено.
int logicalrightshift_v6(int x, int n)
{
unsigned y;
memcpy (&y, &x, sizeof (x));
y >>= n;
memcpy (&x, &y, sizeof (x));
return x;
}
К нему применимы те же комментарии, что и к v5.
Кроме того, я думаю, что (без знака) приведение в v2, v2a, v3, v4 является избыточным. Является
это правильно?
Это зависит. В качестве шестнадцатеричной константы 0x80000000
будет иметь тип int
, если это значение находится в диапазоне int
; в противном случае unsigned
, если это значение находится в диапазоне unsigned
; в противном случае long
, если это значение находится в диапазоне long
; в противном случае unsigned long
(поскольку это значение находится в пределах минимально допустимого диапазона unsigned long
).
Если вы хотите убедиться, что он имеет тип без знака, добавьте константу к U
, к 0x80000000U
.
Резюме:
Преобразование числа, большего чем INT_MAX
в int
, дает результат, определенный реализацией (или, на самом деле, позволяет повысить сигнал, определяемый реализацией).
Преобразование числа вне диапазона в unsigned
выполняется путем повторного сложения или вычитания UINT_MAX + 1
, что означает, что это зависит от математического значения , а не от представления.
Проверка отрицательного int
представления как unsigned
не переносима (хотя положительные int
представления в порядке).
Генерация отрицательного нуля с помощью битовых операторов и попытка использовать результирующее значение не переносимы.
Если вы хотите «логические сдвиги», тогда вы должны везде использовать беззнаковые типы. Типы со знаком предназначены для работы с алгоритмами, в которых значение имеет значение, а не представление.