PDA

View Full Version : Convert Byte Array Containing Floating Point To Single



Pages : [1] 2

nomel
September 6th, 2007, 09:03
Hello,

I have a byte array that contains a 4 byte floating point number. How would I convert this byte array to the single typed floating point number it contains?

Thanks for any help!

Andy Pope
September 6th, 2007, 16:46
Can you post example code and values, both input and expected output.

RichardSchollar
September 6th, 2007, 18:07
Hello,

I have a byte array that contains a 4 byte floating point number. How would I convert this byte array to the single typed floating point number it contains?

Thanks for any help!

Hello Nomel

The following code demonstrates a way to use intermediate strings to convert between byte arrays and the outputted Single value:


Sub test()
Dim b() As Byte, sng As Single, s As String, i As Long
b = StrConv("1.45", vbFromUnicode) 'fill byte array with string
'representation of floating point number
'byte array will contain ANSI values for string components
For i = LBound(b) To UBound(b)
Debug.Print b(i); 'output individual bytes
Next i
s = StrConv(b, vbUnicode) 'assign byte array back to string using conversion function
sng = CSng(s) 'convert string to Single
Debug.Print vbLf & sng 'output Single value
End Sub

Richard

nomel
September 7th, 2007, 02:06
this is not the ascii representation, it's a binary representation of the floating point number.

the byte array will contain the 32 bit representation of a floating point number, so a total of four elements. the output will be a single. so something like

function bytetosingle(bytearray() as byte) as single

in vb.net, this function exists. It is BitConverter.ToSingle(bytearray() as byte, offset).

in delphi or c, I would use a memory pointer, but I'm not sure how to do it in vb. any ideas, or do I have to get all low level (and very slow)?

RichardSchollar
September 7th, 2007, 03:10
What do each of the bytes in the byte array represent? Could you provide an example. Undoubtedly a UDF could be written to do this.

Richard

nomel
September 7th, 2007, 06:24
it's a standard IEEE 32 bit floating point number...the same type used in excel for single. the data in the byte array is the data from memory for the number...how the single is actually stored in the computers memory as ones and zeros. not sure how giving an example will help cause it's an arbitrary bit pattern depending on the number....but here you go:



dim data(3) as byte
dim number as single

'i usually get these values from a piece of equipment...
data(0) = 23
data(1) = 59
data(2) = 120
data(3) = 12
'so the whole binary representation for the number is '0001'0111'0011'1011'0111'1000'0000'1100'...or something close...order of significance could be off.

'now data contains my 4 byte floating point number. who knows what the
'number actually...thus the reason for my desire to convert it to a number i can understand.

'to convert it to single.....
number = somefunction(data) 'this function converts it to a single.


so, anyone know what somefunction is? i'm probably the only person that's ever needed to do this in excel :P I have a feeling I'll have to study the IEEE standard and make a function myself.

mikerickson
September 7th, 2007, 07:53
From your example, the array {23,59,120,12} representes a number. What number?

If you do look up the IEEE standard, could you please post it, since we are (apparently) ignorant of what it is? Thanks.

nomel
September 7th, 2007, 09:08
the IEEE 754 standard (wikipedia link (http://en.wikipedia.org/wiki/IEEE_floating-point_standard)) is the standard that describes how floating point numbers are stored in a computers memory. having a standard allows programs to read numbers sent by other programs or computers.

each variable is stored in the computers memory as a binary number.
for ascii strings, this is something like
[integer number of chars][byte][byte][byte][byte][byte]...etc. where each byte represents the acsii code for the letter (see asc and chr functions).

for numbers, storing as a string wouldn't make any sense, you would be using a whole byte (a number between 0 and 255) to store a value that is only between 1 and 10! So instead, the number is stored in binary, base 2 rather than base 10. for integers, this is fairly direct since any integer can be converted to a binary number. 255d = 11111111b, 56d = 111000b, etc. But what happens if you have a fraction of a number, or a fraction of a number plus an integer (like 3 + .14159 = 3.14159)?

That's where floating point comes in. It allows you to take an integer number (24 bit number for single), then *move* the decimal place to nearly wherever you want it...a *floating* decimal place...a floating point number! The standard describes how to store the information for where the decimal place goes, etc. Slightly simplified, it is in the following format:

[sign bit (+/-)][8 bit decimal place position][23 bit integer]

in this simplified example, for the number 5.12342134, the 24 bit integer (the first bit is always 1, and is hidden) would contain 512342134, and the decimal place position byte would say to put the decimal place after the 5. the sign bit would be 0 for positive. this gives the 32 bits total is how single type numbers are stored in memory.

the details can be googled. :-)

i'll post the function i'm making to convert that byte array in a bit ;-)

