如果我们在讨论C语言且blah不受你的控制,那么可以这样做:
if(blah) number += (1<<n);
在C语言中并没有布尔类型,也不需要,false是0,true不是0,所以不能假设非零就是1,这正是你需要的解决方案,也不能假设blah的任何特定位被设置了,例如:
number += (blah&1)<<n;
这也不一定起作用,因为任何非零值中清除了第零位的都被视为真。通常情况下,你会发现使用0xFFF...FFFF(减1或全1)表示真,但是你不能依赖于典型情况。
现在,如果你完全控制blah的值,并且将其严格保持为0表示false,1表示true,那么你可以做你想要的事情:
number += blah<<n;
并避免分支、额外的高速缓存行填充等潜在问题。
回到通用情况,采用以下通用解决方案:
unsigned int fun ( int blah, unsigned int n, unsigned int number )
{
if(blah) number += (1<<n);
return(number);
}
并编译为两个最流行/使用的平台:
testl %edi, %edi
movl %edx, %eax
je .L2
movl $1, %edx
movl %esi, %ecx
sall %cl, %edx
addl %edx, %eax
.L2:
以上使用了条件分支。
下面的代码使用条件执行,没有分支,没有流水线刷新,是确定性的:
cmp r0,#0
movne r3,#1
addne r2,r2,r3,asl r1
mov r0,r2
bx lr
通过重新排列函数调用中的参数,可以节省mov r0,r2指令,但这是学术问题,在正常情况下不会在此上浪费函数调用。
编辑:
如建议所述:
unsigned int fun ( int blah, unsigned int n, unsigned int number )
{
number += ((blah!=0)&1)<<n;
return(number);
}
subs r0, r0, #0
movne r0, #1
add r0, r2, r0, asl r1
bx lr
当然更便宜,代码看起来也不错,但我不会假设blah!= 0的结果总是具有lsbit设置为零或编译器定义为真。编译器不必为生成工作代码而设置该位。也许标准规定了true的具体值。通过重新排列函数参数,if(blah)number + = ...也将导致三个单时钟指令,而不做出任何假设。
编辑2:
查看我所理解的C99标准:
“==(等于)和!=(不等于)运算符类似于关系运算符,除了它们的优先级较低。每个运算符如果指定的关系为真则产生1,如果为假则产生0。”
这解释了为什么上面的编辑有效,并且为什么你得到movne r0,#1而不是其他随机数。
发帖者提出了关于C的问题,但还指出ADA是当前语言,从独立于语言的角度来看,您不应该假设像C特性一样的“功能”,而是使用if(blah)number = number +(1 << n)。但是,这是在C标记下询问的,因此C的通用(与处理器无关)最快结果是我认为,number + =(blah!= 0)<< n;因此,Steven Wright的评论是正确的,他应该得到这个荣誉。
发帖者的假设基本上是正确的,如果您可以将blah转换为0或1形式,则在数学中使用它比分支更快。让它进入该形式而不比分支更昂贵是诀窍。