Solved

# 8-byte hex IEEE-754 to VB6 single-precision float needed asap!

Posted on 2005-04-16
6,825 Views
Before you think you have the correct solution, please test it with this webpage first:
http://babbage.cs.qc.edu/courses/cs341/IEEE-754hex32.html

And, test your routine with this hex value: 4287A929 which SHOULD be converted to this decimal value: 67.830391 but my routine, and all those I've seen on the web, get 125.2863 instead.

I _have_ seen some of the other routines available, and they all do NOT work with this particular value. Why not? The one by _pit_ here on experts-exchange only accepts 4-byte hex values, and I'm not sure how to "fix" it to accept 8-byte hex values, so I don't even know if his works properly either.

Here is the code I am using. Can anyone please tell me what the bug is...why won't it convert the above hex value to the above correct decimal value? This is not my code, it is the code that has been floating (pardon the pun) around the web for years:

'
' function is getting passed an 8-byte IEEE hex value such as 4287A929
' and is supposed to return a value of 67.83039 but doesn't
'
Public Function SingleConv(whole As Variant) As Variant
Dim expnt, mantis, Psign As Boolean, result, multiplier, L, q, que
L = Len(whole)
If L < 8 Then
whole = whole & String(8 - L, &H30) '"0"
End If
If L > 8 Then
whole = Left(whole, 8)
End If
expnt = "&H" & Left(whole, 3)
If expnt And &H800 Then Psign = True Else Psign = False
expnt = (Val(expnt) And &H7F8) / 8 'shift right 3 places
expnt = "&H" & Hex(expnt)
mantis = "&H" & Right(whole, 6)
mantis = (Val(mantis) And &H7FFFFF) * 2  'convert to numerical and switch top bit off, shift left 1 position
mantis = Hex(mantis)             'stick back into var as hex
expnt = Val(expnt)
'translate expnt
expnt = expnt - &H7F
expnt = 2 ^ expnt
'translate mantis
result = 1
For q = 1 To 6
que = "&H" & Mid(mantis, q, 1)
que = Val(que)
multiplier = 1 / 2 ^ (q * 4)
result = result + (multiplier * que)
Next q
result = result * expnt
If Psign Then result = 0 - result
SingleConv = str(result)
End Function
0
Question by:mushu999

LVL 32

Accepted Solution

A Single data type is only 4 bytes.
The hex data 4287A929 is also 4 bytes.

Converting a 4 byte hex to a Single should be easy:

Option Explicit

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)

Private Sub Command1_Click()

Dim sngVal As Single
Dim sHex As String
Dim lVal As Long

'Our Hex String
sHex = "4287A929"

'The value of the hex string
lVal = Val("&h" & sHex)

'Copy the bytes over to a Single data type
Call CopyMemory(sngVal, lVal, 4)

'Display the results
MsgBox Format(sngVal, "0.000000")

End Sub
0

LVL 32

Expert Comment

The 8 byte representation of 67.830391 is:
4050F525204AF923

And can be tested here:
http://babbage.cs.qc.edu/courses/cs341/IEEE-754hex64.html
0

LVL 4

Assisted Solution

Add one commandbutton named Command1 and one textbox named Text1
Add the following code and type hexvalue in text1 and click command1

