Visual Basic 2010: Recently in one of my projects I have been tasked with reading several data fields in hex. Each field is three characters long. So this is what I have been doing:
'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)
This has been working great except in this case: hexVal="FFD" and fac=32 .NET gives me intVal=4093 and the final=130976. However the legacy system I am checking against gives a -96.
I'm a bit confused but theorize that it's in the hex notation. The only documentation I have for the raw data states: Characters are coded per ISO Alphabet No 5 using seven bits per character, that is, without the addition of a parity bit. Three such characters will comprise the field of each 32-bit word.
Am I converting this incorrectly?
Addendum: I have looked into the definitions of the fields and have discovered that almost all are expected to be positive (or unsigned). A few can be negative or positive (signed). Looking at the legacy code, for each hex field they compute a unsigned result and a signed result. If the field is expected to be always positive then they use the unsigned result. Now if the field is expected to be negative or positive then they take the unsigned result and if higher than a ceiling then they use the signed result, otherwise they use the unsigned result. Here is what I have so far with the snippets from below:
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
Doing a back-to-back comparison with the legacy system it all matches up, but it is not working with hex too much in the past, and I am a little cautious. I would appreciate any further feedback on this approach.