Lycaon
asked on
Request for binary <-> Long info (2's complement?)
I've been trying to find a decimal to binary and binary to decimal function that works correctly with negative numbers, but so far, none have held up to the challenge. I want this to work in Visual Basic, but the VB code will eventually be ported to ASP, so I would rather avoid using external components.
For example, I am reading a Long value from a file. This value is negative, eg, -1487631. When I attempt to convert this to binary using the following function, I of course get a 0 since the function only does positive numbers.
If I try to read the Long as 4 bytes, then convert each byte into an 8 bit binary string, and concatate the 4 strings together, then try to convert the binary string back to a long, I get a number like 1487630.
My question is, how exactly are 4 byte negative longs (or 2 byte negative integers, etc) stored in either files or memory? What's the best way to correctly convert from a negative or positive number of an arbitrary byte length (anything from 1 byte to 32 bytes, even odd ones such as 5, 17, etc)?
Here are the functions I'm currently using.
Public Function Dec2Bin(ByVal Num As Variant) As String
Dim x As Integer
For x = 100 To 0 Step -1 ' I don't expect to ever use a number bigger than (2 ^ 100)
If Num >= (2 ^ x) Then
' Stop
Dec2Bin = Dec2Bin & "1"
Num = Num - (2 ^ x)
Else
Dec2Bin = Dec2Bin & "0"
End If
Next x
Do While Left(Dec2Bin, 1) = "0"
Dec2Bin = Mid(Dec2Bin, 2) ' Remove all extra 0's from the front.
Loop
Do Until Len(Dec2Bin) Mod 8 = 0 And Len(Dec2Bin) <> 0 ' Make sure there are 8 'bits' returned
Dec2Bin = "0" & Dec2Bin
Loop
End Function
Public Function Bin2Dec(ByVal Bin As String) As Variant
Dim x As Integer
For x = 1 To Len(Bin)
Bin2Dec = Bin2Dec + Int(Mid(StrReverse(Bin), x, 1) * (2 ^ (x - 1)))
Next x
End Function
Current points set at 250, but I'm willing to increase it for a prompt response, or information that goes above and beyond what I've asked for.
James
For example, I am reading a Long value from a file. This value is negative, eg, -1487631. When I attempt to convert this to binary using the following function, I of course get a 0 since the function only does positive numbers.
If I try to read the Long as 4 bytes, then convert each byte into an 8 bit binary string, and concatate the 4 strings together, then try to convert the binary string back to a long, I get a number like 1487630.
My question is, how exactly are 4 byte negative longs (or 2 byte negative integers, etc) stored in either files or memory? What's the best way to correctly convert from a negative or positive number of an arbitrary byte length (anything from 1 byte to 32 bytes, even odd ones such as 5, 17, etc)?
Here are the functions I'm currently using.
Public Function Dec2Bin(ByVal Num As Variant) As String
Dim x As Integer
For x = 100 To 0 Step -1 ' I don't expect to ever use a number bigger than (2 ^ 100)
If Num >= (2 ^ x) Then
' Stop
Dec2Bin = Dec2Bin & "1"
Num = Num - (2 ^ x)
Else
Dec2Bin = Dec2Bin & "0"
End If
Next x
Do While Left(Dec2Bin, 1) = "0"
Dec2Bin = Mid(Dec2Bin, 2) ' Remove all extra 0's from the front.
Loop
Do Until Len(Dec2Bin) Mod 8 = 0 And Len(Dec2Bin) <> 0 ' Make sure there are 8 'bits' returned
Dec2Bin = "0" & Dec2Bin
Loop
End Function
Public Function Bin2Dec(ByVal Bin As String) As Variant
Dim x As Integer
For x = 1 To Len(Bin)
Bin2Dec = Bin2Dec + Int(Mid(StrReverse(Bin), x, 1) * (2 ^ (x - 1)))
Next x
End Function
Current points set at 250, but I'm willing to increase it for a prompt response, or information that goes above and beyond what I've asked for.
James
Okay, everything I told you is incorrect. It's not quite as simple as I made it out to be. Darn me and my attempts to do unsigned operations in VB....
Zaphod.
Zaphod.
ASKER
Heh *grin*.
How so?
How so?
ASKER
*dangles another 250 points in front of everyone*
Hi,
Fewf, that turned out to be a lot harder than I thought it would be. Be aware that this uses the internal representation of longs to do its work, which is done in little endian, so some of the loops might seem backwards, but they aren't really. This will work with bytes, integers and longs normally for Bin2Dec, and will also work for Dec2Bin, but it will always return a 32 bit value, you can just truncate the first 2 or 3 bytes for integers and bytes respectively since they will all be either 1 or 0 anyway and are not needed.
Hope this does the trick.
Zaphod.
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Function Dec2Bin(ByVal Value As Long) As String
Dim strRetVal As String
Dim arrBytes(0 To 3) As Byte
Dim i As Integer
Dim j As Integer
CopyMemory arrBytes(0), Value, 4
For i = 3 To 0 Step -1
For j = 7 To 0 Step -1
strRetVal = strRetVal & (arrBytes(i) And (2 ^ j)) / (2 ^ j)
Next
Next
Dec2Bin = strRetVal
End Function
Private Function Bin2Dec(ByVal Value As String) As Long
Dim lngRetVal As Long
Dim arrBytes(0 To 3) As Byte
Dim strSign As String
Dim i As Integer
Dim j As Integer
Dim k As Integer
' Pad the string with the sign bit
Value = String(32 - Len(Value), Left(Value, 1)) & Value
k = 1
For i = 3 To 0 Step -1
For j = 7 To 0 Step -1
If Mid(Value, k, 1) = "1" Then
arrBytes(i) = arrBytes(i) Or (2 ^ (j))
End If
k = k + 1
Next
Next
CopyMemory lngRetVal, arrBytes(0), 4
Bin2Dec = lngRetVal
End Function
Fewf, that turned out to be a lot harder than I thought it would be. Be aware that this uses the internal representation of longs to do its work, which is done in little endian, so some of the loops might seem backwards, but they aren't really. This will work with bytes, integers and longs normally for Bin2Dec, and will also work for Dec2Bin, but it will always return a 32 bit value, you can just truncate the first 2 or 3 bytes for integers and bytes respectively since they will all be either 1 or 0 anyway and are not needed.
Hope this does the trick.
Zaphod.
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Function Dec2Bin(ByVal Value As Long) As String
Dim strRetVal As String
Dim arrBytes(0 To 3) As Byte
Dim i As Integer
Dim j As Integer
CopyMemory arrBytes(0), Value, 4
For i = 3 To 0 Step -1
For j = 7 To 0 Step -1
strRetVal = strRetVal & (arrBytes(i) And (2 ^ j)) / (2 ^ j)
Next
Next
Dec2Bin = strRetVal
End Function
Private Function Bin2Dec(ByVal Value As String) As Long
Dim lngRetVal As Long
Dim arrBytes(0 To 3) As Byte
Dim strSign As String
Dim i As Integer
Dim j As Integer
Dim k As Integer
' Pad the string with the sign bit
Value = String(32 - Len(Value), Left(Value, 1)) & Value
k = 1
For i = 3 To 0 Step -1
For j = 7 To 0 Step -1
If Mid(Value, k, 1) = "1" Then
arrBytes(i) = arrBytes(i) Or (2 ^ (j))
End If
k = k + 1
Next
Next
CopyMemory lngRetVal, arrBytes(0), 4
Bin2Dec = lngRetVal
End Function
ASKER
Dude, that's magic. (Sorry, just watched Austin Powers: Goldmember, heh)
One question though. Is there any way around the CopyMemory call? As it mentions in the first or second paragraph I hope to port this to ASP, and you can't do API in ASP :\
James
One question though. Is there any way around the CopyMemory call? As it mentions in the first or second paragraph I hope to port this to ASP, and you can't do API in ASP :\
James
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Excellent. Here you go. Thanks :)
James
James
Public Function Bin2Dec(Bin as string) as Long
Dim x as integer
for x = 1 to len(bin)
Bin2Dec = Bin2Dec OR Int(Mid(StrReverse(Bin), x, 1) * (2 ^ (x - 1)))
next
End Function
Public Function Dec2Bin(Dec as Long) as String
Dim x as integer
for x = 1 to 32
Dec2Bin = iif(mid(dec,x,1) = "1",1,0)*(2^(32-x))
next
End Function
That's all freehand, so there might be some glitches, but hopefully you get the idea.
Zaphod.