Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ssis/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Bit manipulation 根据标志是等于1还是0,将标志转换为0xFF或0_Bit Manipulation - Fatal编程技术网

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哇,谢谢你的详细回答。学到了很多。在我的例子中有一个转折点:我将使用这个位操作