[RESOLVED] Bit conversion operations
Hello everyone.
In my API class I encountered a lot of times I had to Get/Set low/high order <something> of <something>. For example, get the low order integer from a long. I made a few functions for this:
Code:
Public Shared Function LHByteCombine(ByVal low As Byte, ByVal high As Byte) As Short
Return low + high * 2 ^ 8
End Function
Public Shared Function LHShortCombine(ByVal low As Short, ByVal high As Short) As Integer
Return low + high * 2 ^ 16
End Function
Public Shared Function LHIntegerCombine(ByVal low As Integer, ByVal high As Integer) As Long
Return low + high * 2 ^ 32
End Function
Public Shared Function GetHighByte(ByVal value As Short) As Byte
Return value >> 8
End Function
Public Shared Function GetHighShort(ByVal value As Integer) As Short
Return value >> 16
End Function
Public Shared Function GetHighInteger(ByVal value As Long) As Integer
Return value >> 32
End Function
Public Shared Function GetLowByte(ByVal value As Short) As Byte
Return value - GetHighByte(value) * 2 ^ 8
End Function
Public Shared Function GetLowShort(ByVal value As Integer) As Short
Return value - GetHighShort(value) * 2 ^ 16
End Function
Public Shared Function GetLowInteger(ByVal value As Long) As Integer
Return value - GetHighInteger(value) * 2 ^ 32
End Function
Before I start using these functions permanently, I come to ask if it contains any bugs, especially because of the Unsigned/Signed issue.
Re: Bit conversion operations
You are not coding with option strict on. If you turned it on, you would see none of the methods compile because you are using narrowing conversions on the numeric values. With strict off, the runtime tries to perform the conversion for you, but this can lead to you assuming things are correct when in fact they could be wrong.
For example, try running this:
Code:
Try
Dim myShort As Short = CombineLH(Byte.MaxValue, Byte.MaxValue)
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
Re: Bit conversion operations
I thought there was an issue with your GetHigh methods, but if you do the proper conversion, as Kleinma suggested, it will be ok.
Re: Bit conversion operations
Quote:
Originally Posted by
Shaggy Hiker
I thought there was an issue with your GetHigh methods, but if you do the proper conversion, as Kleinma suggested, it will be ok.
I thought maybe the GetHigh method would make everything really slow and make the app memory hungry ;)
Re: Bit conversion operations
Thanks for the replies. :D
Updated the first post so no narrowing conversions take place. ;)
I had it like that before, but noticed that it was able to do the narrowing itself so left it out. (it called the GetLow byte version when two bytes were passed.)
But I can understand that narrowing numeric values can cause some issues and it would be annoying if you find it out after you made everything around it. :bigyello:
What about the UShort - Short, UInteger - Integer and ULong - Long conversions? I believe the signed versions have a bit set indicating negative/positive. Can that be of any problem when using these functions?
EDIT
Ow and does changing:
To:
Code:
Return CByte(value >> 8)
Improve the code or would the implicit conversion do the same thing anyways?
Re: Bit conversion operations
Quote:
Originally Posted by
kleinma
I thought maybe the GetHigh method would make everything really slow and make the app memory hungry ;)
:lol::lol::lol:
I can't believe I missed that.
Implicit narrowing conversions will happen with Option Strict OFF, but even though they can be done, they are also slow, so don't do them. The speed difference is negligible if you are only calling a few of these, but bit manipulation is something you tend to do lots of if you do it at all, so a bit of attention to this detail will probably pay off, perhaps even noticeably.
The signed versions don't really use a bit to indicate negative. You could look up Twos Complement for an in depth explanation of how negative numbers or stored, or Threes Company for a light commentary on the 70s. However, the net result (or the .NET result, anyways) of the Twos Complement way of storing numbers is that the high bit will be set for negative numbers. One interesting feature of .NET is that when you use the bit shift operator (>>), the most significant bit is propagated over. In C++, the >> operator shifts to the right and fills the high bits with 0. In .NET, the >> operator shifts to the right and fills the top bits with whatever was originally in the most significant bit. In your case, however, this will have no impact, because you are shifting over by a number of bytes, and throwing out the top bytes. If you were not shifting by multiples of 8, or were retaining the most significant bits, then you would have a problem. For example, if your GetHigh returned the same type as the argument rather than a type half the size of the argument, then you would certainly have an issue. You might actually consider doing this. After all, a 32 bit processor handles 32 bit values most efficiently. Therefore, shortening an Integer to a Short doesn't buy you much of anything. You could leave it as an integer, but if you did, you'd have to mask off the high bytes:
Code:
Public Function GetHigh(myInt As Integer) As Integer
return ((myInt >>16) And &HFFFF)
End Function
Re: Bit conversion operations
Aah so THAT is why those functions on the internet use [& &HFFFF]. :bigyello:
But I prefer keeping the conversions, since I will be using the same functions in API's. For example, get the integer of a short and two bytes combined:
Code:
Dim flags As Integer = LHShortCombine(myshort, LHByteCombine(mybyte1, mybyte2))
BTW: I used a bitconverter-method before, but I read everywhere that it is significantly slower. Here the (probably narrowing conversion sensitive) functions I used previously:
Code:
Public Shared Function GetBytes(ByVal ParamArray values() As Object) As Byte()
Dim b As New List(Of Byte)
For Each value As Object In values
If TypeOf value Is Byte Then
b.Add(value)
Else
b.AddRange(BitConverter.GetBytes(value))
End If
Next
Return b.ToArray
End Function
Public Shared Function GetShort(ByVal l As Byte, ByVal h As Byte) As Short
Return BitConverter.ToInt16(GetBytes(l, h), 0)
End Function
Public Shared Function GetInteger(ByVal ParamArray values() As Object) As Integer
Return BitConverter.ToInt32(GetBytes(values), 0)
End Function
I had to use a lot of 'CTyping' because most passed values were enumerated values of type <>.
Basically, I have to pass variables (LParam most of the time) that contain other values inside. I do not want to go with making structures for every single "Type" of LParam but want to "Combine" values of different types into the LParam. This can either be done with that "GetBytes -> Integer" method or by using multiple 'combine and convert' and splitting functions.
Re: Bit conversion operations
By the way, there's a very easy way to convert between things with, for example, this 8-byte type: (Assumes Imports System.Runtime.InteropServices)
Code:
<StructLayout(LayoutKind.Explicit)> _
Public Structure XLong
<FieldOffset(0)> Public UnsignedLongValue As ULong
<FieldOffset(0)> Public LongValue As Long
<FieldOffset(0)> Public HighInt As Integer
<FieldOffset(4)> Public LowInt As Integer
<FieldOffset(0)> Public Short1 As Short
<FieldOffset(2)> Public Short2 As Short
<FieldOffset(4)> Public Short3 As Short
<FieldOffset(6)> Public Short4 As Short
<FieldOffset(0)> Public Byte1 As Byte
<FieldOffset(1)> Public Byte2 As Byte
<FieldOffset(2)> Public Byte3 As Byte
<FieldOffset(3)> Public Byte4 As Byte
<FieldOffset(4)> Public Byte5 As Byte
<FieldOffset(5)> Public Byte6 As Byte
<FieldOffset(6)> Public Byte7 As Byte
<FieldOffset(7)> Public Byte8 As Byte
End Structure
This allows you to get and set any bytes, shorts, ints, or longs in an 8-byte value. It's also very efficient.
Re: Bit conversion operations
Interesting...but there are no problems if I would, for example, send such a structure instead of a long? And what if you have to retrieve the values from the LParam if you know that the order is short-byte-byte? Not sure what happens if you would use such a structure in a ByRef. :confused:
EDIT
Plus I would preferably make all members hidden and use properties with an index variable instead:
vb Code:
<StructLayout(LayoutKind.Explicit)> _
Public Structure XLong
<FieldOffset(0)> Private ULong1 As ULong
<FieldOffset(0)> Private Long1 As Long
<FieldOffset(0)> Private Integer1 As Integer
<FieldOffset(4)> Private Integer2 As Integer
<FieldOffset(0)> Private Short1 As Short
<FieldOffset(2)> Private Short2 As Short
<FieldOffset(4)> Private Short3 As Short
<FieldOffset(6)> Private Short4 As Short
<FieldOffset(0)> Private Byte1 As Byte
<FieldOffset(1)> Private Byte2 As Byte
<FieldOffset(2)> Private Byte3 As Byte
<FieldOffset(3)> Private Byte4 As Byte
<FieldOffset(4)> Private Byte5 As Byte
<FieldOffset(5)> Private Byte6 As Byte
<FieldOffset(6)> Private Byte7 As Byte
<FieldOffset(7)> Private Byte8 As Byte
Public Property ByteMember(ByVal index As Integer) As Byte
Get
Select Case index
Case 0
Return Me.Byte1
Case 1
Return Me.Byte2
Case 2
Return Me.Byte3
Case 3
Return Me.Byte4
Case 4
Return Me.Byte5
Case 5
Return Me.Byte6
Case 6
Return Me.Byte7
Case 7
Return Me.Byte8
Case Else
Return Nothing
End Select
End Get
Set(ByVal value As Byte)
Select Case index
Case 0
Me.Byte1 = value
Case 1
Me.Byte2 = value
Case 2
Me.Byte3 = value
Case 3
Me.Byte4 = value
Case 4
Me.Byte5 = value
Case 5
Me.Byte6 = value
Case 6
Me.Byte7 = value
Case 7
Me.Byte8 = value
End Select
End Set
End Property
Public Property ShortMember(ByVal index As Integer) As Short
Get
Select Case index
Case 0
Return Me.Short1
Case 1
Return Me.Short2
Case 2
Return Me.Short3
Case 3
Return Me.Short4
Case Else
Return Nothing
End Select
End Get
Set(ByVal value As Short)
Select Case index
Case 0
Me.Short1 = value
Case 1
Me.Short2 = value
Case 2
Me.Short3 = value
Case 3
Me.Short4 = value
End Select
End Set
End Property
Public Property IntegerMember(ByVal index As Integer) As Integer
Get
Select Case index
Case 0
Return Me.Integer1
Case 1
Return Me.Integer2
Case Else
Return Nothing
End Select
End Get
Set(ByVal value As Integer)
Select Case index
Case 0
Me.Integer1 = value
Case 1
Me.Integer2 = value
End Select
End Set
End Property
Public Property LongMember() As Long
Get
Return Me.Long1
End Get
Set(ByVal value As Long)
Me.Long1 = value
End Set
End Property
Public Property ULongMember() As ULong
Get
Return Me.ULong1
End Get
Set(ByVal value As ULong)
Me.ULong1 = value
End Set
End Property
End Structure
Re: Bit conversion operations
No, here's how you use it:
Code:
' To retrieve something from LParam with order short-byte-byte
Dim v As XLong
v.LowInt = theLParam
Dim theHighShort As Short = v.Short3
Dim theBytes() As Byte = {v.Byte7, v.Byte8}
' Do stuff
And that in reverse to set things. When you want to transfer its Long value to a function, instead pass theVariable.LongValue.
Re: Bit conversion operations
Hey never thought of that :D
Thanks, I guess I will be using this, or a somewhat modified version. :thumb:
(like adding constructors to hold a certain value at start)
I always thought the <offset> thing only allowed a certain row of values, didn't know the values actually shared their bytes! Another thing learned. :bigyello:
EDIT
Final code:
vb Code:
<StructLayout(LayoutKind.Explicit)> _
Public Structure WORD
<FieldOffset(0)> Public Short1 As Short
<FieldOffset(0)> Public UShort1 As UShort
<FieldOffset(0)> Public Byte1 As Byte
<FieldOffset(1)> Public Byte2 As Byte
End Structure
<StructLayout(LayoutKind.Explicit)> _
Public Structure DWORD
<FieldOffset(0)> Public Integer1 As Integer
<FieldOffset(0)> Public UInteger1 As UInteger
<FieldOffset(0)> Public Short1 As Short
<FieldOffset(0)> Public UShort1 As UShort
<FieldOffset(2)> Public Short2 As Short
<FieldOffset(2)> Public UShort2 As UShort
<FieldOffset(0)> Public Byte1 As Byte
<FieldOffset(1)> Public Byte2 As Byte
<FieldOffset(2)> Public Byte3 As Byte
<FieldOffset(3)> Public Byte4 As Byte
End Structure
<StructLayout(LayoutKind.Explicit)> _
Public Structure QWORD
<FieldOffset(0)> Public Long1 As Long
<FieldOffset(0)> Public ULong1 As ULong
<FieldOffset(0)> Public Integer1 As Integer
<FieldOffset(0)> Public UInteger1 As UInteger
<FieldOffset(4)> Public Integer2 As Integer
<FieldOffset(4)> Public UInteger2 As UInteger
<FieldOffset(0)> Public Short1 As Short
<FieldOffset(0)> Public UShort1 As UShort
<FieldOffset(2)> Public Short2 As Short
<FieldOffset(2)> Public UShort2 As UShort
<FieldOffset(4)> Public Short3 As Short
<FieldOffset(4)> Public UShort3 As UShort
<FieldOffset(6)> Public Short4 As Short
<FieldOffset(6)> Public UShort4 As UShort
<FieldOffset(0)> Public Byte1 As Byte
<FieldOffset(1)> Public Byte2 As Byte
<FieldOffset(2)> Public Byte3 As Byte
<FieldOffset(3)> Public Byte4 As Byte
<FieldOffset(4)> Public Byte5 As Byte
<FieldOffset(5)> Public Byte6 As Byte
<FieldOffset(6)> Public Byte7 As Byte
<FieldOffset(7)> Public Byte8 As Byte
End Structure
Just wondering though...could you store Byte1...Byte8 as an array (of 8 elements) or would that interfere with the Marshalling too much?
E.g.
vb Code:
<StructLayout(LayoutKind.Explicit)> _
Public Structure QWORDA
<FieldOffset(0)> Public Longs() As Long
<FieldOffset(0)> Public ULongs() As ULong
<FieldOffset(0)> Public Integers() As Integer
<FieldOffset(0)> Public UIntegers() As UInteger
<FieldOffset(0)> Public Shorts() As Short
<FieldOffset(0)> Public UShorts() As UShort
<FieldOffset(0)> Public Bytes() As Byte
End Structure
Re: [RESOLVED] Bit conversion operations
I've been using something similar to Minitech there, and just want to confirm it appears very efficient.
Re: [RESOLVED] Bit conversion operations
I don't know; I've never tried. Marshal.Copy seems to handle them pretty well though, and array elements should be stored sequentially in memory so that is a possibility.
No wait, it's not possible - the structure could never know the size of the array ahead of time. You would store a POINTER to the array which would really mess things up for you. On a 64-bit system, the LongValue would be a pointer to the array and on a 32-bit system half of it would be zeroes. Don't do that.
Re: [RESOLVED] Bit conversion operations
Okay, I'll keep the large amounts of members then. ;)
Might add some properties to them like "Bytes(Integer index, Integer count) As Byte()", but I'm not sure. Thanks for the help everyone, really didn't expect that this would have been a possibility. :check:
And yep, it looks very slim. For example, the following function:
Code:
Private Shared Function GetErrorString(ByVal ErrorCode As Long) As String
With New QWORD(ErrorCode)
GetErrorString = "Device " & .Integer2 & " - "
Dim buff As New System.Text.StringBuilder(128)
If mciGetErrorString(.Integer1, buff, buff.Capacity) Then
GetErrorString &= buff.ToString
Else
GetErrorString &= "Error code: " & .Integer1
End If
End With
End Function
Re: [RESOLVED] Bit conversion operations
In c# you can define a sequential sequence of a type within a structure using the fixed keyword but this is considered unsafe and so I assume not an option in VB.
Re: [RESOLVED] Bit conversion operations
Ow I found it no problem having all those members in the end, it is actually pretty useful having all these members ready to access in one go.
For example, I added a CType conversion from "WORD to Short", "DWORD to Integer" and "QWORD to Long" and can now pass my structure into functions that require a Short/Integer/Long parameter. Plus some "FromXXXX" functions:
Code:
Public Shared Function FromSBB(ByVal Short1 As Short, ByVal Byte1 As Byte, ByVal Byte2 As Byte) As DWORD
FromSBB = New DWORD
FromSBB.Short1 = Short1
FromSBB.Byte3 = Byte1
FromSBB.Byte4 = Byte2
End Function
This must be the easiest order conversion I ever did. :bigyello: