Bit manipulation 根据标志是等于1还是0,将标志转换为0xFF或0
我有一个二进制标志Bit manipulation 根据标志是等于1还是0,将标志转换为0xFF或0,bit-manipulation,Bit Manipulation,我有一个二进制标志f,等于零或一。 如果等于1,我想转换为0xFF,否则转换为0 当前的解决方案是f*0xFF,但我更愿意使用位旋转来实现这一点。如何: value = 0xFF & ((1 << 16) - f ) (unsigned char)-f 或者: 0xFF & -f 如果f已经是char,那么您只需要-f 这种方法之所以有效,是因为-0==0和-1==0xFFFFF…,所以求反可以直接得到您想要的结果,如果f大于字符(您没有说),可能会设置一些额外
f
,等于零或一。
如果等于1,我想转换为0xFF
,否则转换为0
当前的解决方案是f*0xFF
,但我更愿意使用位旋转来实现这一点。如何:
value = 0xFF & ((1 << 16) - f )
(unsigned char)-f
或者:
0xFF & -f
如果f
已经是char
,那么您只需要-f
这种方法之所以有效,是因为-0==0
和-1==0xFFFFF…
,所以求反可以直接得到您想要的结果,如果f
大于字符(您没有说),可能会设置一些额外的高位
记住,尽管编译器很聪明。我尝试了以下所有解决方案,所有解决方案都编译为3条或更少的指令,没有一条指令有分支(即使是带有条件的解决方案):
有条件的
汇编至:
remap_cond:
test edi, edi
mov eax, 255
cmove eax, edi
ret
因此,即使是“明显的”条件也可以很好地工作,在大多数现代x86硬件上,根据cmov
的性能,它只需要三条指令,延迟2或3个周期
乘法
您的原始解决方案:
int remap_mul(int f) {
return f * 0xFF;
}
实际上编译成漂亮的代码,完全避免乘法,用移位和减法替换:
remap_mul:
mov eax, edi
sal eax, 8
sub eax, edi
ret
在具有mov消除功能的机器上,这通常需要两个周期,而且mov
通常会通过内联来删除
减法
正如corn3lius指出的,您可以从0x100
和掩码中进行一些减法,如下所示:
int remap_shift_sub(int f) {
return 0xFF & (0x100 - f);
}
这可归结为1:
因此,这是目前为止我认为最好的—在大多数主机上延迟2个周期,movzx
通常可以通过内联2消除—例如,因为它可以在后续的消费指令中使用8位寄存器
请注意,编译器巧妙地消除了掩蔽操作(您可能会认为movzx
解释了掩蔽操作)和0x100
常量的使用,因为它知道简单的否定在这里做同样的事情(特别是,-f
和0x100-f
之间的所有不同位都被0xFF&…
操作屏蔽掉)
这将直接导致以下C代码:
int remap_neg_mask(int f) {
return -f;
}
这是一个完全相同的东西
你可以
1除了在clang
上,它插入一个额外的mov
,以在eax
中获得结果,而不是首先在那里生成结果
2请注意,所谓“内联”,我指的是,如果您实际将其作为函数编写,编译器会进行真正的内联,但如果您直接在需要的位置执行重新映射操作而不使用函数,则会发生什么情况。如何:
(unsigned char)-f
或者:
0xFF & -f
如果f
已经是char
,那么您只需要-f
这种方法之所以有效,是因为-0==0
和-1==0xFFFFF…
,所以求反可以直接得到您想要的结果,如果f
大于字符(您没有说),可能会设置一些额外的高位
请记住,尽管编译器是智能的。我尝试了以下所有解决方案,所有解决方案都编译到了3条或更少的指令,但都没有分支(即使是带有条件的解决方案):
有条件的
汇编至:
remap_cond:
test edi, edi
mov eax, 255
cmove eax, edi
ret
因此,即使是“明显的”条件也可以很好地工作,在大多数现代x86硬件上,根据cmov
的性能,它只需要三条指令,延迟2或3个周期
乘法
您的原始解决方案:
int remap_mul(int f) {
return f * 0xFF;
}
实际上编译成漂亮的代码,完全避免乘法,用移位和减法替换:
remap_mul:
mov eax, edi
sal eax, 8
sub eax, edi
ret
在具有mov消除功能的机器上,这通常需要两个周期,而且mov
通常会通过内联来删除
减法
正如corn3lius指出的,您可以从0x100
和掩码中进行一些减法,如下所示:
int remap_shift_sub(int f) {
return 0xFF & (0x100 - f);
}
这可归结为1:
因此,这是目前为止我认为最好的—在大多数主机上延迟2个周期,movzx
通常可以通过内联2消除—例如,因为它可以在后续的消费指令中使用8位寄存器
请注意,编译器巧妙地消除了掩蔽操作(您可能会认为movzx
解释了掩蔽操作)和0x100
常量的使用,因为它知道简单的否定在这里做同样的事情(特别是,-f
和0x100-f
之间的所有不同位都被0xFF&…
操作屏蔽掉)
这将直接导致以下C代码:
int remap_neg_mask(int f) {
return -f;
}
这是一个完全相同的东西
你可以
1除了在clang
上,它插入一个额外的mov
,以在eax
中获得结果,而不是首先在那里生成结果
2注意,通过“内联”我的意思是,如果你真的把它作为函数来写,编译器真正的内联都会这样做,但是如果你直接在需要它的地方进行重新映射操作,而不使用函数,会发生什么呢。Awesome!我想避免第二个选项,因为它有一个分支,我想避免分支。@Jacko它可能是一个分支。它也可能是一个分支平台相关技巧的y,例如sbb al,al
(如果标志在进位标志中),neg al
(如果标志在al
中为0/1)检查asm输出。注意,1太棒了!我想避免第二个选项,因为它有一个分支,我想避免分支。@Jacko它可能是一个分支。它也可能是任何依赖于平台的技巧,例如sbb al,al
(如果标志在进位标志中),neg al
(如果标志在al
中为0/1)检查asm输出。注意1哇,谢谢你的详细回答。学到了很多。在我的例子中有一个转折点:我将使用这个位操作