• Home   /  
  • Archive by category "1"

C Bit Flag Comparison Essay

Wyświetla następujący przykład na systemach 32 bitowych:

Wyświetla następujący przykład na systemach 64 bitowych:

--- BIT SHIFT RIGHT ON POSITIVE INTEGERS --- Expression: 2 = 4 >> 1 Decimal: val=4 res=2 Binary: val=0000000000000000000000000000000000000000000000000000000000000100 res=0000000000000000000000000000000000000000000000000000000000000010 NOTE: copy of sign bit shifted into left side Expression: 1 = 4 >> 2 Decimal: val=4 res=1 Binary: val=0000000000000000000000000000000000000000000000000000000000000100 res=0000000000000000000000000000000000000000000000000000000000000001 Expression: 0 = 4 >> 3 Decimal: val=4 res=0 Binary: val=0000000000000000000000000000000000000000000000000000000000000100 res=0000000000000000000000000000000000000000000000000000000000000000 NOTE: bits shift out right side Expression: 0 = 4 >> 4 Decimal: val=4 res=0 Binary: val=0000000000000000000000000000000000000000000000000000000000000100 res=0000000000000000000000000000000000000000000000000000000000000000 NOTE: same result as above; can not shift beyond 0 --- BIT SHIFT RIGHT ON NEGATIVE INTEGERS --- Expression: -2 = -4 >> 1 Decimal: val=-4 res=-2 Binary: val=1111111111111111111111111111111111111111111111111111111111111100 res=1111111111111111111111111111111111111111111111111111111111111110 NOTE: copy of sign bit shifted into left side Expression: -1 = -4 >> 2 Decimal: val=-4 res=-1 Binary: val=1111111111111111111111111111111111111111111111111111111111111100 res=1111111111111111111111111111111111111111111111111111111111111111 NOTE: bits shift out right side Expression: -1 = -4 >> 3 Decimal: val=-4 res=-1 Binary: val=1111111111111111111111111111111111111111111111111111111111111100 res=1111111111111111111111111111111111111111111111111111111111111111 NOTE: same result as above; can not shift beyond -1 --- BIT SHIFT LEFT ON POSITIVE INTEGERS --- Expression: 8 = 4 << 1 Decimal: val=4 res=8 Binary: val=0000000000000000000000000000000000000000000000000000000000000100 res=0000000000000000000000000000000000000000000000000000000000001000 NOTE: zeros fill in right side Expression: 4611686018427387904 = 4 << 60 Decimal: val=4 res=4611686018427387904 Binary: val=0000000000000000000000000000000000000000000000000000000000000100 res=0100000000000000000000000000000000000000000000000000000000000000 Expression: -9223372036854775808 = 4 << 61 Decimal: val=4 res=-9223372036854775808 Binary: val=0000000000000000000000000000000000000000000000000000000000000100 res=1000000000000000000000000000000000000000000000000000000000000000 NOTE: sign bits get shifted out Expression: 0 = 4 << 62 Decimal: val=4 res=0 Binary: val=0000000000000000000000000000000000000000000000000000000000000100 res=0000000000000000000000000000000000000000000000000000000000000000 NOTE: bits shift out left side --- BIT SHIFT LEFT ON NEGATIVE INTEGERS --- Expression: -8 = -4 << 1 Decimal: val=-4 res=-8 Binary: val=1111111111111111111111111111111111111111111111111111111111111100 res=1111111111111111111111111111111111111111111111111111111111111000 NOTE: zeros fill in right side Expression: -9223372036854775808 = -4 << 61 Decimal: val=-4 res=-9223372036854775808 Binary: val=1111111111111111111111111111111111111111111111111111111111111100 res=1000000000000000000000000000000000000000000000000000000000000000 Expression: 0 = -4 << 62 Decimal: val=-4 res=0 Binary: val=1111111111111111111111111111111111111111111111111111111111111100 res=0000000000000000000000000000000000000000000000000000000000000000 NOTE: bits shift out left side, including sign bit

If you’re a game developer chances are you’re familiar with the need to describe different variations of an attribute. Whether it’s the type of an attack (melee, ice,  fire, poison, …) or the state of an enemy AI (idle, alerted, chasing, attacking, resting, …) you can’t escape this. The most naive way of implementing this is simply by using constants:

The downside is that you have no actual control over the values you can assign to attackType: it can be any integer, and you can also do dangerous things such as attackType++.

The enum construct

Luckily, C# has a construct called enum (for enumeration) which has been specifically designed for these situations:

The definition of an enum creates a type which can support only a limited range or values. These values are given symbolic labels for clarity and are also returned as string when needed:

Internally, every label has an integer value. Enums starts from zero and every new label is assigned the next integer number. None is zero, Melee is one, Fire is two and so on. You can change that by explicitly changing the value of a label:

Casting an enum to int will return its integer value. To be fair, enums are actual integers.

What makes enums even so interesting is the fact that they are automatically integrated in the Unity inspector. If a public field is an enum, it will conveniently appear like a dropdown menu:

Enums and Flags

