Почему использование только более низкие пять битов операнда сдвига при смещении 32-разрядного значения? (например, (UInt32) 1 <<33 == 2)

Как вы можете проверить исходный файл intobject.c , Python кэширует мелкие целые числа для эффективности. Каждый раз, когда вы создаете ссылку на небольшое целое число, вы ссылаетесь на кэшированное маленькое целое число, а не на новый объект. 257 не является маленьким целым числом, поэтому он вычисляется как другой объект.

Для этого лучше использовать ==.

14
задан Daniel LeCheminant 13 March 2009 в 23:11
поделиться

3 ответа

Это в основном сводится к способу, которым x86 обрабатывает коды операций арифметического сдвига: это только использует нижнюю часть 5 битов количества сдвига. Посмотрите 80 386 руководств по программированию , например. В C/C++ это - технически неопределенное поведение, чтобы сделать, немного сдвига больше чем на 31 бит (для 32-разрядного целого числа), идя с философией C "Вас не платит за то, в чем Вы не нуждаетесь". От раздела 6.5.7, абзаца 3 стандарта C99:

целочисленные продвижения выполняются на каждом из операндов. Тип результата является типом продвинутого левого операнда. Если величина правильного операнда отрицательна или больше, чем или равна ширине продвинутого левого операнда, поведение не определено.

Это позволяет компиляторам опускать единственную команду сдвига на x86 для сдвигов. 64-разрядные сдвиги не могут быть сделаны в одной инструкции относительно x86. Они используют SHLD / SHRD инструкции плюс некоторая дополнительная логика. На x86_64 64-разрядные сдвиги могут быть сделаны в одной инструкции.

, Например, gcc 3.4.4 испускает следующий блок для 64-разрядного сдвига влево произвольной суммой (скомпилированный с -O3 -fomit-frame-pointer):

uint64_t lshift(uint64_t x, int r)
{
  return x << r;
}

_lshift:
    movl    12(%esp), %ecx
    movl    4(%esp), %eax
    movl    8(%esp), %edx
    shldl   %cl,%eax, %edx
    sall    %cl, %eax
    testb   $32, %cl
    je      L5
    movl    %eax, %edx
    xorl    %eax, %eax
L5:
    ret

Теперь, я не очень знаком с C#, но я предполагаю, что он имеет подобную философию - разрабатывают язык, чтобы позволить этому быть реализованным максимально эффективно. Путем определения, что операции сдвига только используют нижнюю часть 5/6 бита количества сдвига, она разрешает JIT-компилятору компилировать сдвиги максимально оптимально. 32-разрядные сдвиги, а также 64-разрядные сдвиги в 64-разрядных системах, могли скомпилировать JIT в единственный код операции.

, Если C# были портированы на платформу, которая имела другое поведение для его собственных кодов операций сдвига, тогда это на самом деле подвергнется дополнительному хиту производительности - JIT-компилятор должен был бы гарантировать, что стандарт уважают, таким образом, это должно было бы добавить дополнительную логику для проверки только нижней части, 5/6 бита количества сдвига использовался.

9
ответ дан 1 December 2019 в 14:33
поделиться

Unit32 переполняется на уровне 32 битов, который определяется в спецификации. Что Вы ожидаете?

CLR не определяет сдвиг влево с водосливным оператором (1) обнаружения. При необходимости в таком средстве, необходимо проверить на себя.

(1) компилятор C# мог бы бросить его к длинному, но я не уверен.

3
ответ дан 1 December 2019 в 14:33
поделиться

Я записал этот простой тест в C (gcc, Linux), и получил подобные результаты. Что интересно, хотя, то, что константа определяет для по смещению, были превращены в нуль вместо переноса. Это действительно давало предупреждение о тех, так по крайней мере, было некоторое распознавание, что это была "неправильная" вещь сделать.

#include <stdio.h>

unsigned int is0 = 1 << 31;
unsigned int is1 = 1 << 32;
unsigned int is2 = 1 << 33;

int main()
{
   unsigned int loopy = 0;
   int x = 0;
   printf("0x%08X\n", is0);
   printf("0x%08X\n", is1);
   printf("0x%08X\n", is2);


   for (x = 0; x < 35; ++x)
   {
      loopy = 1 << x;
      printf("%02d 0x%08X\n", x,loopy);
   }

   return 0;
}

Вот результаты:

0x80000000
0x00000000
0x00000000
00 0x00000001
01 0x00000002
02 0x00000004
03 0x00000008
04 0x00000010
05 0x00000020
06 0x00000040
07 0x00000080
08 0x00000100
09 0x00000200
10 0x00000400
11 0x00000800
12 0x00001000
13 0x00002000
14 0x00004000
15 0x00008000
16 0x00010000
17 0x00020000
18 0x00040000
19 0x00080000
20 0x00100000
21 0x00200000
22 0x00400000
23 0x00800000
24 0x01000000
25 0x02000000
26 0x04000000
27 0x08000000
28 0x10000000
29 0x20000000
30 0x40000000
31 0x80000000
32 0x00000001
33 0x00000002
34 0x00000004
1
ответ дан 1 December 2019 в 14:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: