Смещение знакового бита в .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
};