如果您要使用三元运算符,您可能希望分配其结果:
x = (x % 2 == 0) ? 2 * x : x;
就个人而言,我想我只会使用一点点数学:
x <<= ((x & 1) ^ 1);
x&1
给出 的最低有效位x
,如果 x 为奇数,则为 1,如果为偶数,则为 0。该^ 1
部分翻转该位,因此如果为偶数则为 1 x
,如果为奇数则为 0 x
。然后我们左移x
那么多位。移位 0 位显然x
保持不变。左移一位乘以x
2。
至于为什么后者会(或至少可能)更可取,主要归结为您是否真的关心这种情况下的性能的问题。如果您处于性能无关紧要的情况,那么类似if ((x%2)==0) x *= 2;
的东西可能是您的最佳选择。
我至少猜到这个问题的至少部分原因是对效率的担忧。如果是这样,纯数学方法可能是更好的选择。例如,让我们考虑一下 VC++ 为这两者生成的代码:
; mathematical version:
mov eax, DWORD PTR _x$[esp-4]
mov ecx, eax
not ecx
and ecx, 1
shl eax, cl
[注意:对于这个源代码,g++ 产生几乎相同的目标代码]。
忽略从内存加载的(可能的)时间x
,这应该在几乎任何英特尔处理器上执行不超过 4 个时钟周期,回到 386 左右。更好的是,我希望任何处理器都能产生任何编译器非常相似的结果——对于几乎任何合理的处理器,从源代码到汇编语言的直接、字面翻译将在四条指令中进行实际的数学运算,每条指令都尽可能简单和快速。
使用该if
语句的版本如下所示:
; Line 2
mov ecx, DWORD PTR _x$[esp-4]
mov eax, ecx
and eax, -2147483647 ; 80000001H
jns SHORT $LN5@f
dec eax
or eax, -2 ; fffffffeH
inc eax
$LN5@f:
lea eax, DWORD PTR [ecx+ecx]
je SHORT $LN1@f
; Line 3
mov eax, ecx
$LN1@f:
随着编译的进行,这还不错。至少避免了div
那将是实现%2
. 不幸的是,它仍然不够聪明,无法具有竞争力——它仍然有几个分支,其中一个可能不是很可预测,所以大约有一半的时间我们会为一个预测错误的分支付出代价。
根据编译器,您可以(并且将会)看到比这更好的。例如使用 g++ 代替,我得到这个:
mov eax, DWORD PTR [ebp+8]
and eax, 1
test eax, eax
jne L2
sal DWORD PTR [ebp+8]
L2:
mov eax, DWORD PTR [ebp+8]
虽然肯定比 VC++ 对这段代码所做的更好,但它仍然远不及数学版本。特别是,除非该最低有效位是相当可预测的偶数或奇数,否则该分支可能有一半的时间被错误预测。
底线:在最好的情况下,这可能接近于匹配数学版本——但这将取决于编译器和输入数据的合作。除了编译器和输入数据的最偶然组合之外,几乎可以肯定它至少会慢 2 倍,而 10 倍甚至都不会令人惊讶。
当然,根据我使用的标志、编译器版本等,我可能能够从任一编译器中获得比实际效果更好的结果。有了一些坚持,我什至可能得到与代码的数学版本相同的结果。除非我非常了解目标编译器和 CPU,否则我什至不确定是否能获得同样好的结果——而且它们变得更好的机会充其量让我觉得非常渺茫。