Смещение знакового бита в .NET.

Я читаю биты из монохромного растрового изображения. Я сохраняю каждые 16 бит в short в обратном порядке. Если бит в растровом изображении черный, сохраните 1. Если белый, сохраните 0.

Например: для растрового изображения: bbbw bbbw bbbw wwww
мой короткий: 0000 0111 0111 0111

Первый способ, которым я пытался это сделать:

short m;
// ...
Color c = bmp.GetPixel(j, i);
if (c.R == Color.Black)
    m |= short.MinValue;
m >>= 1;
// ...

После одного задания и смены я получил ожидаемый -32768 (1000 0000 0000 0000).
После второго раза я получил -16384 (1100 0000 0000 0000).

Я изменил свой код на использование ushort и изменил строку if на s |= (ushort)Math.Pow(2, 15);, и теперь он работает.

Мой вопрос: почему бит знака не изменяется в .NET? Есть ли способ сдвинуть знаковый бит?

Ответ 1

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

alt text

Если вам нужен логический сдвиг (без расширения знака), использовать беззнаковые числа:

alt text

Ответ 2

http://msdn.microsoft.com/en-us/library/k2ay192e.aspx

"Оператор → сдвигает биты выражения 1 вправо на количество бит, указанное в выражении 2. Значок бит выражения1 используется для заполнения цифр слева. Цифры, сдвинутые вправо, отбрасываются. Тип данных выражения1 определяет тип данных, возвращаемый этим оператором.

Ответ 3

Правое смещение целых чисел со знаком в С# заполняет левые биты значком знака. Эффективно, результат правого сдвига целочисленного знака на один бит эквивалентен его делению на 2.

Вы также можете найти такое смещение в других местах. Например, сборка x86 предоставляет две различные команды: sar (которая заполняет левые биты знаковым битом) и shr (которая заполняет левые биты нулем).

Если вы не хотите этого поведения на С#, вам придется использовать неподписанные типы при переключении.

Ответ 4

per http://www.blackwasp.co.uk/CSharpShiftOperators.aspx

... знаковые целые числа используют бит наивысшего порядка, чтобы определить, является ли значение переменной положительным или отрицательным, а остальные биты используют две примечания дополнения для отрицательных значений. Бит наивысшего порядка обычно рассматривается как бит переполнения для сдвиг влево. Чтобы это разрешить, С# понимает, что этот бит не должен корректироваться для подписанных типов данных и что отрицательные числа должны соответственно сдвигаться. Таким образом, сдвигание работает как с отрицательными, так и с положительными.

int value = -240;
int halved = value >> 1;      // Result = -120

Ответ 5

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

Совет по оптимизации

Предполагая, что вам нужно делать много таких преобразований (как правило, в растровых изображениях много пикселей), вы должны рассмотреть возможность использования массива из 256 байт, который обеспечивал бы прямую обратную версию битового шаблона (или что-то другое преобразование может быть) для одного полного байта. Затем, непосредственно индексируя этот массив, либо с помощью значения hi, либо с младшим байтом 16-битного слова, вы получаете результаты для всех 8 бит. В некоторых случаях, когда время/производительность превзойдены (и пространство доступно...), вы можете даже использовать размер массива 64k, обрабатывая одно полное слово за раз.

Учитывая преобразование, указанное в вашем примере, у вас должен быть массив предварительно вычисленных значений:

    byte[] mirror = { 
       0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
       0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
       0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x78, 0xF8,
       // etc..
       0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
    };