mikerickson
September 7th, 2007, 09:36
It's not clear how the postition of the decimal point is coded. One example of "This array means this number" would help enormously.
This will break the array into the three parts mentioned.

Dim byteRRay(1 To 4) As Byte
Dim integerPart As Integer
Dim sign As Integer
Dim decimalPostion As Integer

integerPart = 512 * (127 And byteRRay(2)) + 256 * byteRRay(3) + byteRRay(4)

sign = 2 * Sgn(byteRRay(1) And 128) - 1: Rem +/- 1

decimalPostion = 2 * (byteRRay(1) And 127) + ((byteRRay(2) And 128) / 128)

nomel
September 7th, 2007, 09:49
like i said...the details can be googled...or you could follow the link i provided at the beginning.

mikerickson
September 7th, 2007, 10:03
From wikipedia, v = s 2e m, so


Function Bytes2Double (byteRRay() As Byte) as Double
Dim integerPart As Integer
Dim sign As Integer
Dim decimalPostion As Integer

integerPart = 512 * (127 And byteRRay(2)) + 256 * byteRRay(3) + byteRRay(4)

sign = 2 * Sgn(byteRRay(1) And 128) - 1: Rem +/- 1

decimalPostion = 2 * (byteRRay(1) And 127) + ((byteRRay(2) And 128) / 128)

Bytes2Double = sign * 2^(decimalPostion-127) * integerPart

End Function
Your byte array may be indexed 0-3, rather than the 1-4 above and it might be ordered backwards from my assumption.

Dave Hawley
September 7th, 2007, 10:09
Nomel, you have a few people help you, particually Mike. Yet I do not see a single Thanks.

nomel
September 7th, 2007, 10:49
I haven't said thanks yet because I'm still at work and have been very busy.

Thanks for the interest and help mike. The simplified description I gave was simplified to just help explain what the standard defined, not for implementation. There are other details (in the link I provided) like normalization, and the significant must be converted to a binary fraction before that equation can be used. Those pesky little details ;-) And that example I made up ends up being around 6.06x10^-25. So small :-(

Here is the finished function. I've tested it against the examples on the wikipedia page I linked to:



Function ByteArrayToSingle(data() As Byte, offset) As Single
'converts a byte array of an ieee 754 single type floating point numbers to a single.
'data byte array contains the 32 bit single numbers, starting at offset
'Description and details: http://en.wikipedia.org/wiki/IEEE_754#Single-precision_32_bit
'NOTE! Byte order could be different on strange systems/equipment (like a scope)!
Dim n As Single 'this is the resulting single number
Dim frac As Double 'fractional binary number from significant
Dim e As Integer 'exponent
Dim sig As Long 'significant integer
Dim i As Integer 'for for loops and whatnot

'get exponent
e = (data(offset) And 127) * 2 '127 = 0111111 'isolate first 7 bits, then shift left one.
e = e Or Int((data(offset + 1) And 128) / (2 ^ 7)) 'lsb is in next byte...isolate and put in e.
e = e - 127 'e is biased by 127 so it can stay unsigned (always positive)

sig = 0
sig = data(offset + 1) * 2 ^ 16 'shift left 16.
sig = sig Or (data(offset + 2) * 2 ^ 8) 'shift over 8 and put second byte in sig
sig = sig Or (data(offset + 3)) 'put third byte in sig.

frac = 1 'start with 1 so result is 1.<fraction>
If e = -127 Then 'denormalized (0.fraction rather than 1.fraction)
e = -126
frac = 0 'start with 0 so result is 0.<fraction>.
End If

'calculate binary fraction from significant.
For i = 22 To 0 Step -1 'step through all 23 bits.
If (sig And 2 ^ i) > 0 Then frac = frac + 2 ^ -(23 - i) 'if the bit is a 1, the number will be non zero.
Next

n = (2 ^ e) * frac 'calculate the final number
If (data(offset) And 128) > 0 Then n = -n 'if sign bit is set, make negative.

ByteArrayToSingle = n 'return result

End Function


What a terribly inefficient and backwards way to get a number that's already in memory back into memory! :roll:

Dave Hawley
September 7th, 2007, 10:50
I haven't said thanks yet because I'm still at work and have been very busy. But you replied many times.

nomel
September 7th, 2007, 11:00
Yes, while running to get coffee between tests in the lab. :-) Still my mistake, I'll try to be more courteous.

