2

Visual Basic 2010:最近在我的一个项目中,我的任务是读取多个十六进制数据字段。每个字段长度为三个字符。所以这就是我一直在做的:

'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。

我有点困惑,但理论上它是十六进制表示法。我拥有的关于原始数据的唯一文档是:字符是按照 ISO Alphabet No 5 编码的,每个字符使用 7 位,也就是说,没有添加奇偶校验位。三个这样的字符将构成每个 32 位字的字段。

我是否错误地转换了这个?

附录:我研究了这些字段的定义,发现几乎所有字段都应该是正数(或无符号数)。少数可以是负数或正数(有符号)。查看遗留代码,对于每个十六进制字段,他们计算一个无符号结果和一个有符号结果。如果预计该字段始终为正,则他们使用无符号结果。现在,如果该字段预计为负数或正数,则他们采用无符号结果,如果高于上限,则使用有符号结果,否则使用无符号结果。到目前为止,这是我对以下片段的了解:

    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

与遗留系统进行背靠背比较,这一切都匹配,但过去它并没有过多地使用十六进制,我有点谨慎。我将不胜感激有关此方法的任何进一步反馈。

4

1 回答 1

4

intVal=4093 是正确的。
final=130976 也是正确的。

FFD也可以解释为以二进制补码表示的 -3(这就是计算机存储负值的方式)。32 * -3 = -96。

FFD               = 111111111101 (binary)

在二进制补码中,当第一位为 a1时,表示该数为负数。为了取反,首先反转所有位,然后加 1:

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

由于 3 是负数,所以实数必须是 -3。

您也可以使用十六进制表示法执行此操作。如果第一个十六进制数字 >= 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

(第二列只是以相反的顺序列出了十六进制数字。)

因此,对于 FFD,您会得到 002(十六进制)。您可以将此十六进制数转换为十进制数并加 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 个十六进制数字,你可以简单地这样做

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 回答