Results 1 to 19 of 19

Thread: Bits'n'Bytes

  1. #1
    Guest
    Hi!

    I'm working on some conversion stuff, and would like to know why this isn't working with negative values. The following 2 Functions are from microsoft themselves and are functional & tested (by me...):

    Code:
    Public Function HiByte(ByVal wParam As Integer) As Byte
        HiByte = wParam \ &H100 And &HFF&
    End Function
    
    Public Function LoByte(ByVal wParam As Integer) As Byte
        LoByte = wParam And &HFF&
    End Function
    So I tried to put them back together, but keep getting overflows with negative values...
    Code:
    Public Function Bytes2Word(ByVal bHiByte As Byte, ByVal bLoByte As Byte) As Integer
        Bytes2Word = (bHiByte * &H100) Or (bLoByte And &HFF&)
    End Function
    Does anyone know jow to correctly do this? (Please test this/your code before replying)

  2. #2
    Guest
    Sorry, I don't know the answer , but Yonatan would know this.

  3. #3
    Guest
    Probably cos you're using unsigned bytes. The sign (pos or negative) takes an extra bit at the front of the byte. That would make the length of a signed byte equaling 255 be 9 bits long. I think.

    This is something I haven't actually tested in VB - it's just based on general computing principles.

    - gaffa

  4. #4
    Guru Yonatan's Avatar
    Join Date
    Apr 1999
    Location
    Israel
    Posts
    892
    Yep Scorp, your answer was correct, 2 points.
    This could be done with bitwise comparison (as you are doing) but it is not as much fun as copying the memory directly.

    Here's the best way (in my opinion):
    Code:
    Option Explicit
    
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSrc As Any, ByVal cbSrc As Long)
    
    Function LoByte(ByVal wWord As Integer) As Byte
        Call CopyMemory(LoByte, wWord, 1)
    End Function
    
    Function HiByte(ByVal wWord As Integer) As Byte
        Call CopyMemory(HiByte, ByVal VarPtr(wWord) + 1, 1)
    End Function
    
    Function MakeWord(ByVal btLoByte As Byte, ByVal btHiByte As Byte) As Integer
        Call CopyMemory(MakeWord, btLoByte, 1)
        Call CopyMemory(ByVal VarPtr(MakeWord) + 1, btHiByte, 1)
    End Function
    Enjoy!

    And, your overflows are because a Byte in VB is unsigned. It cannot receive negative values.
    Code:
    Dim MyByte As Byte
    
    MyByte = -1 ' Overflow

  5. #5
    Guest
    But why won't this for negative values work then? I've tried this one, but to no avail... Help?

    Code:
        If bHiByte > &H80 Then
            Byte2Word = -(((bHiByte - &H80) * &H100) Or (bLoByte And &HFF&))
        Else
            Byte2Word = (bHiByte * &H100) Or (bLoByte And &HFF&)
        End If

  6. #6
    Guru Yonatan's Avatar
    Join Date
    Apr 1999
    Location
    Israel
    Posts
    892
    Use my LoByte, HiByte and MakeWord functions.
    Also, for negative numbers, use the LoByte function to unsign them.
    For example:
    Code:
    ' This will not work for negative values:
    MyWord = MakeWord(MyLoByte, MyHiByte)
    
    ' This will work for any values:
    MyWord = MakeWord(LoByte(MyLoByte), LoByte(MyHiByte))
    It may seem like it does not matter (a byte is a byte) but it will actually make it possible to use negative values.

    Enjoy!

  7. #7
    Fanatic Member Mad Compie's Avatar
    Join Date
    Aug 2000
    Location
    Kuurne (Belgium)
    Posts
    553
    This is stupid guys, he made an error by not considering the max. length of an integer/byte/long.
    The erroneous function is Bytes2Word.
    You're doing a multiplication with &h100 (=256). Since a byte can only be 255, an error occurs when sending i.e. &h88 as the first parameter.
    You can't change the parameter to an integer because the multiplication could set b15 of the word, wchich also creates an error (integers are >=-32768 and <+32768).
    So, just handle the parameters as a long (I used Clng) and no error will occur anymore!

    Code:
    Option Explicit
    
    Private Sub Form_Paint()
      Me.Cls
      Me.Print "1024d = " & Format$(Hex(HiByte(1024)), "00"); Format$(Hex(LoByte(1024)), "00")
      Me.Print Bytes2Word(&H88, 0)
    End Sub
    
    Public Function HiByte(ByVal wParam As Integer) As Byte
        HiByte = wParam \ &H100 And &HFF&
    End Function
    
    Public Function LoByte(ByVal wParam As Integer) As Byte
        LoByte = wParam And &HFF&
    End Function
    
    Public Function Bytes2Word(ByVal bHiByte As Byte, ByVal bLoByte As Byte) As Long
      Bytes2Word = (CLng(bHiByte) * &H100) Or (CLng(bLoByte) And &HFF&)
    End Function

  8. #8
    Guest
    I'm very sorry guys.... BUT:

    1) Yonatan: I like your solution. It's very cretaive and functional. But I don't like the call the API for such a simple thing. It should be possible in "simple" (or "clean" or "normal" or whatever you like") VB.

    2) Mad Compie: When I pass the vale -1234 to HiByte I get 252, and to LoByte I get 46. So passing 252 and 46 to Byte2Word it should return -1234 but instead it returns 64558. I also thought of that (using Cint where you are using Clng) but it doesn't work.

    Is there NO way around this problem? I don't think I can accept that... Anyone? Let's put our heads together if we can't get it to work....

  9. #9
    Guest
    Anyone?

  10. #10
    Guru Yonatan's Avatar
    Join Date
    Apr 1999
    Location
    Israel
    Posts
    892
    I use CopyMemory everyday so it just comes naturally.

    And the solution is: Make Bytes2Word return Integer rather than Long.

    ...or use my solution.

  11. #11
    Guest
    My function DOES return an Integer. I'm at this point now where I've got:

    Code:
        If bHiByte >= &H80 Then  ' Negative value
            Byte2Word = (((((bHiByte And &H7F) - 1) * &H100) Or bLoByte) Or &H8000)
            
            ' AND &H7F   = Change Byte's Sign to Positive
            ' *  &H100 = Shift 8 bits -> Left
            ' Or bLoByte = Add LowByte
            ' Or &H8000  = Change Sign back to negative.    
    
        Else    ' Positive value        
    
            Byte2Word = (bHiByte * &H100) Or (bLoByte And &HFF&)
        
        End If
    But it won't work for the values -256 to 0 and some other (seemingly random but surely not random)... I'm still working on it... If anyone knows, please let me know too...

  12. #12
    Guru Yonatan's Avatar
    Join Date
    Apr 1999
    Location
    Israel
    Posts
    892
    Here is a slow solution without CopyMemory:
    Code:
    Function LoByte(ByVal wWord As Integer) As Byte
        LoByte = wWord And &HFF
    End Function
    
    Function HiByte(ByVal wWord As Integer) As Byte
        HiByte = wWord \ &H100 And &HFF
    End Function
    
    Function MakeWord(ByVal btLoByte As Byte, ByVal btHiByte As Byte) As Integer
        MakeWord = CInt("&H" & Hex(btLoByte Or (CLng(btHiByte) * &H100)))
    End Function
    To use MakeWord with any values, including negative:

    Call MakeWord(LoByte(MyLoByte), LoByte(MyHiByte))

    But, CopyMemory is still the fastest, and in my opinion, the best solution.
    I'll get over it.

  13. #13
    Guest
    Yonatan: Have you tried your code? I'm very sorry but it doesn't work for me...
    Paste this in a form and see what it does...

    Code:
    Option Explicit
    
    Const MinInt = -32768
    Const MaxInt = 32766
    
    Function LoByte(ByVal wWord As Integer) As Byte
        LoByte = wWord And &HFF
    End Function
    
    Function HiByte(ByVal wWord As Integer) As Byte
        HiByte = wWord \ &H100 And &HFF
    End Function
    
    Function MakeWord(ByVal btLoByte As Byte, ByVal btHiByte As Byte) As Integer
        MakeWord = CInt("&H" & Hex(btLoByte Or (CLng(btHiByte) * &H100)))
    End Function
    
    Private Sub Form_Load()
        Dim T As Integer
        Dim a As Byte
        Dim b As Byte
        
        For T = MinInt To MaxInt
            a = HiByte(T)
            b = LoByte(T)
            If MakeWord(b, a) <> T Then Debug.Print T & "<>" & MakeWord(b, a)
            'Also a,b doesn't work...
            'If MakeWord(a, b) <> T Then Debug.Print T & "<>" & MakeWord(a, b)
        Next T
    End Sub
    [Edited by RobIII on 09-28-2000 at 08:47 AM]

  14. #14
    Guest
    Maybe I'll go with the CopyMemory thing then.. That one DOES work.. But I still find it strange I (we?) can't get this thing to work correctly....

    But what I've forgotten all the time is to thank you for all of your efforts (everybody!)

  15. #15
    Guest
    DAMN. Watch these timings:

    CopyMemory loop (in sec):
    0,3515625
    0,3515625
    0,359375
    0,3515625
    0,359375
    0,359375
    0,359375

    "Normal/Clean/Simple VB" loop (in sec):
    0,2890625
    0,2890625
    0,2773438
    0,2890625
    0,2929688
    0,2890625
    0,2890625

    Number of iterations was the same in both tests. Although my function Didn't return the correct results...

    So CopyMemory IS slower... I think I'll get back and try a bit more 'till my function functions....

  16. #16
    Guru Yonatan's Avatar
    Join Date
    Apr 1999
    Location
    Israel
    Posts
    892
    So you really hate CopyMemory.
    Two mistakes in the "clean" VB code.
    Code:
    Const MaxInt = 32767 ' Not: 32766
    
    ' And, as I said, pass the LoBytes of the values to make it work.
    If MakeWord(LoByte(b), LoByte(a)) <> T Then Debug.Print T & "<>" & MakeWord(LoByte(b), LoByte(a))

  17. #17
    Guru Yonatan's Avatar
    Join Date
    Apr 1999
    Location
    Israel
    Posts
    892
    I tested the timing and here is my conclusion.
    It seems that, for simple LoByte and HiByte, the delay of loading the Kernel32.DLL, getting the address of RtlMoveMemory and calling it was too much.
    So for LoByte and HiByte, the pure VB version was a little bit faster.
    But, for MakeWord, the CopyMemory solution is significantly faster (almost 3 times faster).
    So here are the fastest functions I thought of:
    Code:
    Option Explicit
    
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSrc As Any, ByVal cbSrc As Long)
    
    Function LoByte(ByVal wWord As Integer) As Byte
        LoByte = wWord And &HFF
    End Function
    
    Function HiByte(ByVal wWord As Integer) As Byte
        HiByte = wWord \ &H100 And &HFF
    End Function
    
    Function MakeWord(ByVal btLoByte As Byte, ByVal btHiByte As Byte) As Integer
        Call CopyMemory(MakeWord, btLoByte, 1)
        Call CopyMemory(ByVal VarPtr(MakeWord) + 1, btHiByte, 1)
    End Function
    
    ' Best usage for MakeWord:
    MyWord = MakeWord(SomeValueForLoByte And &HFF, SomeValueForHiByte And &HFF)
    ' Using the "And &HFF" thing in place works best and fastest for negative values.
    Enjoy!

  18. #18
    Guest
    Yeah! Now that's what I call productive thinking. I'm glad we "sorted it out". Thank you, very, very much

    Rob.

  19. #19
    Fanatic Member Mad Compie's Avatar
    Join Date
    Aug 2000
    Location
    Kuurne (Belgium)
    Posts
    553
    When dealing with bits and bytes, you normally shouldn't pay attention to the leftmost signbit.
    It's only interesting if we want to do some calculations with the values, otherwise, $FF is 255 and not -1. It's completely up to you if you want to use signed or unsigned values.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width