在.NET中移动符号位

时间:2009-09-30 17:47:51

标签: c# .net bit-shift

我正在从单色位图中读取位。我以相反的顺序将每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中转移?有没有办法改变符号位?

5 个答案:

答案 0 :(得分:31)

在C#中,移位是算术移位(与逻辑移位相反)。在正确的算术移位中,符号位在左侧移入,因此保留了数字的符号。右移相当于除以2:

alt text

如果您想要逻辑转换(无符号扩展名),使用无符号数字

alt text

答案 1 :(得分:5)

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

“>>运算符将expression1的位向右移动expression2中指定的位数.expression1的符号位用于填充左边的数字。向右移位的数字是表达式1的数据类型确定此运算符返回的数据类型。“

答案 2 :(得分:3)

在C#中右移签名整数,用符号位填充左侧位。实际上,将有符号整数右移一位的结果相当于将其除以2.

你也可以在其他地方找到这种正确的转变。例如,x86程序集提供两个不同的指令sar(用符号位填充左边的位)和shr(用左边的位填充零)。

如果您不想在C#中使用此行为,则在转移时必须使用无符号类型。

答案 3 :(得分:2)

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

...有符号整数使用最高位来确定变量的值是正还是负,剩余的位对负值使用二进制补码表示法最高位通常被认为是溢出位左转操作。为了实现这一点,C#理解不应该对签名数据类型调整该位,并且应该相应地移动负数。因此,转移适用于负值和正值。

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

答案 4 :(得分:1)

您的问题的简短答案,正如您所发现的那样是使用无符号整数来避免引入符号位,这一切都很好。但是请考虑以下

优化提示

假设您必须进行大量此类转换(通常位图中有很多像素),您应该考虑使用256字节的数组,这将直接提供位模式的反转版本(或者其他转换可能是)一个完整的字节。然后通过直接索引此数组,使用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
    };