Private Function HextoIEEE754(HexValue As String) As Double
Dim i As Integer
Dim binvalue  As String
Dim Sign As Integer
Dim Exponent As Double
Dim Mantissa As Double
binvalue = ""
For i = 1 To Len(HexValue)
Select Case Mid\$(HexValue, i, 1)
Case "0"
binvalue = binvalue & "0000"
Case "1"
binvalue = binvalue & "0001"
Case "2"
binvalue = binvalue & "0010"
Case "3"
binvalue = binvalue & "0011"
Case "4"
binvalue = binvalue & "0100"
Case "5"
binvalue = binvalue & "0101"
Case "6"
binvalue = binvalue & "0110"
Case "7"
binvalue = binvalue & "0111"
Case "8"
binvalue = binvalue & "1000"
Case "9"
binvalue = binvalue & "1001"
Case "A"
binvalue = binvalue & "1010"
Case "B"
binvalue = binvalue & "1011"
Case "C"
binvalue = binvalue & "1100"
Case "D"
binvalue = binvalue & "1101"
Case "E"
binvalue = binvalue & "1110"
Case "F"
binvalue = binvalue & "1111"
End Select
Next i
If Mid(binvalue, 1, 1) = 0 Then Sign = 1 Else Sign = -1
Exponent = GetExponent(Mid(binvalue, 2, 8))
Mantissa = GetMantissa(Mid(binvalue, 10, 23))
HextoIEEE754 = Sign * 2 ^ Exponent * Mantissa
End Function
Function GetExponent(binval As String) As Long
Dim n As Integer
n = Len(binval) - 1
a = n
Do While n > -1
x = Mid(binval, ((a + 1) - n), 1)
GetExponent = IIf((x = "1"), GetExponent + (2 ^ (n)), GetExponent)
n = n - 1
Loop
GetExponent = GetExponent - 127
End Function
Private Function GetMantissa(binval As String) As Double
Dim i As Integer
Dim z As Double
Dim result As Double
Dim b As Integer
result = 0
For i = 1 To 23
b = Int(Val(Mid\$(binval, i, 1)))
z = b * (1 / (2 ^ i))
result = result + z
Next i
GetMantissa = 1 + result
End Function
Private Sub Command1_Click()
MsgBox HextoIEEE754(Text1.Text)
End Sub
0

LVL 1

Author Comment

Erick37, you are looking at the 64-bit answer. The 32-bit answer comes out correctly on that same web page. I will try your answer shortly. If it works correctly in all cases then you'll have earned the points. If not, then I hope others keep trying. I've wasted so much time on this stupid problem my head hurts!  :)
0

LVL 4

Expert Comment

Tested Erick's and it works fine actually great.  If you are going to use this program to convert a large number hexadecimals to IEEE754 (such as PLC values - Double Floating) then use his for sure.  If you need the exponent, sign, and mantissa use mine.  I originally wrote this code for PLC (AB, GE).  I will now use Ericks for PLCs because I don't need the exponent, sign, or mantissa.
0

LVL 1

Author Comment

Because it uses a memory move, I'm wondering how it actually works. It scares me to not understand the method being used, since it might break on an untested value later. This is being used in a program that must run 24x7 without crashing or errors...so far it has been doing awesome, but we just discovered that the algorithm I used for IEEE conversion has..ummm...flaws.....

Also, I retract my previous comment to Erick. I reread what he said, and I responded incorrectly. Indeed, I was not thinking straight regarding the 4-byte and 8-byte hex value sizes, since 8 bytes IS 64 bits! Good grief, I'm exhausted...so, forgive me Erick for my silly-sounding reply above.

I'd still like you to briefly explain why your routine works at all, and how it does the work. Seems like it depends on the CPU or perhaps DMA controller itself to "convert" the values in memory. Not sure if this is something to rely on--what if the user gets a new computer that doesn't exhibit the same hardware characteristics?
0

LVL 1

Author Comment

I found the skinny on this CopyMemory thingy, from this URL, FWIW: http://vb.mvps.org/hardcore/html/bringyourhatchet.htm

CopyMemory: A Strange and Terrible Saga

Here’s the long, strange story of how the Win32 function for copying raw memory came to be called CopyMemory, even though there’s no such function in Visual Basic or in the Windows API.

It started when I first began searching for the Win32 equivalent of the Win16 hmemcpy function for use in Visual Basic version 4. No such thing—not even a note that the function might be obsolete. But…

The closest I could come up with was the CopyMemory function, which has exactly the same arguments and is documented the same as the old hmemcpy. Unfortunately, despite what you might read in Win32 documentation, there is no such thing as CopyMemory. You can search all the 32-bit DLLs with the DumpBin utility, but you won’t find any DLL containing CopyMemory. But…

