[解決済み] アセンブリ言語 - Moduloはどのように行うのですか?
質問
x86アセンブリでモジュロ演算子や命令のようなものはありますか?
どのように解決するのですか?
モジュラス/ディバイダーが既知の定数であり、パフォーマンスを重視する場合は、以下を参照してください。 これ と これ . 実行時までわからないループ不変の値に対しては、乗法的な逆数も可能です。 https://libdivide.com/ (ただし、JITコードジェンなしだと、1つの定数に必要なステップだけをハードコーディングするよりも効率が悪い)。
は絶対に使用しないでください。
div
は2の既知の累乗です。
大いに
よりも遅い
and
は余り、右シフトは割り算です。 Cコンパイラの出力を見て、2の累乗による符号なしまたは符号ありの除算の例を見てください。
Godbolt コンパイラエクスプローラで
. 実行時の入力が 2 のべき乗であることが分かっている場合は、次のようにします。
lea eax, [esi-1]
;
and eax, edi
というようなことをするために
x & (y-1)
. Modulo 256はさらに効率的です。
movzx eax, cl
は、最近のIntel CPUではレイテンシがゼロになる(
mov-elimination(ムーブエリミネーション
)、2つのレジスタが別々である限りは
単純/一般的な場合:実行時に値が不明な場合
は
DIV
インストラクション
(およびその対応する
IDIV
は商と余りを表します。 符号なしの場合、余りとモジュールは同じものです。 符号ありの場合
idiv
は、次のようになります。
余り(モジュラスではない)
は負になることがあります。
例
-5 / 2 = -2 rem -1
x86 の分割セマンティクスは、C99 の
%
演算子を使用します。
DIV r32
で64ビットの数値を分割します。
EDX:EAX
を 32 ビットのオペランド(任意のレジスタまたはメモリ)で演算し、その商を
EAX
と余りを
EDX
. 商のオーバーフローでエラーとなる。
符号なし32ビットの例 (どのモードでも動作します)
mov eax, 1234 ; dividend low half
mov edx, 0 ; dividend high half = 0. prefer xor edx,edx
mov ebx, 10 ; divisor can be any register or memory
div ebx ; Divides 1234 by 10.
; EDX = 4 = 1234 % 10 remainder
; EAX = 123 = 1234 / 10 quotient
16ビットアセンブリでは、次のようになります。
div bx
で32ビットオペランドを分割する場合
DX:AX
で
BX
. インテルの
アーキテクチャ ソフトウェア開発者向けマニュアル
をご覧ください。
通常は常に
xor edx,edx
の前に符号なし
div
でEAXをEDX:EAXにゼロ拡張しています。
これが、"normal" 32-bit / 32-bit => 32-bit divisionのやり方です。
符号付き除算の場合。
使用
cdq
前に
idiv
から
記号
-EAXをEDX:EAXに拡張する。 参照
なぜDIV命令を使う前にEDXを0にしなければならないのですか?
. その他のオペランドサイズについては
cbw
(AL->AX)です。
cwd
(AX->DX:AX)です。
cdq
(EAX->EDX:EAX)、または
cqo
(RAX->RDX:RAX)とすることで、上半分を
0
または
-1
は、ローハーフの符号ビットにより決定されます。
div
/
idiv
は、オペランドサイズが8、16、32、(64ビットモードでは)64ビットで利用可能です。 64ビットのオペランドサイズは、現在のIntel CPUでは32ビット以下よりはるかに低速ですが、AMD CPUではオペランドサイズに関係なく、実際の数値の大きさだけを気にします。
8ビットのオペランドサイズは特殊で、暗黙の入出力はDL:ALではなくAH:AL(別名AX)であることに注意してください。 参照 DOSBoxで8086アセンブリ。idiv命令でバグ? を参考にしてください。
符号付き64ビット除算の例 (64ビットモードが必要です)
mov rax, 0x8000000000000000 ; INT64_MIN = -9223372036854775808
mov ecx, 10 ; implicit zero-extension is fine for positive numbers
cqo ; sign-extend into RDX, in this case = -1 = 0xFF...FF
idiv rcx
; quotient = RAX = -922337203685477580 = 0xf333333333333334
; remainder = RDX = -8 = 0xfffffffffffffff8
制限事項/よくある間違い
div dword 10
はエンコードできません
を機械語に変換します (したがって、アセンブラは無効なオペランドに関するエラーを報告します)。
とは異なり
mul
/
imul
(通常、より高速な2オペランドを使用すべきところ
imul r32, r/m32
または3-オペランド
imul r32, r/m32, imm8/32
の代わりに、上位半分の結果を書くのに時間を浪費しないような)、即値による除算、または上位半分の配当入力のない32ビット/32ビット => 32ビット除算や余りのための新しいオペコードはありません。
除算はとても遅く、(願わくば)まれなことなので、EAX や EDX を回避させる方法や、即値を直接使用する方法をわざわざ追加する必要はなかったのです。
div と idiv は商が1つのレジスタに収まらない場合、エラーになります。 (AL / AX / EAX / RAX、配当と同じ幅)。 これはゼロによる除算を含みますが、ゼロでない EDX とより小さい除数でも起こります。 Cコンパイラが32ビット値をDX:AXに分割する代わりに、ゼロ拡張や符号拡張を行うのはこのためです。
また、なぜ
INT_MIN / -1
x86 のような 2 の補数系では符号付き商をオーバーフローさせるからです。 参照
なぜ整数の-1(マイナス1)除算はFPEになるのですか?
x86とARMの比較の例です。
idiv
は、この場合、確かに不具合が発生します。
x86の例外は
#DE
- 除算例外です。 Unix/Linux システムでは、カーネルは #DE 例外を発生させたプロセスに SIGFPE 算術例外シグナルを送出します。 (
ゼロによる整数の除算が浮動小数点例外を引き起こすのはどのプラットフォームですか?
)
について
div
を使用すると、配当が
high_half < divisor
は安全です。
0x11:23 / 0x12
よりも小さい。
0xff
ということで、8ビットの商に収まる。
巨大な数を小さな数で割る拡張精度の除算は、ある塊の余りを次の塊の上位半数配当(EDX)として使用することで実装できます。 このため、remainder=EDX quotient=EAXとしたのでしょう。
関連
-
[解決済み] BL命令ARM - その仕組み
-
[解決済み] SRLとSRAの違いは何ですか?[重複しています]。
-
[解決済み] 要素ごとの加算は、結合ループよりも分離ループの方がはるかに高速なのはなぜですか?
-
[解決済み] <は<=より速いのか?
-
[解決済み] JavaScriptで整数の除算を行い、余りを別途取得する方法は?
-
[解決済み] Collatz予想の検証を行うC++のコードは、なぜ手書きのアセンブリよりも高速に動作するのでしょうか?
-
[解決済み] Intel CPU の _mm_popcnt_u64 で、32 ビットのループカウンターを 64 ビットに置き換えると、パフォーマンスが著しく低下します。
-
[解決済み】乱数生成器を使うとモジュロバイアスがかかると言われるのはなぜ?
-
[解決済み】マルチコアアセンブラとはどのようなものですか?
-
[解決済み】GDBで現在のアセンブリ命令を表示する
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】Nasmエラー:オペコードとオペランドの無効な組み合わせ
-
[解決済み] MIPSの大、小、大
-
[解決済み] popまたはadd esp、4 ? その差は何ですか?
-
[解決済み] MIPS: スタックポインタ($sp)とスタックの関連した使用法
-
[解決済み] MIPSプログラムの中で`lw`と`sw`が実際にどのように機能するかを理解する
-
[解決済み] CPUのParity Flagは何のためにあるのですか?
-
[解決済み] ARMアセンブリのstrの説明
-
[解決済み] アセンブリで命令セテは何をするのですか?
-
[解決済み] ARMはSDIVとUDIVを区別していますが、ADD、SUB、MULでは区別していないのはなぜですか?
-
[解決済み] アセンブリの追加要求の明確化