Link to home
Start Free TrialLog in
Avatar of mushu999
mushu999

asked on

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:
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
ASKER CERTIFIED SOLUTION
Avatar of Erick37
Erick37
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
The 8 byte representation of 67.830391 is:
4050F525204AF923

And can be tested here:
http://babbage.cs.qc.edu/courses/cs341/IEEE-754hex64.html
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of mushu999
mushu999

ASKER

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.  
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?
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.
.
.
.
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
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.
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!