Изучив трудный путь, я попытался сдвинуть a long long
и uint64_t
на более чем 32 бит на машине x86 в результате 0
. Я смутно помню, что где-то читал, а на 32-битных машинных операциях сдвига работают только первые 32 бита, но не могут вспомнить источник.
Я хотел бы знать, что если Shifting более 32 бит целого числа uint64_t на машине x86 является Undefined Поведение?
Перемещает более 32 бит целого числа uint64_t на машине x86 Undefined Поведение?
Ответ 1
В стандарте говорится (6.5.7 в n1570):
3 Целые рекламные акции выполняются на каждом из операндов. Тип результата это продвинутый левый операнд. Если значение правильного операнда отрицательное или больше или равно ширине продвинутого левого операнда, поведение не определено.
4 Результат E1 < E2 - левые сдвинутые позиции E2; освобожденные биты заполняются нули. Если E1 имеет неподписанный тип, значение результата равно E1 × 2 E2 уменьшенному по модулю один больше максимального значения, представляемого в типе результата. Если E1 имеет подписанный тип и неотрицательное значение, а E1 × 2 E2 представим в типе результата, то это полученное значение; в противном случае поведение не определено.
5 Результат E1 → E2 - это правые позиции E2 в E1. Если E1 имеет неподписанный тип или если E1 имеет подписанный тип и неотрицательное значение, значение результата является интегралом часть частного E1/2 E2. Если E1 имеет подписанный тип и отрицательное значение, результирующее значение определяется реализацией.
Сдвиг a uint64_t
на расстояние менее 64 бит полностью определяется стандартом.
Так как long long
должно быть не менее 64 бит, сдвиг long long
значений менее 64 бит определяется стандартом для неотрицательных значений, если результат не переполняется.
Обратите внимание, однако, что если вы пишете литерал, который вписывается в 32 бита, например. uint64_t s = 1 << 32
, как предполагалось @drhirsch, вы фактически не переносите 64-битное значение, а 32-битное. Это поведение undefined. Наиболее распространенными результатами являются сдвиг на shift_distance % 32
или 0, в зависимости от того, что делает оборудование.
Ответ 2
Даниэль Фишер отвечает, отвечает на вопрос о спецификации языка C. Что касается того, что на самом деле происходит на машине x86 при выводе сдвига на переменную величину, обратитесь к Руководство для разработчиков программного обеспечения Intel Том 2B, p, 4-506:
Счет маскируется до 5 бит (или 6 бит если в 64-битном режиме и REX.W). Диапазон отсчетов ограничен 0 - 31 (или 63, если 64-битный режим и REX.W).
Итак, если вы попытаетесь сдвинуть на величину больше 31 или 63 (для 32- и 64-разрядных значений соответственно), аппаратное обеспечение будет использовать только нижние 5 или 6 бит суммы сдвига. Итак, этот код:
uint32_t RightShift(uint32_t value, uint32_t count)
{
return value >> count;
}
В результате получится RightShift(2, 33) == 1
на x86 и x86-64. Это по-прежнему undefined поведение в соответствии со стандартом C, но на x86, если компилятор компилирует его в инструкцию sar
, он будет определять поведение в этой архитектуре. Но вы все равно должны избегать написания такого кода, который зависит от особенностей, характерных для архитектуры.
Ответ 3
Стандарт C требует, чтобы сдвиг работал правильно. У конкретного глючного компилятора может быть описанный вами дефект, но это плохое поведение.
Это тестовая программа:
#include <stdio.h>
#include <inttypes.h>
int main(void)
{
uint64_t x = 1;
for (int i = 0; i < 64; i++)
printf("%2d: 0x%.16" PRIX64 "\n", i, (x << i));
return 0;
}
Это вывод на машине i686 с RHEL 5 с GCC 4.1.2, а также на машине x86/64 (также с RHEL 5 и GCC 4.1.2) и на Mac x86/64 (работающая под управлением Mac OS X 10.7.3 с GCC 4.7.0). Поскольку это ожидаемый результат, я пришел к выводу, что на 32-битной машине нет необходимой проблемы, и что GCC по крайней мере не обнаружил такой ошибки с GCC 4.1.2 (и, вероятно, никогда не обнаруживал такую ошибку).
0: 0x0000000000000001
1: 0x0000000000000002
2: 0x0000000000000004
3: 0x0000000000000008
4: 0x0000000000000010
5: 0x0000000000000020
6: 0x0000000000000040
7: 0x0000000000000080
8: 0x0000000000000100
9: 0x0000000000000200
10: 0x0000000000000400
11: 0x0000000000000800
12: 0x0000000000001000
13: 0x0000000000002000
14: 0x0000000000004000
15: 0x0000000000008000
16: 0x0000000000010000
17: 0x0000000000020000
18: 0x0000000000040000
19: 0x0000000000080000
20: 0x0000000000100000
21: 0x0000000000200000
22: 0x0000000000400000
23: 0x0000000000800000
24: 0x0000000001000000
25: 0x0000000002000000
26: 0x0000000004000000
27: 0x0000000008000000
28: 0x0000000010000000
29: 0x0000000020000000
30: 0x0000000040000000
31: 0x0000000080000000
32: 0x0000000100000000
33: 0x0000000200000000
34: 0x0000000400000000
35: 0x0000000800000000
36: 0x0000001000000000
37: 0x0000002000000000
38: 0x0000004000000000
39: 0x0000008000000000
40: 0x0000010000000000
41: 0x0000020000000000
42: 0x0000040000000000
43: 0x0000080000000000
44: 0x0000100000000000
45: 0x0000200000000000
46: 0x0000400000000000
47: 0x0000800000000000
48: 0x0001000000000000
49: 0x0002000000000000
50: 0x0004000000000000
51: 0x0008000000000000
52: 0x0010000000000000
53: 0x0020000000000000
54: 0x0040000000000000
55: 0x0080000000000000
56: 0x0100000000000000
57: 0x0200000000000000
58: 0x0400000000000000
59: 0x0800000000000000
60: 0x1000000000000000
61: 0x2000000000000000
62: 0x4000000000000000
63: 0x8000000000000000
Ответ 4
Перемещение по числу, содержащемуся между 0 и предшественником ширины типа, не вызывает поведения undefined, но это делает левое смещение отрицательного числа. Вы бы это сделали?
С другой стороны, смещение отрицательного числа с правой стороны определяется реализацией, и большинство компиляторов, когда правые сдвиговые типы подписей, распространяют знаковый бит.
Ответ 5
Нет, это нормально.
ISO 9899: 2011 6.5.7 Операторы побитового сдвига
Если значение правильного операнда отрицательно или больше или равно по ширине продвинутого левого операнда, поведение undefined.
Это не так, так что все хорошо и четко определено.