如果我们在谈论 C 并且 blah 不在您的控制范围内,那么只需执行以下操作:
if(blah) 数字 += (1<<n);
C 中确实没有布尔值,也不需要,false 为零,true 不为零,因此您不能假设不是 0 是 1,这是您的解决方案所需要的,也不能假设任何特定的blah 中的位已设置,例如:
数字 += (blah&1)<<n;
不一定会起作用,因为 0x2 或 0x4 或任何非零且位零清除的东西都被认为是真的。通常,您会发现 0xFFF...FFFF(减一或全一)被用作真值,但您不能依赖典型值。
现在,如果您完全控制 blah 中的值,并将其严格控制为 0 表示假,1 表示真,那么您可以按照您的要求进行操作:
数字+=废话<<n;
并避免潜在的分支、额外的缓存行填充等。
回到通用案例,采用这个通用解决方案:
unsigned int fun (int blah, unsigned int n, unsigned int number )
{
if(blah) 数字 += (1<<n);
返回(数字);
}
并为两个最流行/最常用的平台编译:
测试 %edi, %edi
movl %edx, %eax
杰.L2
移动 $1, %edx
移动 %esi, %ecx
销售 %cl, %edx
添加 %edx, %eax
.L2:
以上使用条件分支。
下面的一个使用条件执行,没有分支,没有管道刷新,是确定性的。
cmp r0,#0
移动 r3,#1
添加 r2,r2,r3,asl r1
移动 r0,r2
bx lr
可以通过重新排列函数调用中的参数来保存 mov r0,r2 指令,但这是学术性的,您通常不会对此进行函数调用。
编辑:
如建议:
unsigned int fun (int blah, unsigned int n, unsigned int number )
{
数字 += ((blah!=0)&1)<<n;
返回(数字);
}
潜艇 r0, r0, #0
移动 r0, #1
添加 r0, r2, r0, asl r1
bx lr
当然更便宜,而且代码看起来不错,但我不会假设 blah!=0 的结果,即零或任何编译器定义为 true 的结果总是具有 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; 所以史蒂文赖特的评论是正确的,他应该为此受到赞扬。
海报的假设也基本正确,如果您可以将 blah 转换为 0 或 1 形式,那么在没有分支的意义上,在数学中使用它会更快。把它变成那种形式而不比分支更昂贵是诀窍。