トロPのVBAホビラボ 32ビット符号無し整数 その4 [トロPのVBAホビラボ]
ここまでで加算、減算、比較、表示をこの順に実現できました。ここにきて順番を意識しましたが、これは後々いままで実現してきたものを見直すためです。
普通に演算子を使うのではなく、プロシージャーを呼び出して演算を実現してきていますが、プロシージャーを呼び出して足し算などを行うことに慣れていません。
ですから、使う時になると、慣れるまで注意が必要です。
また、あまりに基礎的なものなので、今後ずっとよく使うものという性質もあります。品質を上げることも重要ポイントになります。
今回は予定通り乗算を考えていきます。演算の実現の中でもプロシージャーを使った演算をしますので、これからは次の注意をはじめから入れていきましょう。
- 符号無し32ビット整数の加算、減算、比較は、
必ず作成済みのプロシージャーを使う。 - リテラルの32ビット整数には必ず型サフィックスを付ける。
実際に利用するときも、これらを守ることは大切です。
さて、乗算のロジックです。これは10進数の掛け算を筆算で行うときの要領で、2進数で筆算を行っていきます。
- 乗数の1桁目が1なら被乗数、0なら0を初期値とする。
- 乗数の2桁目が1なら被乗数に2(2の1乗)を掛けて足す。
- 乗数の3桁目が1なら被乗数に4(2の2乗)を掛けて足す。
- 以降32桁目まで、乗数のn桁目が1なら被乗数に2の(n-1)乗を掛けて足す。
ここで、2のn乗を掛けるというところがまだ実装していない掛け算の表現になっています。これはいけません。
その1で足し算を論理演算で実現したように、全く別の、しかも非常に単純な機械的な方法はないでしょうか。
ここで、2倍するということを2進数の世界で考えると、実は1桁左にずらずというだけのことです。2のn乗倍なら左にn桁ですね。
このようなことをする演算はシフト演算と呼ばれています。
では、シフト演算の実現を考えましょう。
とは言っても、
これが全てです。本体は実に単純ですね。
注意点の1点目としては、オーバーフロー。
32桁目からずらずビット数分の桁(1ビットずらすなら32ビット目、2ビットずらすなら31ビット目と32ビット目、...)は、あふれてしまいます。
これを許すか許さないかは、オプション引数で対応しましょう。
許す場合は単純に溢れた桁を無視します。
注意点の2点目は、どれだけずらすことを考慮するか。
今回は純粋に左にずらすことだけを許しましょう。
なので、非正数を指定することは許しません。
また、32ビット以上ずらすことは、結果は0だと言っているに過ぎません。
ロジックを通すだけ時間の無駄です。
しかし、実用上境界あたりはエラーを生じない方が便利なことはあるかもしれません。
よって、ここでは33以上を許さないことにします。
以上により、次の順番でプログラムを組んでいきましょう。
- ずらすビット数に1から32が指定されているかチェックする。
- オーバーフローを許さないなら、オーバーフローチェックをする。
- 桁ずらしを実施する。
プログラムを書きますので、冒頭の注意点を思い出しましょう。
足し算、引き算、0以外との比較にはULngsSum、ULngsSub、ULngsCompを使います。特に比較は補助的に作りましたので忘れやすいですね。
また、直に数字を書くときは「&」を接尾します。
サンプル |
---|
Public Function ShiftLeftLogically _ (ByVal Value As Long, Optional ByVal Bits As Long = 1&, _ Optional ByVal OverFlowStop As Boolean = False) _ As Long Dim l_Bit As Long Dim l_Shifted As Long If (ULngsComp(Bits, 1&) < 0&) Or (0& < ULngsComp(Bits, 32&)) _ Then Err.Raise 9001&, , "The value of Bits is out of range from 1 to 32." If OverFlowStop Then For l_Bit = 32& To ULngsSub(33&, Bits) Step -1& If 0& <> (Value And LngBitXtrVal(l_Bit)) _ Then Err.Raise 9002&, , "An overflow has occurred." Next l_Bit End If l_Shifted = 0& For l_Bit = 32& To ULngsSum(Bits, 1&) Step -1& If 0& <> (Value And LngBitXtrVal(ULngsSub(l_Bit, Bits))) _ Then l_Shifted = (l_Shifted Or LngBitXtrVal(l_Bit)) Next l_Bit ShiftLeftLogically = l_Shifted End Function |
シフト演算が実現できましたので、乗算のロジックを修正しましょう。
- 乗数の1桁目が1なら被乗数、0なら0を初期値とする。
- 乗数の2桁目が1なら
被乗数に2(2の1乗)を掛けて被乗数を1ビット左にシフトして足す。 - 乗数の3桁目が1なら
被乗数に4(2の2乗)を掛けて被乗数を2ビット左にシフトして足す。 - 以降32桁目まで、乗数のn桁目が1なら
被乗数に2の(n-1)乗を掛けて被乗数を(n-1)ビット左にシフトして足す。
これでようやくプログラム化できますが、また冒頭の注意点を思い出しましょう。
足し算、引き算にはULngsSum、ULngsSubを使います。また、直に数字を書くときは「&」を接尾します。
サンプル |
---|
Public Function ULngsProd(ByVal Value1 As Long, ByVal Value2 As Long) As Long Dim l_Bit As Long Dim l_Prod As Long Dim l_RoopCnt As Long l_Prod = 0& If 0& <> (Value2 And LngBitXtrVal(1)) Then l_Prod = Value1 Else l_Prod = 0& End If For l_Bit = 2& To 32& If 0& <> (Value2 And LngBitXtrVal(l_Bit)) _ Then l_Prod = ULngsSum _ (l_Prod, ShiftLeftLogically(Value1, ULngsSub(l_Bit, 1&), True)) Next l_Bit ULngsProd = l_Prod End Function |
これで掛け算もできるようになりました。
イミディエイトウィンドウ |
---|
? ULngToStr(&H7FFFFFFF) 2147483647 ? ULngToStr(ULngsProd(2&, &H7FFFFFFF)) 4294967294 |
いままで実現してきた順番は次のようになっています。次回の割り算実現でも、前のものは必ず使うよう、後のものを使わないよう気を付けていきましょう。
- その1: ビットの取り出しLngBitXtrVal
- その1: 足し算ULngsSum
- その2: 2の補数ComplementOf2
- その2: 引き算ULngsSub
- その2: 大小比較ULngsComp
- その4: 論理左シフト演算ShiftLeftLogically
- その4: 掛け算ULngsProd
また、演算自体には関わりませんが、その3で書いておいた文字列化のプロシージャーULngToStrも何かと必要でしょう。
コメント 0