Bashのビット単位の操作は期待どおりに機能しません。

Bashのビット単位の操作は期待どおりに機能しません。

奇妙な問題が発生しました。説明のために、私のコンピュータから最大の符号なし番号(printf "%X \n" -1私に提供されているFFFFFFFFFFFFFFFF)を取得していくつかのビットを移動しましょう。まず左に移動します。

printf "%X \n" $(( 0xFFFFFFFFFFFFFFFF<<4 ))
FFFFFFFFFFFFFFF0
printf "%X \n" $(( 0xFFFFFFFFFFFFFFFF<<8 ))
FFFFFFFFFFFFFF00
printf "%X \n" $(( 0xFFFFFFFFFFFFFFFF<<16 ))
FFFFFFFFFFFF0000

今まではそんなに良くなった。予想通り。それでは右に移動してみましょう。

printf "%X \n" $(( 0xFFFFFFFFFFFFFFFF>>4 ))
FFFFFFFFFFFFFFFF
printf "%X \n" $(( 0xFFFFFFFFFFFFFFFF>>8 ))
FFFFFFFFFFFFFFFF
printf "%X \n" $(( 0xFFFFFFFFFFFFFFFF>>16 ))
FFFFFFFFFFFFFFFF

何を待つ? ?なぜこれがうまくいかないのですか?これはバグですか?


編集する:

誰かが提案された符号ビットへの接続を提案するかどうか心配です。しかし、私たちは算術について話しているわけではないので、ここでは記号の概念に立つ場所はありません。*などの他のツールは/算術に使用されます。ビットを操作できるツールのポイントは、ビットを操作できることです。後でそのビットを署名するかどうかにかかわらず表示するように選択する方法に関係なく。正しいですか?良い:

printf "%u \n" -1
18446744073709551615

誰でもどんなアイデアがありますか?

編集する:

ここの答えは乗算や除算について直接議論するので、私の懸念をより明確に説明します。乗算/除算とビットシフトは2つの異なるものですが、長い間プログラマの心の中でこれらの間の関連性を見ることができました。算術を実行するときは、ビットシフトには符号の概念が必要です。 Bashには、これら2つの異なるタスクを処理するための2つのツールセットがあります。数字に2を掛けたい場合は、この*ツールを使用してください。 Bashが内部的にビットシフトを使用して算術を実行できるという事実は要点を超えています。

答えの1つを引用すると...

符号ビットがコピーされない場合、結果は符号なしである。たとえば、1111 00008ビット値を右に1回移動すると、0111 1000

しかし、それは私が望んでいたまさに1111 0000それであることがわかりました。0111 1000除算をしたい場合は算術演算子を使用します。

とにかく、移動時にどのビットを埋めるべきかを明示的に指定する方法はありますか?

ベストアンサー1

持つ右に移動する2つの異なる方法一般的に使用されます。

「論理右シフト」は左側に0ビットを挿入するため、1つの位置だけ右にシフトした結果は、符号なしの2進数を2で割った値に対応します。echo $(( 16 >> 1 ))与えられた8

また、算術右シフトは符号ビットのコピーを左に挿入するため、1ビットを右に移動した結果は次のように分割することになります。兆候2進数を2で割ります。 、およびを提供しますecho $(( 16 >> 1 ))。 2の補数を除いて、実際の除算の丸めと一致しません。8echo $(( -16 >> 1 ))-8-15 >> 1-8-15 / 2-7

符号ビットがコピーされずにクリアされると、結果は正数になります。たとえば、1111 00008ビット値(0xf0、-16)を右に1回移動すると0111 1000(0x78、+120)となります。


今、これらのどれを使用するかは、より厳しい質問です。

実際、多くの実装では符号付き数値に算術シフトを使用しますが、シェル算術はほとんど符号付き long に対して実行されます。

しかし、これが完全な保証ではありません。シェル操作のPOSIX定義は、ほとんどの動作のC標準を参照します。たとえば、演算子テーブルには>>どのような種類の移動を実行すべきかは明記されていません。 (望むより:シェルコマンド言語、2.6.4 算術拡張そしてShell and Utilities、1.1.2 ISO C規格から派生した概念:算術精度と演算)

オペランドとオプション引数の値を含む整数変数と定数[...]は、ISO C標準符号付き長いデータ型と同じように実装されています[...]

算術演算子および制御フローキーワードの実装は、参照されているISO C標準部分[...]の実装と同じでなければなりません
<<>>セクション6.5.7、ビット単位のシフト演算子

cppreference.comでC演算子について話します。それ

negative の場合、a値はa >> b実装によって定義されます(ほとんどの実装では算術右シフトを実行するため、結果は負のままです)。

(これはおそらくすべてが2の報酬ではなかった世界の残骸であろう。 . 実装が定義されている)。

他のプログラミング言語、JavaScriptのように、別の算術右シフト>>と論理右シフト演算子を使用します>>>。しかし、Cはそうではなく、私が試したシェルも同じです。

また、単語の幅より大きいオフセットを使用してシフトを実行すると、奇妙なことが発生することがわかります。 x86 では、プロセッサが移動された値の最低 6 ビットしか表示されない1 << 64ため。ただし、他のプロセッサでは結果が異なる場合があります。11 << 0(1 << 32) << 320


あなたは言う、

しかし、ここでシンボルの概念は立場がありません。私の言葉は、後で署名されているか署名されていない状態で表示することを選択しても、数字は数字であるということです。そうですか?

2の補数機械(例:32x32 - > 32)で加算、減算、乗算のサブ部分についても同様です。

ただし、これは一般的な乗算や除算の高次部分には該当しません。 8 ビット値は、0xff符号なしの数値 255 または符号付きの数値 -1 を表すことができます。たとえば、8x8 - > 16乗算は、符号付き値(-1 * -1)か符号なし値(255 * 255)かによって、OR0xff * 0xffです。また、たとえば、符号があるか(-1/3 == 0)、または符号がないか(255/3 == 85)に応じて、isまたはです。0x00010xfe010xff / 300x55

おすすめ記事