アメブロのランキングはあまり気にしていないのですが、検索ワードはおもしろいのでちょくちょくチェックしています。


やはり、C++関係のキーワードで検索してこのブログに到達する人が多いのですが、中にはC++以外のキーワードでやってくる人もいらっしゃいます。


その中で、ときどき現れる検索ワードで気になっていたものがあります。


VBA エンディアン 変換


「VBAでビッグエンディアン⇔リトルエンディアンを変換? そんな処理が必要になるプログラムをVBAで書くの?」と思いつつも、そのうち変換関数をつくって記事に書こうと思ってました。(しかし最近この検索ワードは現れません)


ところが実際にやってみるとこれが意外とてこずりました(汗)


VBAだからC/C++やJavaなどで使える << や >> などのビットシフト演算が使えないのはわかっていましたが、掛け算・割り算で代用できるので簡単だろうと思ってました。そして、最初はこんなコードを書いていました…



Function ReverseIntEndian(ByVal Data As Integer) As Integer

  注意!これは間違った実装

  ReverseIntEndian = (Data And &HFF) * &H100

  ReverseIntEndian = ReverseIntEndian Or (Data / &H100)

End Function


Function ReverseLngEndian(ByVal Data As Long) As Long

  注意!これは間違った実装

  ReverseLngEndian = ReverseIntEndian(Data And &HFFFF&) * &H10000

  ReverseLngEndian = ReverseLngEndian _

                Or ReverseIntEndian(Data / &H10000)

End Function



ReverseIntEndian関数とReverseLngEndian関数は、それぞれ、2バイト、4バイト整数のビッグエンディアン⇔リトルエンディアンを変換する関数です(しかし上の実装には誤りがあります)。


上の実装では、16進数の&H100を掛けたり割ったりすることで、左8ビットシフト、右8ビットシフト演算の代用にしています。同様に&H10000を掛けたり割ったりして16ビットシフト演算の代用にしています。


いちおう、上の実装でも次のテストコードは通ります…



Sub TestReverseEndian()

  Debug.Assert &H201 = ReverseIntEndian(&H102)

  Debug.Assert &H4030201 = ReverseLngEndian(&H1020304)

End Sub



しかし、次のようなテストケースを追加するとテストに失敗します。


Debug.Assert &H8F7F = ReverseIntEndian(&H7F8F)


VBAは符号なし整数が使えないのでした… そして、32768(&H8000&)より大きな数を2バイトのInteger型の変数に代入しようとするとオーバーフローしたといって怒られます…


使い慣れない言語はいろいろ勝手が違うので、ハマりますね。


う~は、この時点で、いちどこの実装を捨ててテスト駆動開発に切り替えることにしました(最初からそうすべきでした 汗)。詳細を書くと長くなりそうなのでそれは別記事にするとして、出来た実装が次のようなものです(他にも実装方法はあると思いますが)。



Function ReverseIntEndian(ByVal Data As Integer) As Integer

  Dim Temp As Long

  Temp = (CLng(Data) And &HFF&) * &H100&

  Temp = Temp Or ((CLng(Data) And &HFF00&) \ &H100&)

  If &H8000& And Temp Then

    ReverseIntEndian = Temp Or &HFFFF0000

  Else

    ReverseIntEndian = Temp

  End If

End Function


Function ReverseLngEndian(ByVal Data As Long) As Long

  ReverseLngEndian = ReverseIntEndian(Data And &H7FFF&) * &H10000

  ReverseLngEndian = ReverseLngEndian Or _

    (&HFFFF& And ReverseIntEndian((Data And &HFFFF0000) \ &H10000))

  If &H8000& And Data Then

    ReverseLngEndian = ReverseLngEndian Or &H800000

  End If

End Function



あまり美しくありませんが、まあ、しょうがないですね。


(なんとかIf文を使わずに実装できないか悪あがきしましたがあきらめました)


そういえば、VB .NETはビットシフト演算が使えるんですよね。