x86 または x86_64 アーキテクチャでの除算オーバーフロー エラーについていくつか質問があります。最近、整数オーバーフローについて読んでいます。通常、算術演算の結果が整数オーバーフローになると、FLAGS レジスタのキャリー ビットまたはオーバーフロー ビットがセットされます。しかし、どうやらこの記事によると、除算によるオーバーフローはオーバーフロー ビットを設定せず、0 で除算する場合と同様にハードウェア例外をトリガーします。
現在、除算による整数オーバーフローは、乗算よりもはるかにまれです。除算オーバーフローをトリガーする方法はわずかしかありません。1 つの方法は、次のようなことです。
int16_t a = -32768;
int16_t b = -1;
int16_t c = a / b;
この場合、符号付き整数の 2 の補数表現により、正の 32768 を符号付き 16 ビット整数で表すことができないため、除算演算がオーバーフローし、-32768 という誤った値が発生します。
いくつかの質問:
1) この記事の内容に反して、上記はハードウェア例外を引き起こしませんでした。Linux を実行している x86_64 マシンを使用しています。ゼロで除算すると、プログラムはFloating point exception
. しかし、除算のオーバーフローを引き起こすと、プログラムは通常通り続行し、誤った商を黙って無視します。では、なぜこれがハードウェア例外を引き起こさないのでしょうか?
2) 他の算術オーバーフローとは対照的に、除算エラーがハードウェアによって非常に厳しく扱われるのはなぜですか? 乗算オーバーフロー (偶発的に発生する可能性がはるかに高い) がハードウェアによって暗黙のうちに無視されるのはなぜですか? 除算オーバーフローは致命的な割り込みをトリガーするはずです?
===========編集==============
わかりました、皆さん、回答ありがとうございます。基本的に、上記の 16 ビット整数除算は、商がまだレジスタ サイズよりも小さいため、ハードウェア障害を引き起こすべきではないという回答を受け取りました。私はこれを理解していません。この場合、商を格納するレジスタは 16 ビットです。これは符号付きの正の 32768 を格納するには小さすぎます。では、なぜハードウェア例外が発生しないのでしょうか?
よし、これを GCC インライン アセンブリで直接実行して、何が起こるか見てみましょう。
int16_t a = -32768;
int16_t b = -1;
__asm__
(
"xorw %%dx, %%dx;" // Clear the DX register (upper-bits of dividend)
"movw %1, %%ax;" // Load lower bits of dividend into AX
"movw %2, %%bx;" // Load the divisor into BX
"idivw %%bx;" // Divide a / b (quotient is stored in AX)
"movw %%ax, %0;" // Copy the quotient into 'b'
: "=rm"(b) // Output list
:"ir"(a), "rm"(b) // Input list
:"%ax", "%dx", "%bx" // Clobbered registers
);
printf("%d\n", b);
これは単に誤った値を出力します: -32768
. 商 (AX) を格納するレジスタが小さすぎて商に収まらない場合でも、ハードウェア例外は発生しません。したがって、ここでハードウェア障害が発生しない理由がわかりません。