アメブロのランキングはあまり気にしていないのですが、検索ワードはおもしろいのでちょくちょくチェックしています。
やはり、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はビットシフト演算が使えるんですよね。