数日前、StackExchangeのメンバーであるAntoは、ビット単位の演算子の有効な使用方法について問い合わせました。私は、整数を2のべき乗で乗算および除算するよりも高速であると述べました。StackExchangeメンバーのDaeminは、右シフトが負の数の問題を引き起こしたと述べて反論しました。
その時点で、符号付き整数でシフト演算子を使用することを考えたことはありませんでした。私は主に低レベルのソフトウェア開発でこの手法を使用しました。したがって、常に符号なし整数を使用しました。Cは、符号なし整数で論理シフトを実行します。論理右シフトを実行する場合、符号ビットには注意が払われません。空白ビットはゼロで埋められます。ただし、Cは符号付き整数を右にシフトするときに算術シフト演算を実行します。空白ビットは符号ビットで埋められます。この違いにより、負の値はゼロに切り捨てられるのではなく、無限に丸められます。これは、符号付き整数除算とは異なる動作です。
数分考えた結果、一次解決策が生まれました。このソリューションは、シフトする前に条件付きで負の値を正の値に変換します。値は、シフト操作が実行された後、条件付きで負の形式に変換されます。
int a = -5;
int n = 1;
int negative = q < 0;
a = negative ? -a : a;
a >>= n;
a = negative ? -a : a;
このソリューションの問題は、通常、条件付き割り当てステートメントが少なくとも1つのジャンプ命令に変換されることであり、ジャンプ命令は、両方の命令パスをデコードしないプロセッサーでは高価になる可能性があります。命令パイプラインを2回再プライミングしなければならないことは、除算をオーバーシフトすることで得られるパフォーマンスの向上に良い影響を与えます。
上記で述べたように、私は土曜日に条件付き割り当ての問題に対する答えで目が覚めました。算術シフト演算を実行するときに発生する丸めの問題は、2の補数表現を操作する場合にのみ発生します。補数表現では発生しません。この問題を解決するには、シフト操作を実行する前に2の補数値を1の補数値に変換します。次に、1の補数値を2の補数値に変換する必要があります。驚くべきことに、シフト操作を実行する前に負の値を条件付きで変換することなく、この一連の操作を実行できます。
int a = -5;
int n = 1;
register int sign = (a >> INT_SIZE_MINUS_1) & 1
a = (a - sign) >> n + sign;
2の補数の負の値は、1を引くことで1の補数の負の値に変換されます。反対に、1の補数の負の値は、1を追加することで2の補数の負の値に変換されます。上記のコードは、符号ビットを使用して2の補数から1の補数へ、またはその逆に変換するために機能します。負の値のみに符号ビットが設定されます。したがって、aが正の場合、変数記号はゼロになります。
上記のことで、上記のような他のビット単位のハックがあなたのトリックの袋に入ったと考えることができますか?お気に入りのビット単位のハックは何ですか?私は常に、パフォーマンス指向の新しいビット単位のハッキングを探しています。