The vast majority of developers use enums just as we’ve seen before. There’s much more we can do with them though. The first limitation is that standard enums can only hold a value at a time. What if we are using a melee weapon with a fire attack (a fire sword?!)? To solve this problem, enums can be decorated with [Flags]. This allows them to be treated as bit masks, storing multiple values between them:

In the example above, attackType both holds Melee and Fire values. We’ll see later how it is possible to retrieve these values. But first we need to understand how this is actually working. Enums are store as integers; when you have consecutive numbers, their bit representations look like this:

If we want to use [Flags] at its best, we should use only powers of two for the values of our labels. As you can see below, this means that every non-zero label has exactly one 1 in its binary representation, and that they are all in different positions:

At this stage, the variable attackType can be seen as a series of bits, each one indicating if it has or not a certain property. If the first bit is one, it is a melee attack; if the second bit is one, it is a fire attack, if the third bit is one it is an ice attack, and so on. It is important to notice in order for this to work, labels have to be manually initialised as powers of two. We will see later in this post how to do it more elegantly. Lastly, since enums are normally stored into Int32, it’s unwise to have an enum with more then 32 different labels.

To be fair, you can use enums even without [Flags]. The only thing it does is allowing a nicer output ofenums when they are printed.

Bitwise operators

A bit mask is, essentially, an integer value in which several binary property (yes/no) are independently stored in its bit. In order to pack and unpack them we need some special operators. C# calls them bitwise operator, because they work on a bit to bit basis, ignoring carries unlikely addition and subtraction operators.

Bitwise OR

Setting a property is possible using the bitwise OR :

What the bitwise OR does is setting the bit in the i-th position to 1 if either one of its operands has the i-th bit to 1.

If you print attackType you’ll get a user-friendly result: Melee|Fire. This unfortunately doesn’t happen with the inspector; Unity doesn’t recognise the new type and simply leaves the field empty:

If you want the mixed type to appear, you’ll have to define it manually in the enum:

Bitwise AND

The complementary operator to the bitwise OR is the bitwise AND. It works in the exact same way, with the exception that when applied with two integers it keeps only the bits which are set in both of them. While bitwise OR is used to set bits, bitwise AND is typically used to unpack property previously stores in an integer.

Applying the bitwise AND between attackType and AttackType.Ice sets all the bits to zero, excepts the one relative to AttackType.Ice itself; its final value is determined by attackValue. If the attack vas indeed an icy one, the result will be exactly AttackType.Ice; otherwise zero:
If bitwise operators are making your head spinning, .NET 4.0 has introduced the method HasFlag which can be conveniently used as follow:

Now, you need to be careful about the fact that

publicstaticintNONE=0;

publicstaticintMELEE=1;

publicstaticintFIRE=2;

publicstaticintICE=3;

publicstaticintPOISON=4;

 

publicintattackType=NONE;

// Outside your class

publicenumAttackType{

    None,

    Melee,

    Fire,

    Ice,

    Poison

}

 

// Inside your class

publicAttackType attackType=AttackType.None;

attackType=AttackType.Poison;

Debug.Log("Attack: "+attackType);# Prints "Attack: Poison"

publicenumAttackType{

    None,     // 0

    Melee,    // 1

    Fire,     // 2

    Ice=4,  // 4

    Poison    // 5

}

[Flags]publicenumAttackType{

    None   =0,

    Melee  =1,

    Fire   =2,

    Ice    =4,

    Poison=8

}

// ...

publicAttackType attackType=AttackType.Melee|AttackType.Fire;

// Consecutive

publicenumAttackType{

    // Decimal     // Binary

    None   =0,    // 000000

    Melee  =1,    // 000001

    Fire   =2,    // 000010

    Ice    =3,    // 000011

    Poison=4     // 000100

}

// Powers of two

[Flags]publicenumAttackType{

    // Decimal     // Binary

    None   =0,    // 000000

    Melee  =1,    // 000001

    Fire   =2,    // 000010

    Ice    =4,    // 000100

    Poison=8     // 001000

}

attackType=AttackType.Melee|AttackType.Fire;

// OR

attackType=AttackType.Melee;

attackType|=AttackType.Fire;

// Label          Binary   Decimal

// Melee:         000001 = 1

// Fire:          000010 = 2

// Melee | Fire:  000011 = 3

[Flags]publicenumAttackType{

    // Decimal                  // Binary

    None         =0,           // 000000

    Melee        =1,           // 000001

    Fire         =2,           // 000010

    Ice          =4,           // 000100

    Poison       =8,           // 001000

 

    MeleeAndFire=Melee|Fire// 000011

}

attackType=AttackType.Melee|AttackType.Fire;

boolisIce=(attackType&AttackType.Ice)!=0;

// Label                Binary   Decimal

// Ice:                 000100 = 4

// MeleeAndFire:        000011 = 3

// MeleeAndFire & Ice:  000000 = 0

 

// Fire:                000010 = 2

// MeleeAndFire:        000011 = 3

// MeleeAndFire & Fire: 000010 = 2

1

attackType.HasFlag(AttackType.Ice);

One thought on “C Bit Flag Comparison Essay

Leave a comment

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *