三項演算子を使用する場合は、おそらくその結果を代入する必要があります。
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
ません。1 ビット左にシフトすると 2 が乗算x
されます。
後者が望ましい (または少なくとも望ましい) 理由については、ほとんどの場合、この状況でパフォーマンスを本当に気にするかどうかという問題に帰着します。パフォーマンスが問題にならない状況にある場合は、if ((x%2)==0) x *= 2;
おそらく次のようなものが最良の選択です。
私は少なくとも、質問の理由の少なくとも一部は効率への懸念であると推測していました. もしそうなら、純粋に数学的方法がより良い選択である可能性が高い. たとえば、VC++ が 2 つに対して生成するコードを考えてみましょう。
; mathematical version:
mov eax, DWORD PTR _x$[esp-4]
mov ecx, eax
not ecx
and ecx, 1
shl eax, cl
[注: このソース コードでは、g++ はほぼ同一のオブジェクト コードを生成します]。
メモリからロードする (可能な) 時間を無視するとx
、これは 386 前後のほぼすべての Intel プロセッサで 4 クロック サイクル以内に実行されるはずです。かなり似通った結果 - ソース コードからアセンブリ言語への直訳的で文字通りの変換は、ほぼすべての合理的なプロセッサで、実際の計算を 4 つの命令で実行します。各命令は可能な限り単純かつ高速です。
ステートメントを使用したバージョン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
ます。残念ながら、それはまだ競争力があるほどスマートではありません。まだいくつかの分岐があり、そのうちの 1 つはおそらくあまり予測できません。
コンパイラによっては、これよりもよく見えることがあります。たとえば、代わりに 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 について十分に理解していない限り、同じような結果が得られるかどうかさえかなり確信が持てないでしょう。