15

我正在从单色位图中读取位。我short以相反的顺序存储每16位。如果位图中的位为黑色,则存储 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 中移动?有没有办法移动符号位?

4

5 回答 5

36

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

替代文字

如果您想要一个逻辑移位(无符号扩展),请使用无符号数字

替代文字

于 2009-09-30T17:55:08.933 回答
5

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

">> 运算符将表达式 1 的位右移表达式 2 中指定的位数。表达式 1 的符号位用于填充从左侧开始的数字。向右移动的数字将被丢弃。表达式 1 的数据类型决定此运算符返回的数据类型。”

于 2009-09-30T17:54:52.933 回答
3

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

你也可以在其他地方找到这种右移。例如,x86 汇编提供了两条不同的指令,sar(用符号位填充左侧位)和shr(用零填充左侧位)。

如果您不希望在 C# 中出现这种行为,则在转换时必须使用无符号类型。

于 2009-09-30T17:50:00.120 回答
2

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

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

int value = -240;
int halved = value >> 1;      // Result = -120
于 2009-09-30T17:57:50.503 回答
1

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

优化提示

假设您必须进行大量此类转换(通常位图中有很多像素),您应该考虑使用一个 256 字节的数组,该数组将直接提供位模式的反转版本(或任何转换可能be) 为一个完整的字节。然后通过使用 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
    };
于 2009-09-30T17:52:51.790 回答