やりすぎ?! 本当に無駄!! な、コードを公開したいだけの車輪の再発明w
除算ルーチンを自作してみました。
ただし・・・
Function Divide(ByVal d As Variant, ByVal e As Variant, _
ByRef q As Variant, ByRef r As Variant) As Boolean
' @params
' d ; (in)被除数
' e ; (in)除数
' q ; (out)商
' r ; (out)剰余
Dim w As Variant
Divide = True
q = CDec(0)
r = CDec(0)
If (e = 0) Then ' ゼロ除算
Divide = False ' エラー
Exit Function
End If
If (d = 0) Then ' 被除数 = 0
Exit Function
End If
If (d < e) Then ' 被除数 < 除数
r = d
Exit Function
End If
w = CDec(1) ' 桁の重み
Do While (d - e >= e)
e = e + e ' e = e * 2 左シフト
w = w + w ' w = w * 2
Loop
Do While (w)
If (d >= e) Then
d = d - e
q = q + w
End If
e = Int(e / 2) ' ★ 右シフト
w = Int(w / 2)
Loop
r = d
End Function
Sub test_driver() ' テストコードのエントリーポイント
Const MAX_DIGITS As Long = 28
Dim q, r
Dim d, e
Dim i As Long
Dim n As Long
For i = 1 To 10000
n = GetRandom(1, MAX_DIGITS) ' 被除数の桁
d = CDec(RandomNumberString(n))
n = GetRandom(1, n) ' 除数の桁は、被除数以下とする
e = CDec(RandomNumberString(n))
If (Divide(d, e, q, r)) Then
Debug.Assert q = Quotient(d, e) And r = Modular(d, e)
If (i Mod 100 = 0) Then
Debug.Print d & " / " & e & " = " & q & "..." & r
End If
End If
Next i
Debug.Print "$"
End Sub
Function RandomNumberString(ByVal n As Long) As String
' @params
' n ; 桁数
Dim i As Long
Dim s As String
s = ""
For i = 1 To n
s = s & CStr(GetRandom(0, 9))
Next i
RandomNumberString = s
End Function
Function GetRandom(ByVal lo As Long, ByVal hi As Long) As Long
' @params
' lo ; 下限値
' hi ; 上限値
GetRandom = Int((hi - lo + 1) * Rnd() + lo)
End Function
・・・ただし、「出来た!!」の「た〜」の辺りで気づいてしまった
のですが、除算の実装(Divide)の中で、除算演算子("/")を使って
しまっているではないですか・・・orz (★ のところ)
右シフトがしたいだけなのに。。。
e と w は、ともに左シフトして、それをまた後戻りするだけなので、
行き(左シフト)の値を退避して、帰り(右シフト)に使用することに。
' その2
Function Divide(ByVal d As Variant, ByVal e As Variant, _
ByRef q As Variant, ByRef r As Variant) As Boolean
' @params
' d ; 被除数
' e ; 除数
' q ; 商
' r ; 剰余
Dim w As Variant
Dim ae(96) As Variant ' 左シフトの値を退避
Dim aw(96) As Variant '
Dim n As Long '
Divide = True
q = CDec(0)
r = CDec(0)
If (e = 0) Then ' ゼロ除算
Divide = False ' エラー
Exit Function
End If
If (d = 0) Then ' 被除数 = 0
Exit Function
End If
If (d < e) Then ' 被除数 < 除数
r = d
Exit Function
End If
w = CDec(1) ' 桁の重み
n = 1 ' 左シフトの値を退避
ae(n) = e '
aw(n) = w '
Do While (d - e >= e)
e = e + e ' e = e * 2 左シフト
w = w + w ' w = w * 2
n = n + 1 ' 左シフトの値を退避
ae(n) = e '
aw(n) = w '
Loop
Do While (w)
If (d >= e) Then
d = d - e
q = q + w
End If
'''' e = Int(e / 2) ' ★ 右シフト
'''' w = Int(w / 2)
e = ae(n) ' 退避した左シフトの値を復元
w = aw(n) ' (右シフトの代用)
n = n - 1 '
Loop
r = d
End Function
かなり不恰好になりましたw
仮に、Long型の配列をひとつの大きな整数とするような
ユーザ定義型(※)を作成し、その型を操作するいくつかの
シンプルな機能:
その型同士の比較、
加算、
減算、
左右シフト、
数字列をその型へ変換、
その型を数字列へ変換
を実装すれば、かなり大きな値を扱うことが可能になり
そうです(本当かな?)。
(※) たとえば
Type BigInteger
v(1 To ?) As Long
End Type
加算時のキャリーのことなどを考えると、各要素の下位 16ビット
だけを使用するなどすると、扱いやすいのか。
# 何をひとりで盛り上がってるんだ → オレww
長々とスレ汚し、失礼いたしました〜 m(_ _)m
# テストドライバ中の Quotient、Modular は 月 さんのソレです