こんにちは
とあるハッシュ値のようなものを作成するプロセスで困っています。
次のようなコードを書いています。
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)
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
ありがとうございます。
実際に書いてはいませんがこれが正解だと思います。
結局、C言語でシンプルな処理を書いて(なぜかオーバーフローしませんでした)それをexe化してVBAからコマンドラインで呼び出し、その戻り値を受け取る形で落ち着きました。
なんなんでしょうねぇ。。。。
No.6
- 回答日時:
#5の方へ
よこからで、恐縮ですが、
私も、当初、通貨型を考えたのですが
通貨型は整数で、15桁~16桁でオーバーフローします。
従って、
Dim work As Currency
work = CCur(num1) * CCur(num2)
で、num1とnum2がMAXLONGに近い値をとる場合、
work = CCur(num1) * CCur(num2)
のところで、エラーが発生します。
従って、今回の問題の完全な解決にはなりませんでした。
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
------------------------------------
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
ありがとうございます。
エラー処理で分岐するところまでできましたが、数が大きすぎると数値を減らす処理のところでやはり溢れてしまいました。
No.2
- 回答日時:
num1*num2を計算した時点で(resultに代入するまでもなく)エラーになりそうな気もしますが・・・
とりあえずDouble型で計算して範囲チェックしてからLong型に代入しては?
ただ32bit×32bitの全範囲で計算するなら、結果は64bit全範囲を取り得るのでDoubleでも正確には表せなくなる可能性がありますけど。
# Long内で処理できるように16bit単位で計算するとかした方が正解かも
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Visual Basic(VBA) VBAでのMATCH関数 3 2022/10/17 19:06
- Visual Basic(VBA) 数字が「0」の列を削除するため、下記のコードを実行しましたが、コンパイルエラーSubまたはFunct 3 2022/12/04 00:00
- Visual Basic(VBA) 数式が消える 1 2023/03/19 16:55
- Visual Basic(VBA) 前回ご教授いただいたコードに覚えたてのループ処理で品名りんごAから順に20回for nextでループ 7 2023/01/13 22:01
- Visual Basic(VBA) 別シートのデータを参照して値を入れたい。 まとめデータシートのC列D列の値を商品一覧シートのコードが 7 2022/08/17 13:20
- Visual Basic(VBA) いつもお世話になっております、VBAで教えて頂きたいのですが 2 2022/05/05 22:20
- Excel(エクセル) VBAで組み合わせ算出やCOUNTIFSの処理を高速化したいです。 4 2022/04/07 02:38
- Visual Basic(VBA) VBAで時間(00:00形式)を積算(足し算)したい 1 2022/11/15 17:04
- Excel(エクセル) VBA オリジナル関数で選択セルの合計を作成したい 3 2023/03/19 19:45
- Visual Basic(VBA) 貼り付けた値が消えていく 以下はソースファイルの2番目のシートのB6から最終行を取得 ターゲットファ 2 2023/07/27 12:23
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
65536は2の何乗なのでしょうか?
-
CとFORTRANの計算速度はどちら...
-
プログラミング 処理速度
-
60進数の四則計算
-
再帰呼び出しの計算量
-
構文解析を利用した計算プログ...
-
数値計算の高速化 (cos, sin, exp)
-
Perlでのルートの計算
-
排他的論理和 BCC(水平パリテ...
-
Perlで時間の計算
-
DLL(VC++で作った)で稼動中の...
-
Visual C++でdebugとreleaseで...
-
「評価」「評価値」という言葉...
-
[急募]Pythonについてです。
-
10進数から8進数へ
-
VBAの再計算が反映されない件に...
-
VB6.0でのバイナリデータの扱い...
-
R言語での極小値の指数形式での...
-
円周上の3点で作られる三角形...
-
アドオン利率を実質年率に変換
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
65536は2の何乗なのでしょうか?
-
VBAの再計算が反映されない件に...
-
排他的論理和 BCC(水平パリテ...
-
EXCELなどで「返す」という表現
-
C言語の課題で、1年の秒数を計...
-
バッチファイルでウインドウを...
-
骨折リスク評価のFRAXについて...
-
変化させるセルが変化しない
-
CとFORTRANの計算速度はどちら...
-
なぜオーバーフローになるので...
-
数値計算の高速化 (cos, sin, exp)
-
モジュラス103の計算とは何でし...
-
C# 計算処理中に実行中ウィン...
-
モジュロ
-
引き放し法による除算アルゴリ...
-
60進数の四則計算
-
C言語についてです。 再帰を使...
-
Perlで時間の計算
-
CRC8を教えてください
-
傾いた四角形内の範囲の条件式
おすすめ情報