Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bitwise operations - checking and removal

Note the simple example below:

Module Module1
    <Flags>
    Public Enum Names
        None    = 0
        Test    = 1
        Test2   = 2
        Test3   = 4
        Test4   = 8
    End Enum

    Sub Main()
        Dim test As Names = Names.Test Or Names.Test3
        If (test And Names.Test3) = Names.Test3
            Console.WriteLine("TRUE")
        Else
            Console.WriteLine("FALSE")
        End If
    End Sub
End Module

The first part of my question relates to the line If (test And Names.Test3) = Names.Test3.

Wouldn't it be better to simply check If test And Names.Test3 as if the flag exists? If it evaluates to a non-zero value (meaning the flag exists) then the result of the condition would be True anyway.

Is there a good reason to use the first way of checking over the second? (Whilst my answer is for VB.NET, I would also be interested in knowing if this is a potential pitfall anywhere else, ie C#, C++, etc).

Also, with regards to flag removal, it seems there's two ways to do this:

test = test Xor Names.Test3 and test = test And Not Names.Test3

However, the first will add the flag if it's missing, and remove it if it is there, whereas the second will only remove it. Is that the only difference? Or is there another reason why I should prefer one method over the other?

like image 879
Interminable Avatar asked Mar 12 '23 19:03

Interminable


2 Answers

You are correct in stating that you can effectively replace this:

If (test And Names.Test3) = Names.Test3 Then

with this

If (test And Names.Test3) Then

But, the second example will not compile with Option Strict On as you rightly get the error:

Option Strict On disallows implicit conversions from 'Names' to 'Boolean' so in order to get this to compile you need to wrap a CBool round it.

So, in conclusion I would say it is much better to use the first example as the intent is very clear:- you are checking to see if a bit is set.

In terms of flag removal i.e. unsetting a bit you should use:

test = test And Not Names.Test3

Using Xor has the effect of toggling the value.

The following might help (especially if you make them extension methods):

Public Function SetBit(ByVal aValue As Names, ByVal aBit As Names) As Names
    Return (aValue Or aBit)
End Function

Public Function ClearBit(ByVal aValue As Names, ByVal aBit As Names) As Names
    Return (aValue And Not aBit)
End Function

Public Function IsBitSet(ByVal aValue As Names, ByVal aBit As Names) As Boolean
    Return ((aValue And aBit) = aBit)
End Function

Public Function ToggleBit(ByVal aValue As Names, ByVal aBit As Names) As Names
    Return (aValue Xor aBit)
End Function
like image 183
Matt Wilko Avatar answered May 03 '23 04:05

Matt Wilko


Remember that Flags enums don't have to all be purely single bit values. E.g. imagine (with better names) that your enum was :

<Flags>
Public Enum Names
    None    = 0
    Test    = 1
    Test2   = 2
    Test3   = 4
    Test4   = 8
    Test2AndTest4 = 10
End Enum

Now, you wouldn't want to just test that test And Names.Test2AndTest4 is non-zero since that doesn't answer the correct question. So it's a better habit to get into, in general, to And your mask to check and then compare to the mask value, to ensure that all bits of the mask are set.

like image 23
Damien_The_Unbeliever Avatar answered May 03 '23 05:05

Damien_The_Unbeliever