RESOLVED: Conversion from 2-part 64bit Floating Point to Fixed Point
So I have already figured out how to convert 32-bit (4 byte) Floating Point numbers to Fixed Point using VB's data types from a previous forum thread, in the form shown below-
Code:
<StructLayout(LayoutKind.Explicit)> _
Public Structure DataUnion
<FieldOffset(0)> Public FloatValue As Single
<FieldOffset(0)> Public LongValue As Long
<FieldOffset(0)> Public ByteValue As Byte
End Structure
I simply send my value into the Long field and retrieve it from the Single field.
Now I have a slightly more interesting problem, though- I need to combine (not add, but concatenate) two 4 byte (Single) values into one 8 byte (Double) value, convert to Fixed Point, and round down to Single.
Here is an example of what I mean:
I am working with a piece of hardware that outputs values in 8 byte (64-bit, or Double precision) Floating Point numbers. A typical Hex value would be 40 40 DD E4 2D 3B A2 6D. Unfortunately, the hardware that I use for data capture can only receive 4 byte (32-bit, or Single precision) values, so I capture two of them at an offset (i.e. Part 1 is 40 40 DD E4 and Part 2 is 2D 3B A2 6D). There is an added level of complexity, in that the software that is doing the conversion exposes these values (in its API) as decimal values (1077992932 and 758882925 would be my inputs in this example), so I have to convert them to another form first to even combine them.
When I convert the 8-byte number by hand, I get the proper decimal fixed point value (roughly 33.733 or so) but I'm not really sure how to convert to the proper data type and combine these values in VB. Has anyone done this before?
Re: Conversion from 2-part 64bit Floating Point to Fixed Point
So I've gotten closer but I'm not quite there yet. What I have attempted to do is convert my two singles to hexadecimal representations, combine them as strings, and I will then convert them back. This code is shown below, in its entirety-
FLOAT CONVERT Code:
Imports Microsoft.Win32
Imports System.Runtime.InteropServices
Module Module1
<StructLayout(LayoutKind.Explicit)> _
Public Structure DataUnion
<FieldOffset(0)> Public FloatValue As Single
<FieldOffset(0)> Public FloatLongValue As Double
<FieldOffset(0)> Public LongValue As Long
<FieldOffset(0)> Public ByteValue As Byte
End Structure
Sub Main()
Dim p1 As Single, p2 As Single
Dim ba1() As Byte, ba2() As Byte, wholeByte(7) As Byte
Dim hex1 As String, hex2 As String, whole As String
Dim suLong As Int64
Dim su As DataUnion
p1 = 1077992931
ba1 = BitConverter.GetBytes(p1)
hex1 = BitConverter.ToString(ba1)
p2 = 831725571
ba2 = BitConverter.GetBytes(p2)
hex2 = BitConverter.ToString(ba2)
whole = hex1 + "-" + hex2
ba1.CopyTo(wholeByte, 0)
ba2.CopyTo(wholeByte, 4)
Console.WriteLine("Part 1 is {0}d and part 2 is {1}d" & vbCrLf, p1, p2)
Console.WriteLine("Part1 byte array is {0}", hex1)
Console.WriteLine("Part2 byte array is {0}", hex2)
Console.WriteLine("Whole byte array is {0}", whole)
Console.WriteLine(vbCrLf)
suLong = BitConverter.ToInt64(wholeByte, 0)
su.LongValue = suLong
Console.WriteLine("suLong is {0} and su.LongValue is {1}", suLong, su.LongValue)
Console.WriteLine("su.FloatValue is {0}", su.FloatValue)
End Sub
End Module
Unfortunately, when I run this Console application I get incorrect Hex values. For example, "Part1" is correctly converted by Windows Calculator to 40 40 DD E2, whereas this console application converts it to BC 81 80 4E. The MS documentation states that GetBytes method returns a byte array of the given value, so does anyone see what I'm missing here?
Re: Conversion from 2-part 64bit Floating Point to Fixed Point
Alright, more progress but still not quite there. If the above p1 = 1077992931d is used and I implement the code
Additional Lines Code:
Dim samp1 As String = Hex(p1)
Console.WriteLine("Part1 converted to Hex directly is {0}", samp1)
at the bottom of my Main Sub, then I get an output of 40 40 DE 00h. This converts back to 1077992960d, but I would've expected to see 40 40 DD E3h as the output.
Given the decimal values I'm seeing, this looks less like rounding and more like a different conversion method. It needs to be exactly accurate, though, since I'm combining these representations to make that one 64-bit number.
Re: Conversion from 2-part 64bit Floating Point to Fixed Point
So now I've gotten the pieces to convert correctly but the whole Hex value does not convert back to decimal properly. See code below.
Code:
Imports Microsoft.Win32
Imports System.Runtime.InteropServices
Module Module1
<StructLayout(LayoutKind.Explicit)> _
Public Structure DataUnion
<FieldOffset(0)> Public FixedPointValue As Single
<FieldOffset(0)> Public LongValue As Long
<FieldOffset(0)> Public ByteValue As Byte
End Structure
Sub Main()
Dim p1 As Int32, p2 As Int32
Dim wholeByte2(7) As Byte
Dim su As DataUnion
p1 = 1077992931 'properly converts to 40 40 DD E3h
p2 = 831725571 'properly converts to 31 93 20 03h
Dim samp1 As String = Hex(p1), samp2 As String = Hex(p2), wholeHexStr As String = samp1 & samp2
Console.WriteLine("Part1 converted to Hex directly is {0}. Part 2 is {1}.", samp1, samp2)
Console.WriteLine("Whole is {0}.", wholeHexStr)
Dim j As Integer = 0
For i = 0 To wholeHexStr.Length - 2
wholeByte2(j) = Byte.Parse(wholeHexStr.Substring(i, 2), Globalization.NumberStyles.HexNumber)
i += 1
j += 1
Next
Console.WriteLine("Whole with splits as usable value is {0}.", BitConverter.ToString(wholeByte2))
Console.WriteLine(vbCrLf)
Dim ConLong As Long = BitConverter.ToInt64(wholeByte2, 0) 'should convert to 4629944384795910146d
'actually converts to 225341823854133312d
su.LongValue = ConLong
Console.WriteLine("Complete Long in structure is {0}", su.LongValue)
Console.WriteLine("Converted value is {0}", su.FixedPointValue)
End Sub
End Module
Anyone know why my Byte-to-Long conversion isn't working as expected?
Re: Conversion from 2-part 64bit Floating Point to Fixed Point
Ended up resolving this myself. Looks like the Hex() method was converting in Big Endian format, whereas the number structure was converting in Little Endian format. The proper conversion is shown below.
Code:
Imports Microsoft.Win32
Imports System.Runtime.InteropServices
Module Module1
<StructLayout(LayoutKind.Explicit)> _
Public Structure DataUnion
<FieldOffset(0)> Public FixedPointValue As Double
<FieldOffset(0)> Public LongValue As Long
<FieldOffset(0)> Public ByteValue As Byte
End Structure
Sub Main()
Dim p1 As Int32, p2 As Int32
Dim wholeByte(7) As Byte
Dim su As DataUnion
Dim TempValue As Double, FinalValue As Single
p1 = 1077992931 'properly converts to 40 40 DD E3h
p2 = 831725571 'properly converts to 31 93 20 03h
Dim samp1 As String = Hex(p1), samp2 As String = Hex(p2), wholeHexStr As String = samp1 & samp2
Console.WriteLine("Part1 converted to Hex directly is {0}. Part 2 is {1}.", samp1, samp2)
Console.WriteLine("Whole is {0}.", wholeHexStr)
Dim j As Integer = 0
For i = 0 To wholeHexStr.Length - 2
wholeByte(7 - j) = Byte.Parse(wholeHexStr.Substring(i, 2), Globalization.NumberStyles.HexNumber)
i += 1
j += 1
Next
Console.WriteLine("Whole with splits as system-usable value is {0}.", BitConverter.ToString(wholeByte))
Console.WriteLine(vbCrLf)
Dim ConLong As Long = (BitConverter.ToInt64(wholeByte, 0)) 'properly converts to 4629944384795910147d
su.LongValue = ConLong
Console.WriteLine("Complete Long in structure is {0}", su.LongValue) '4629944384795910147d
Console.WriteLine("Which happens to be {0} in Hex", Hex(su.LongValue)) '40 40 DD E3 31 93 20 03h
TempValue = su.FixedPointValue
Console.WriteLine("Converted value is {0}", TempValue)
FinalValue = Convert.ToSingle(TempValue)
Console.WriteLine("Final value is {0}", FinalValue)
End Sub
End Module