>>
and を使用し<<
てシフトする代わりに、 and を使用*
し/
て左右にシフトすることは可能ですか?
8 ビットの場合: 0x01 * 2 = 0000|0010。
もちろん、(整数演算の場合) 左シフトに 2 を掛け、右シフトに 2 で割ることができます。
でもやってはいけない。
完全に優れた左シフト演算子と右シフト演算子があり、「缶に書かれていることとまったく同じ」ことを行います。別の方法で、コンパイラやコードを読んでいる他の人を混乱させるのはなぜですか?
できますが、なぜそうしたいのですか?シフト操作は、CPU が最も迅速に実行できる操作の 1 つです。
実際には、パフォーマンスの最適化として反対のことがしばしば提案されます: シフトを使用して、2 の累乗による乗算/除算を実装します。
しかし、中途半端なコンパイラはそれをやってくれるので、絶対に手動でやるべきではありません: コードを明確に保つことが最も重要なことなので、数学をしているなら乗算と除算を使い、ビットを再操作します。
ビット操作を行う場合を除いて、すべてのケースでシフトを避ける必要があります。* 2 の代わりに << を書くと、コードが読みにくくなります。明示的に必要でない限り、ビットシフトを使用しないでください。
とにかく、コンパイラは*2
nを最適化します。<<
可能ですが、これを実際のビット シフトに最適化するのは賢いコンパイラです。また、「シフト」を意味する場合は、<< と記述した方がよいでしょう。「掛ける」という意味の場合は、* と書きます。
浮動小数点はまったくシフトしないことに注意してください。それらの指数は増加するだけです。
はい、1 つの落とし穴を除いて:負の数を扱う場合、除算は右シフトとは異なる動作をする場合があります。負の数に対する右シフトの動作は、コンパイラで定義されています。
もちろん、符号なし整数を使用することで、この問題を回避できます。
他の人が言ったように、最新のコンパイラは、シフトまたは乗算/除算のどちらを使用しても、おそらく同じ出力を生成します。したがって、コードの追跡と保守が容易になるものは何でも使用してください。
はい、できます。あなたの例を取る:
int main() {
if ((1*2) == (1 << 1)) printf("you certainly can!\n");
else printf("doesn't work that way\n");
}
そしてそれが生成する出力:
you certainly can!
(0x01*2 == 0000|0010) が ((1*2) == (1 << 1)) よりもはるかに問題があることに同意した理由については、この応答へのコメントを参照してください。
あなたはできる、
x << 1 == x * 2
x << 2 == x * 4
etc...
逆に
x >> 1 == x / 2
x >> 2 == x / 4
etc...
私は、経験豊富な c プログラマーがビットシフト操作を見て、2 つの方法が類似していることを知ることができるはずだと主張します。あなたの意図が明らかであるため、私は常にビットシフトを使用します。乗算または除算オプションを使用した場合は、一見乱数で値を突然乗算している理由を明確にコメントする必要があります。ビットシフトは、ビットをシフトしようとしていることを明確に示しています。
最適化に関するコメントについては、タイム クリティカルな組み込みプログラミングなど絶対に必要でない限り、コードの最適化について過度に心配することはありません。最適化ではなく、コードの可読性と保守性に重点を置く必要があります。開発の最後にさらにパフォーマンスが必要な場合は、いつでも戻って最適化を行うことができます (すべての作業を慎重に文書化するようにしてください)。しかし、開発中は、'<<' / '>>' などを使用して、最も読みやすく維持しやすいものを選びます。
はい、いつでもシフトを 2 乗の乗算に置き換えることができますが、指摘されているように、なぜですか? 指摘されていないのは、単純な 2 の累乗だけでなく、乗算をシフトに置き換えると便利な場合があるということです。たとえば、320 * 200 ピクセル バッファーを使用した古き良き時代には、ピクセル バイトへのオフセットは y* でした。 320+x. これは次のように行うことができます。
int OffsetXY( int x, int y)
{
int offset;
y <<= 6;
offset = y;
offset += y << 2; // offset = original y * 320
return offset + x;
}
この種のものは現在、ほとんどの開発者にとって学術的な関心事にすぎませんが、組み込みの世界では役立つ可能性があります (最近 DS でいくつかのコーディングを行い、この種のことは役に立ちました)。
他のすべての人は、それが完全に可能だと言っています。それも無意味です。シフトするつもりなら、シフトしてください。掛けるつもりなら掛けます。コードの読者を無駄な「最適化」と混同しないでください。
質問は別として:
コンパイラは、ほとんどの場合、整数の乗算/除算を一定量で調べ、式を一連のシフト/加算操作に減らします。これは、2 進乗算が簡単なタスクではなく、ALU に渡されると 1 クロック サイクルで完了するシフトや加算よりもはるかに時間がかかるためです。CPU の整数乗数の興味深い点は、ほぼ同様ですが、自動化された方法です。実行しているシフトのすべてのレベルの条件を停止してテストする必要がないため、個々のシフト/加算操作を使用する方がわずかに高速です。
ここに基本的なアルゴリズムのドラフトを作成することもできますが、今は真夜中近くなので、Google で検索してください。十分な要望があれば、記事を編集して明日掲載します。
この最適化は、1 つのシフト操作であるため、2^n の種類の定数による乗算に特に当てはまります。