> ABで、直前の計算(加算)でオーバーフローしたかどうかを判断する関数ってありましたっけ?
たぶんそのような関数はないと思います。
だから自分で作ってしまいましょう(あるいは山本様にお願いするというのもアリです)。
作ってしまうとは言ったものの、これが一筋縄ではいかない模様。
(32bit) + (32bit) の計算ならば結果を 64bit 変数に格納し、上位 32bit が 0 または &HFFFFFFFF かどうかを判断すればよいとおもいます。
しかしこれでは、オペランドの片方(または両方)が 64bit の場合に破綻します。また符号ありと符号なしを混ぜたときにイヤなことになるかも。
いろいろ考えた結果
http://www.activebasic.com/forum/viewtopic.php?t=308 からヒントを得ました。
CPU の内部には、計算結果に関するあれこれを保存しておくためのフラグ記憶域 (EFLAGS) があります。
そこに直接アクセスすれば最も確実にオーバーフローを検査できるというわけです。
言うは易く行うは難し。ABにはインラインアセンブラがないのでハンドアセンブルでかなり苦労しました。
コード: 全て選択
' Constants of Status Flags
Const EFLAGS_CF = &H1 ' Carry Flag 加算(減算)で桁上がり(桁借り)が発生
Const EFLAGS_PF = &H4 ' Parity Flag 最下位バイトのパリティ
Const EFLAGS_AF = &H10 ' Adjust Flag 調整フラグ(BCD 命令で使用)
Const EFLAGS_ZF = &H40 ' Zero Flag 計算結果がゼロ
Const EFLAGS_SF = &H80 ' Sign Flag 計算結果の符号
Const EFLAGS_OF = &H800 ' Overflow Flag オーバーフローが発生
Dim __gf = &HC3589C As DWord
Dim __sf = &HE1FF9D59 As DWord
Dim GetEFLAGS As *Function() As DWord
Dim SetEFLAGS As *Sub(ByVal NewValue As DWord)
GetEFLAGS = VarPtr(__gf)
SetEFLAGS = VarPtr(__sf)
極限までサイズを縮めたので、関数本体が DWord 変数に収まりました。我ながらなかなか。
使用方法
コード: 全て選択
c = a + b
' 計算の直後に使用すること
If GetEFLAGS() And EFLAGS_OF Then
MessageBox(NULL, "オーバーフローが発生", "テスト", MB_OK)
End If
コメント中にも書きましたが、計算の直後に GetEFLAGS() してください。
他の処理を間に挟むと、それにより EFLAGS が変化する恐れがあります。
データ実行防止機能で停止する場合や、プログラムの一般公開を考えている場合には VirtualAlloc() 等で適宜解決してください。
メモリページに実行権限が割り当てられていない場合、一般保護例外が発生する恐れがあります。
機械語なんて弄らないで済むのであればそれに越したことはないんですけどね。
他にももっといい方法がないか模索してみます。
[変更履歴]
2006/5/4 - 引用元 URL を新しいものに修正。