Dave Hawley
September 7th, 2007, 11:02
Thank you!

shg
September 7th, 2007, 12:28
Here's a version that covers the odd cases:

Function Byte2Sng(ab() As Byte, Optional bBigEndian As Boolean = True) As Variant
Const iBias As Long = 127 ' exponent bias
Const dDen As Double = 2 ^ 23 ' denominator of fraction

Dim n As Integer

Dim b3 As Long, b2 As Long, b1 As Long, b0 As Long ' MSB through LSB
Dim dSgn As Double ' sign
Dim dVal As Double

Dim iFra As Long ' fraction
Dim iExp As Long ' exponent

n = LBound(ab)
If bBigEndian Then
b3 = ab(n): b2 = ab(n + 1): b1 = ab(n + 2): b0 = ab(n + 3)
Else
b0 = ab(n): b1 = ab(n + 1): b2 = ab(n + 2): b3 = ab(n + 3)
End If

dSgn = IIf(b3 And &H80, -1, 1) ' MSb of MSB
iExp = 2 * (b3 And &H7F) + (b2 And &H80) / &H80 ' next 8 bits
iFra = &H10000 * (b2 And &H7F) + &H100 * b1 + b0 ' last 23 bits

Select Case iExp
Case &HFF
Select Case iFra
Case Is >= &H400000
Byte2Sng = "QNaN" ' Quiet Not a Number
Exit Function '-------------------------------------------->
Case 0
' positive and negative infinities
Byte2Sng = IIf(dSgn = 1, "+Inf", "-Inf")
Exit Function '-------------------------------------------->
Case Else
Byte2Sng = "SNaN" ' Signaling Not a Number
Exit Function '-------------------------------------------->
End Select
Case 0
' 0 or denormalized
iExp = 1
Case Else
' add the implied 1
iFra = iFra Or &H800000
End Select
dVal = dSgn * iFra / dDen * 2# ^ (iExp - iBias)
Byte2Sng = CSng(dVal)
End Function

nomel
September 8th, 2007, 02:32
ohhh...very nice shg!! I didn't realize you could convert the fraction that way.

One problem though, you have to change the exponent to -126 if it's denormalized.

The denormalized hex value 00400000h should be come out to be about 5.910^-39, but with your function, it is about 2.9X10^-39.

Besides that, I'm using yours! :D

shg
September 8th, 2007, 02:43
Good catch, thank you. Code in prior post edited for anyone else interested.

shg
September 10th, 2007, 02:52
There had to be an easier way. The LSet statement copies a variable of one user-defined type to another user-defined type (Far be it from VBA to make it simple ...)


Type ub4 ' Little-Endian Single storage order
b0 As Byte: b1 As Byte: b2 As Byte: b3 As Byte
End Type

Type Float
r As Single
End Type

Function Byte2Sng(ab() As Byte) As Single
' Converts the 4-byte, 0-based, Big-Endian array in ab to a Single
' SNaN will cause overflow (as it should)
Dim ab1 As ub4, fltR As Float ' our two user-defined types

' put the bytes in Little-Endian order
ab1.b3 = ab(0): ab1.b2 = ab(1): ab1.b1 = ab(2): ab1.b0 = ab(3)

' point the Float at the bytes
LSet fltR = ab1

' done!
Byte2Sng = fltR.r
End Function
Test a couple of numbers:

Sub test()
Dim ab(1 To 4) As Byte

' test: 0.15625
ab(0) = &H3E: ab(1) = &H20: ab(2) = &H0: ab(3) = &H0
Debug.Print "num test: " & Byte2Sng(ab)

' test: -118.625
ab(0) = &HC2: ab(1) = &HED: ab(2) = &H40: ab(3) = &H0
Debug.Print "num test: " & Byte2Sng(ab)
End Sub