空腹おやじのログと備忘録

VBA(主にExcel)でいろいろな実験的な事とか、Linuxのコマンドとか設定とかについて忘れないように、あれこれと・・・

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))

f:id:Z1000S:20190417222952j:plain
orz

何処?
f:id:Z1000S:20190418213350j:plain

? 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))

f:id:Z1000S:20190417222952j:plain
orz
今度は何処ですか・・・
f:id:Z1000S:20190418220422j:plain

? byLowByte * &H100

f:id:Z1000S:20190418214015j:plain
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