如何使用移位运算符将字节舍入为0或255

本文关键字:舍入 字节 何使用 运算符 | 更新日期: 2023-09-27 18:09:56

为了在c#项目中重新收集位图图像中的像素,我想将RGB值四舍五入到255或到0;

每个值是一个字节。

现在我正在做下面的事情:

(byte)(Pixels[i] < 128 ? 0 : 255);

我确信这可以以更快的方式实现,而不需要使用位操作进行类型转换。我该怎么做呢?

如何使用移位运算符将字节舍入为0或255

   (byte)(Pixels[i] < 128 ? 0 : 255)

是的,如果位图包含太多随机数据,由于分支预测不佳,这往往表现不佳。对于这样的语句,抖动不会产生有条件的移动。

可以使用一个技巧,右移保留有符号整数值的符号位。这使得这段代码可以工作:

   (byte)((sbyte)Pixels[i] >> 7)

生成没有分支的代码:

000000a7  movsx       eax,byte ptr [edx+eax+8]  ; Pixels[i], sign extended to 32-bits
000000ac  sar         eax,7                     ; >> operator
000000af  and         eax,0FFh                  ; (byte) cast

几种可能性:

  • (byte)((b << 24) >> 31);
  • (byte)((sbyte)b >> 31);
  • (uint)(int)(sbyte)b >> 24;

对于前两种方法,技巧是将大数映射为负值,然后使用带符号的右移将结果转换为-1或0,最后转换回byte。

最后一个在理论上更好,因为它最终可以在没有屏蔽的情况下编译为movsx eax,... shr eax, 24。但我怀疑。net JITter是否意识到这一点。

只能在未检查的上下文中使用

使用(预先填充的)查找表可能会获得最佳性能。

var lookup = Enumerable.Range(0, 256).Select(i => i < 128 ? (byte)0 : 255).ToArray();

因为您只有256个值,所以它可以驻留在L1缓存中,这意味着它的访问并不比算术计算更昂贵:

Pixels[i] = lookup[Pixels[i]];