2

Visual Basic 2010: 最近、私のプロジェクトの 1 つで、いくつかのデータ フィールドを 16 進数で読み取る作業を担当しました。各フィールドの長さは 3 文字です。だからこれは私がやっていることです:

'Read the hex value and convert it to decimal
Dim hexVal as string  = "38D"
Dim intVal as integer = Convert.ToInt32(hexVal, 16)

'The integer result gets multiplied by a scaling factor
'(this is set by the manufacturer for each field).
Dim fac as single  = 200
Dim final as single  = intVal * fac
Debug.Print (final)

これは、この場合を除いてうまく機能しています: hexVal="FFD" と fac=32 .NET は intVal=4093 と final=130976 を与えます。しかし、私がチェックしている従来のシステムでは -96 が返されます。

私は少し混乱していますが、16 進数表記であると理論付けています。生データについて私が持っている唯一の文書には、次のように記載されています。文字は、1 文字あたり 7 ビットを使用して、つまりパリティ ビットを追加せずに、ISO アルファベット No 5 に従ってコード化されています。このような 3 つの文字で、各 32 ビット ワードのフィールドが構成されます。

これを間違って変換していますか?

補遺: フィールドの定義を調べたところ、ほとんどすべてが正 (または符号なし) であると予想されることがわかりました。負または正 (符号付き) になるものもいくつかあります。従来のコードを見ると、16 進フィールドごとに、符号なしの結果と符号付きの結果が計算されます。フィールドが常に正であると予想される場合は、符号なしの結果が使用されます。フィールドが負または正であると予想される場合は、符号なしの結果を取得し、上限よりも高い場合は符号付きの結果を使用し、それ以外の場合は符号なしの結果を使用します。これは、以下のスニペットでこれまでに得たものです。

    Dim hexVal As String, res As Single, decCeiling As Single
    Dim paddedHex1 As String, intVal1 As Integer = 0, resVal1 As Single = 0
    Dim paddedHex2 As String, intVal2 As Integer = 0, resVal2 As Single = 0
    Dim IsUnsignedInput As Boolean

    hexVal = "FB1"   'Raw hex input (in this case represents temperature in deg C)
    res = 0.125      'A resolution factor. Used to scale the decimal to a real-world nummber
    decCeiling = 150 'The maximum temperature we can expect is 150 degree Celcius
    IsUnsignedInput = False 'Is field unsigned or signed (for temps we can expect negative and positive)
    If hexVal.Length > 8 Then
        Throw New Exception("Input '" & hexVal & "' exceeds the max length of a raw input. The max is 8 characters.")
    EndIf

    'This calcualtion assumes an unsigned value (that is, always a positive number)
    paddedHex1 = hexVal.ToString.PadLeft(8, CChar("0"))
    intVal1 = Convert.ToInt32(paddedHex1, 16)
    resVal1 = intVal1 * res

    'This assumes a signed value (that is, could be a negative OR positive number.
    'Use two's complement to arrive at the result.
    paddedHex2 = hexVal.PadLeft(8, CChar("F"))
    Dim sb As New StringBuilder(paddedHex2.Length)
    For i As Integer = 0 To paddedHex2.Length - 1
        Dim hexDigit As Integer = Convert.ToInt32(paddedHex2(i), 16)
        sb.Append((15 - hexDigit).ToString("X"))
    Next i

    Dim inverted As Integer = Convert.ToInt32(sb.ToString, 16)
    intVal2 = -(inverted + 1)
    resVal2 = intVal2 * res

    'Finally, which result do we use as the final decimal? For our example we get
    'resVal1 (unsigned)=+502.125
    'resVal2 (signed)  =  -9.875
    'Field is signed so is 502.125 > 150? Yes, so use the signed result of -9.875.
    If IsUnsignedInput Then
        'If unsigned then we always expect a positive value so use straight conversion.
        Debug.Print("Result=" & resVal1)
    Else
        'If signed then we expect a positive OR negative value
        If resVal1 > decCeiling Then
            'Standard conversion yields a higher number than expected so use two's complement to get number
            Debug.Print("Result=" & resVal2)
        Else
            'Standard conversion yields a number that is in the expected range so use straight conversion to get number
            Debug.Print("Result=" & resVal1)
        End If
    End If

従来のシステムと背中合わせに比較すると、すべて一致しますが、過去に 16 進数であまり機能していないので、少し注意が必要です。このアプローチに関するさらなるフィードバックをいただければ幸いです。

4

1 に答える 1

4

intVal=4093 が正しいです。
final=130976 も正しいです。

FFDは、2 の補数で表される -3 として解釈することもできます (これは、コンピューターが負の値を格納する方法です)。32 * -3 = -96。

FFD               = 111111111101 (binary)

2 の補数では、最初のビットが a の場合1、数値が負であることを意味します。数値を否定するには、まずすべてのビットを反転してから 1 を追加します。

FFD inverted =      000000000010
+ 1          =      000000000011 = 3 (decimal).

3 は否定された数なので、実数は -3 でなければなりません。

これは Hex 表記でも行うことができます。最初の 16 進数が >= 8 の場合は、負の数です。したがって、次の置換を行って数値を否定します。

0 -> F
1 -> E
2 -> D
3 -> C
4 -> B
5 -> A
6 -> 9
7 -> 8
8 -> 7
9 -> 6
A -> 5
B -> 4
C -> 3
D -> 2
E -> 1
F -> 0

(2 列目は単純に 16 進数を逆順に並べたものです。)

したがって、FFD の場合は 002 (16 進数) になります。この 16 進数を 10 進数に変換して 1 を足すと 3 になります。これは負の値を表すため、-1 を掛けます。3 * -1 = -3。

Function ConvertHex(ByVal hexVal As String) As Integer
    If hexVal(0) >= "8"c Then 'We have a negative value in the 2's complement
        Dim sb As New System.Text.StringBuilder(hexVal.Length)
        For i As Integer = 0 To hexVal.Length - 1
            'Convert one hex digit into an Integer
            Dim hexDigit As Integer = Convert.ToInt32(hexVal(i), 16)

            'Invert and append it as hex digit
            sb.Append((15 - hexDigit).ToString("X"))
        Next i

        'Get the inverted hex number as Integer again
        Dim inverted As Integer = Convert.ToInt32(sb.ToString(), 16)

        'Add 1 to the inverted number in order to get the 2's complement
        Return -(inverted + 1)
    Else
        Return Convert.ToInt32(hexVal, 16)
    End If
End Function

ただし、常に3桁の16進数がある場合は、これを簡単に行うことができます

Function ConvertHex3(ByVal hexVal As String) As Integer
    Dim number = Convert.ToInt32(hexVal, 16)
    If hexVal(0) >= "8"c Then
        Return number - &H1000
    Else
        Return number
    End If
End Function

それははるかに賢いです!

于 2014-09-10T21:02:07.007 に答える