If you search carefully through the Win32 C include files, you’ll turn up the following in WINBASE.H:

#define CopyMemory RtlCopyMemory
#define MoveMemory RtlMoveMemory
#define ZeroMemory RtlZeroMemory

This C equivalent of an alias indicates that CopyMemory is another name for a function called RtlCopyMemory. Don’t ask why; just check for RtlCopyMemory in KERNEL32.DLL. Again, nothing. More sleuthing in the Win32 include files reveals the reason. WINNT.H contains something like this:

#define RtlCopyMemory(dst, src, len) memcpy(dst, src, len)

In other words, RtlCopyMemory is an alias for the C memcpy function, but you can’t use memcpy or any other C library function from Basic. The documenta-tion is simply lying when it claims that CopyMemory is a Windows function rather than a C function. If it’s not exported from a DLL, you can’t call it. But… KERNEL32.DLL does contain an entry for RtlMoveMemory. If you check the Win32 documentation, you’ll see that MoveMemory does the same thing as CopyMemory except that it handles overlapped memory in a different fashion. I can’t imagine a situation in which a Basic programmer would be copying overlapped memory. No reason not to use MoveMemory instead. The name CopyMemory seemed more intelligible than hmemcpy or MoveMemory, so I used this alias for both 16-bit and 32-bit versions:

#If Win32 Then
Declare Sub CopyMemory Lib "KERNEL32" Alias "RtlMoveMemory" ( _
lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)
#Else
Declare Sub CopyMemory Lib "KERNEL" Alias "hmemcpy" ( _
lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)
#End If

The Windows API type library has an equivalent (or almost equivalent) CopyMemory function.

That explains why I used CopyMemory, but why does everybody else use it? Because I sent a copy of my sidebar to an internal alias at Microsoft, and someone who read it decided it would make a good Knowledge Base article. I agreed to let them use it if they mentioned it was an excerpt from my book. Good advertising, I thought. Ever since then I’ve read articles and heard speakers at the VBITS conference talking about CopyMemory as if it really existed. And none of them mention my book as the source. So don’t be fooled by false advertising. If they talk about RtlMoveMemory, they figured it out on their own. If they talk about CopyMemory, they got it (perhaps without knowing) from me.
.
.
.
0

LVL 32

Expert Comment

The CopyMemory method works because a VB Single data type uses the IEEE-754 method.  So all we need to do is find a way to set the value of the bytes directly in our Single variable.

First thing to do is to convert the string representation of the Hex value in to memory.  Easily done with the Val() function and assigning the result to a Long data type (4 bytes).

Now that we have the 4 bytes in memory, use CopyMemory() to copy the same bytes into a variable declared as Single.

Another use of CopyMemory can be found here (with a better explanation than mine) :)

"How To Retrieve Individual Bytes from a Multi-Byte Type in VB"
http://support.microsoft.com/default.aspx?scid=kb;en-us;171652
0

LVL 4

Expert Comment

Try mine, it is currently used to convert PLC data 24 hours a day seven days a week.  It converts approximately 400 values every 60 ms and works fine.  Did not think about the CopyMemory issues.
0

LVL 1

Author Comment

I was in a bind to get the fix out the door last night, so after spending much time testing Erick's tiny solution I implemented it in my app. However, I can see a potential need to have the split-apart numeric fields that rd's solution provides in the future. Thus I split the answer points...hope neither of you are offended!
0

## Featured Post

If you have ever used Microsoft Word then you know that it has a good spell checker and it may have occurred to you that the ability to check spelling might be a nice piece of functionality to add to certain applications of yours. Well the code that…
When designing a form there are several BorderStyles to choose from, all of which can be classified as either 'Fixed' or 'Sizable' and I'd guess that 'Fixed Single' or one of the other fixed types is the most popular choice. I assume it's the most p…
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…
Show developers how to use a criteria form to limit the data that appears on an Access report. It is a common requirement that users can specify the criteria for a report at runtime. The easiest way to accomplish this is using a criteria form that a…