• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:VBAで一時的にオーバーフローを回避したい)

VBAで一時的にオーバーフローを回避する方法

このQ&Aのポイント
  • VBAで数値の演算を行う際に、Long型の範囲を超えてしまう場合にエラーが発生することがあります。
  • オーバーフローを回避するためには、事前に演算結果が範囲内に収まるかどうかを判定する必要があります。
  • 適切な判定方法としては、演算前に演算子を使って範囲内に収まるかどうかを確認する方法や、演算後に結果を範囲内に収める方法があります。

質問者が選んだベストアンサー

  • ベストアンサー
  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.7

num1,num2を16ビット.8ビット,8ビットに分解します num1=n1h * 2^16+n1m*2^8+n1l num2=n2h * 2^16+n2m*2^8+n2l num1*num2を計算すると (n1h*n2h)*2^32 +(n1h*n2m+n1m*n2h)*2^24 +(n1h*n2l+n1m*n2m+n1l*n2h)*2^16 +(n1m*n2l+n1l*n2m)*2^8 + n1l*n2l となります。 (n1h*n2h)*2^32 から、 n1h*n2h<>0 ならば、33ビット以上が確実です。 n1h*n2h=0なら、残りが32ビット以上(2^31以上)ならMAXLONG超となります。 n1h*n2m+n1m*n2hは 16bit*8bitで最大24bit、24bit+24bitで最大25bitなのでオーバーフローはありません この値を m3=m3h*2^7+m3l (m3lは7ビット分) とすると +(n1h*n2m+n1m*n2h)*2^24 = (m3h*2^7+m3l ) * 2^24 = m3h*2^31 + m3l * 2^24 よって、m3h>0ならMAXLONG超となります。 同様に +(n1h*n2l+n1m*n2m+n1l*n2h)*2^16 は 24bit+24bit+16bitで最大26bit 。 m2=m2h*2^15+m2lとすると = (m2h*2^15+m2l ) * 2^16 =m2h*2^31+m2l*2^16 m3l * 2^24+m2l*2^16 = (m3l*2^8+m2l)*2^16 k=m3l*2^8+m2l = kh * 2^15+kl とすると (m3l*2^8+m2l)*2^16 = kh*2^31+kl*2^16 +(n1m*n2l+n1l*n2m)*2^8 + n1l*n2l はオーバーフロー無しで計算できるので kl*2^16 +(n1m*n2l+n1l*n2m)*2^8 + n1l*n2l > MAXLONG kl*2^16 > MAXLONG - ((n1m*n2l+n1l*n2m)*2^8 + n1l*n2l) これは、大小判定のみにして、余計な計算を省いたものです。 「'数値を減らす処理」が「オーバーフローした桁を無視する」なら、 -MAXLONG + kl*2^16 +(n1m*n2l+n1l*n2m)*2^8 + n1l*n2l で求められます。 しかし、resultから足したり割ったりするなら、上記で計算を省略した X * 2^31 の部分が必要です。 こちらだと、「多倍長数」「任意精度数」と呼ばれる手法が必要です。 例えば num1=n1(0)+n1(1)*2^8+n1(2)*2^16+n1(3)*2^24 として result=num1+num2 は d=n1(0)+n2(0) r(0)=d mod 256 ' dの下8bit c = d \ 256 '繰り上がり分 d=n1(1)+n2(1)+c '繰り上がり分を足す r(1)=d mod 256 c = d \ 256 .... と筆算の要領で「桁」毎に計算します。 あるいは、外部の多倍長精度ライブラリをアドオン等の形でVBAに取り込む方法があります。 試していませんが、検索で最上位に来たものです。 http://supermab.com/biginteger.html

tuktukrace
質問者

お礼

ありがとうございます。 実際に書いてはいませんがこれが正解だと思います。 結局、C言語でシンプルな処理を書いて(なぜかオーバーフローしませんでした)それをexe化してVBAからコマンドラインで呼び出し、その戻り値を受け取る形で落ち着きました。 なんなんでしょうねぇ。。。。

その他の回答 (6)

  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.6

#5の方へ よこからで、恐縮ですが、 私も、当初、通貨型を考えたのですが 通貨型は整数で、15桁~16桁でオーバーフローします。 従って、 Dim work As Currency work = CCur(num1) * CCur(num2) で、num1とnum2がMAXLONGに近い値をとる場合、 work = CCur(num1) * CCur(num2) のところで、エラーが発生します。 従って、今回の問題の完全な解決にはなりませんでした。

回答No.5

作業変数として通貨型を使うのはどうでしょう? Dim MAXLONG As Long MAXLONG = &H7FFFFFFF 'Long型の正の数の上限です。 Dim work As Currency work = CCur(num1) * CCur(num2) If ( work > MAXLONG) Then End If

tuktukrace
質問者

お礼

ありがとうございます。 いろいろ試しているのですが、もはや型だけの問題ではなく、そもそもオーバーフローさせないしくみが必要なようです。

  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.4

以下のようにしてみてはいかがでしょうか。 num1*num2 を行うfunction を作成する。 OKの場合、その結果を返す。 エラー(オーバーフロー)の場合、-1を返す。 以下、そのサンプルです。 ---------------------------------------- Option Explicit Sub test() Dim MAXLONG As Long Dim num1 As Long Dim num2 As Long Dim result As Long MAXLONG = &H7FFFFFFF num1 = 3456 num2 = 34567 result = mult(num1, num2) If (result = -1) Then MsgBox ("Error:" & result) Else MsgBox ("OK:" & result) End If End Sub Function mult(num1 As Long, num2 As Long) As Long On Error GoTo error1 mult = num1 * num2 Exit Function error1: mult = -1 End Function ------------------------------------

  • nishi6
  • ベストアンサー率67% (869/1280)
回答No.3

こんな感じでどうでしょうか。 Sub CalcTest()   Const MAXLONG As Long = &H7FFFFFFF   Dim num1 As Long   Dim num2 As Long   Dim result As Long   num1 = 123456   num2 = 134567   On Error GoTo ErrorTrp   Do Until num1 * num2 <= MAXLONG     '数値を減らす処理(例です)     num1 = num1 - 10     num2 = num2 - 10   Loop   result = num1 * num2   MsgBox num1 & " × " & num2 & " = " & result   Exit Sub ErrorTrp:   Resume Next End Sub

tuktukrace
質問者

お礼

ありがとうございます。 エラー処理で分岐するところまでできましたが、数が大きすぎると数値を減らす処理のところでやはり溢れてしまいました。

  • rinkun
  • ベストアンサー率44% (706/1571)
回答No.2

num1*num2を計算した時点で(resultに代入するまでもなく)エラーになりそうな気もしますが・・・ とりあえずDouble型で計算して範囲チェックしてからLong型に代入しては? ただ32bit×32bitの全範囲で計算するなら、結果は64bit全範囲を取り得るのでDoubleでも正確には表せなくなる可能性がありますけど。 # Long内で処理できるように16bit単位で計算するとかした方が正解かも

tuktukrace
質問者

お礼

ありがとうございます。 Doubleでもオーバーフローする値だと溢れてしまいました。

  • f272
  • ベストアンサー率46% (8469/18132)
回答No.1

ぜんぜん確認していないけど,例えば if (num1 > (MAXLONG / num2)) then '数値を減らす処理 else result = num1 * num2 endif で何とかなるんじゃないかな?

tuktukrace
質問者

お礼

ありがとうございます。 ある程度の数ならできましたが、思いっきり大きな数になるとオーバーフローしてしまいました。

関連するQ&A