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

Posted on 2005-04-16
Last Modified: 2011-10-03
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
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
    LVL 32

    Expert Comment

    The 8 byte representation of 67.830391 is:

    And can be tested here:
    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
         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
    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!  :)
    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.  
    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 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, 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?
    LVL 1

    Author Comment

    I found the skinny on this CopyMemory thingy, from this URL, FWIW:

    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.
    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";en-us;171652
    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.
    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!

    Featured Post

    How your wiki can always stay up-to-date

    Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
    - Increase transparency
    - Onboard new hires faster
    - Access from mobile/offline

    Join & Write a Comment

    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…

    754 members asked questions and received personalized solutions in the past 7 days.

    Join the community of 500,000 technology professionals and ask your questions.

    Join & Ask a Question

    Need Help in Real-Time?

    Connect with top rated Experts

    21 Experts available now in Live!

    Get 1:1 Help Now