VBAで、エンディアンの変換をやろうとしたら、やっぱりハマった
Windowsを使っていると、ソケット通信のコーディングでもしない限り、ほとんど意識する事がないであろうビッグエンディアンとリトルエンディアンに関する事です。
まず、「エンディアンって何それ美味しいの?」って人は、こちらをどうぞ。
ja.wikipedia.org
やろうとしているのは、1Byte単位で、上位下位を入れ替えるというもので、例えば、
Integerなら、16進表記で、&H1234 を &H3412 に
Longなら、16進表記で、&H12345678 を &H78563412
に変換するというものです。
まず、Integerから。サクッとこんな感じかな?
Public Function convertEndian16NG(ByVal iValue As Integer) As Integer Dim byHighByte As Byte Dim byLowByte As Byte byHighByte = (iValue And &HFF00) \ &H100 byLowByte = iValue And &HFF convertEndian16NG = (byLowByte * &H100) Or byHighByte End Function
じゃあ、テスト。
? hex(convertEndian16NG(&H1234)) 3412
OK。もうひとつ。
? hex(convertEndian16NG(&H8765))
orz
何処?
? hex(iValue And &HFF00) 8700
OK
? typename(iValue And &HFF00) Integer
OK
? hex((iValue And &HFF00)\&H100) FF87
ん???
87 になるはずが、FF87 になってる!
最上位ビットが1だから(別の言い方をすれば、負の値だから)、右シフトしてきた時に、頭が0でなくて、1になるのか・・・
忘れてた。
じゃあ、最上位ビットが "1" でなければいいから、強制的に Long になるように
iValue と And を取る &HFF00 を Integer から Long に変えるおまじないをしよう。( &HFF00 → &HFF00& )
? &HFF00 -256 ? &HFF00& 65280
Public Function convertEndian16NG2(ByVal iValue As Integer) As Integer Dim byHighByte As Byte Dim byLowByte As Byte byHighByte = (iValue And &HFF00&) \ &H100 byLowByte = iValue And &HFF convertEndian16NG2 = (byLowByte * &H100) Or byHighByte End Function
再テスト
? hex(convertEndian16NG2(&H1234)) 3412 ? hex(convertEndian16NG2(&H8765)) 6587
OK。
もうひとつ
? hex(convertEndian16NG2(&H1080))
orz
今度は何処ですか・・・
? byLowByte * &H100
32768(&H8000 ::正値 Integerで &H8000 は、-32768)だから、Integerの上限値(32767 : &H7FFF)を超えてしまうのね。
Byte と Integer の掛け算だから、Integerになる想定だったのに、Integerに収まらなくてオーバーフローになっちゃうのね。
じゃあ、下位の1Byteは最上位ビットを除いて処理して、後からその分を補正してやろう。
Public Function convertEndian16(ByVal iValue As Integer) As Integer Dim byHighByte As Byte Dim byLowByte As Byte Dim iNewValue As Integer byHighByte = (iValue And &HFF00&) \ &H100 byLowByte = iValue And &HFF iNewValue = ((byLowByte And &H7F) * &H100) Or byHighByte If (byLowByte And &H80) = &H80 Then iNewValue = iNewValue Or &H8000 End If convertEndian16 = iNewValue End Function
再々テスト
? hex(convertEndian16(&H1234)) 3412 ? hex(convertEndian16(&H8765)) 6587 ? hex(convertEndian16(&H1080)) 8010 ? hex(convertEndian16(&HFEDC)) DCFE ? hex(convertEndian16(&H0F08)) 80F ? hex(convertEndian16(&HFFFF)) FFFF ? hex(convertEndian16(0)) 0
とりあえず、良さげ。
以上を踏まえて、Long版は・・・
と行こうかと思ったら、Longの場合、Integerの場合のように、上位の整数型が無いから出来ないじゃん。
(64bitバージョンには、LongLongがあるの?うちの娘に入ってるの、32bitバージョンだから・・・)
ここまでやっておいて、どうするつもりなの???
・
・
・
・
・
今更なんですが、IntegerでもLongでも、もっと簡単に出来る方法を思いついちゃったんですよ。
さっきまでの前振りは何だったの?
おまけ
データ型 | 識別子の型文字 |
---|---|
Integer | % |
Long | & |
Decimal | @ |
Single | ! |
Double | # |
String | $ |
? typename(1) Integer ? typename(1%) Integer ? typename(1&) Long ? typename(1@) Currency ? typename(1!) Single ? typename(1#) Double