プロが教えるわが家の防犯対策術!

こんにちは

とあるハッシュ値のようなものを作成するプロセスで困っています。

次のようなコードを書いています。

Dim MAXLONG As Long
MAXLONG = &H7FFFFFFF 'Long型の正の数の上限です。

result = num1 * num2
If ( result > MAXLONG) Then
'数値を減らす処理

計算結果が Long 型の最大値を超えている場合に上限に収まるよう調整する処理をしたいのですが、最大値を超えている時点で result 変数に格納できずにエラーになってしまいます。

変数を事前にすべて As Long で宣言し、数値ではなく &H付きの 16 進数で代入してみたり、Clng() で囲ってみたり・・・いろいろ試したのですができません。

最終的には Long 型の範囲内に収めてその数値を使いたいのですが、一時的に溢れたかどうかをどのように判定すればよいのでしょうか?

補足すべき事があれば教えてください。

Windows 7 Pro 64bit Excel 2007 (これは32bit)

A 回答 (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
    • good
    • 0
この回答へのお礼

ありがとうございます。
実際に書いてはいませんがこれが正解だと思います。

結局、C言語でシンプルな処理を書いて(なぜかオーバーフローしませんでした)それをexe化してVBAからコマンドラインで呼び出し、その戻り値を受け取る形で落ち着きました。

なんなんでしょうねぇ。。。。

お礼日時:2014/08/01 09:14

#5の方へ


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

作業変数として通貨型を使うのはどうでしょう?



Dim MAXLONG As Long
MAXLONG = &H7FFFFFFF 'Long型の正の数の上限です。

Dim work As Currency
work = CCur(num1) * CCur(num2)
If ( work > MAXLONG) Then
End If
    • good
    • 0
この回答へのお礼

ありがとうございます。

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

お礼日時:2014/08/01 09:11

以下のようにしてみてはいかがでしょうか。


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
------------------------------------
    • good
    • 0

こんな感じでどうでしょうか。



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
    • good
    • 0
この回答へのお礼

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

お礼日時:2014/08/01 09:10

num1*num2を計算した時点で(resultに代入するまでもなく)エラーになりそうな気もしますが・・・



とりあえずDouble型で計算して範囲チェックしてからLong型に代入しては?
ただ32bit×32bitの全範囲で計算するなら、結果は64bit全範囲を取り得るのでDoubleでも正確には表せなくなる可能性がありますけど。
# Long内で処理できるように16bit単位で計算するとかした方が正解かも
    • good
    • 0
この回答へのお礼

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

お礼日時:2014/08/01 09:10

ぜんぜん確認していないけど,例えば


if (num1 > (MAXLONG / num2)) then
'数値を減らす処理
else
result = num1 * num2
endif
で何とかなるんじゃないかな?
    • good
    • 0
この回答へのお礼

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

お礼日時:2014/08/01 09:09

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!