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

Before you think you have the correct solution, please test it with this webpage first:

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
Who is Participating?
Erick37Connect With a Mentor Commented:
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
The 8 byte representation of 67.830391 is:

And can be tested here:
rdwillettConnect With a Mentor Commented:
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
     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
Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

mushu999Author Commented:
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!  :)
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.  
mushu999Author Commented:
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?
mushu999Author Commented:
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)
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.
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"
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.
mushu999Author Commented:
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!
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.