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

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.9×10^-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