Search Unity

  1. Get the latest news, tutorials and offers directly to your inbox with our newsletters. Sign up now.
    Dismiss Notice

[bitwise operation on integers ]What does "x >> 16" and "x & 0xffff" do numerically?

Discussion in 'Scripting' started by Mr-Mechanical, Apr 7, 2020.

  1. Mr-Mechanical

    Mr-Mechanical

    Joined:
    May 31, 2015
    Posts:
    507
    Looking at some source code:
    public int SimplexVertexA(int index) => (int)(Simplex[index] >> 16);
    public int SimplexVertexB(int index) => (int)(Simplex[index] & 0xffff);

    What do the bitwise operations do to the ints numerically?
     
  2. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    1,893
    The >>-operator is the right bitshift operator. It shifts the bits of the left parameter to the right by a number of bits defined by the right parameter. So if we had a small number 1111 and bitshifted it 2 bits to the right, we would get 1111 >> 2 = 11 = 0011. So instead of the decimal value of 15 (1111), we would now work with 3 (0011).
    In your example it's similar. Integers are 32bit, and whoever wrote that code shifts it 16 to the right.
    Bitshifting is done for various reasons. Bitshifting by one is the same as multiplying / dividing by 2, for example. It's also useful for getting the individual 8-bit color values from a Color32, to name another example. Quite frankly i have no idea what it is used for in your example. For one reason or another the person who wrote it is interested in the numeric value of the left half of the integer value in bits.

    The &-operator is the bitwise-and operator. It works similar to &&, which you probably often used to compare bools. It simply works on bits instead, so if we had a value a = 1011 and b = 0010, then a & b would be 0010, i.e. the number we get if we only consider the bits that where 1 for both inputs. This is useful in networking for applying masks and such.
    The 0xffff means the same as 2^16, so 1111.1111.1111.1111. Considering integers are 32 bit and the applied mask is only half its size, we basically just set the left half of the integer to 0's and work with what's left.

    So to sum this up, the SimpleVertexA version returns you the short (= 16 bit) value of the left side of an integer, while the SimpleVertexB version returns you the short value of the right side of an integer. What for, i dont know.
    You can read up on these operators here:
    https://docs.microsoft.com/en-us/do...ference/operators/bitwise-and-shift-operators
     
    Kurt-Dekker and Mr-Mechanical like this.
  3. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,175
    As soon as you see operators like these, it's important to stop imagining these as ints (despite the data type) and start thinking of them as a series of 1s and 0s.

    So if x is 24, think of it as
    00011000 (but with a bunch of 0s in front of it because int is 32bit)
    Now it's easy to make sense of the bitwise operators, and I'll include all the most common operators for completeness:

    Bit shifts << and >>

    These operators simply shift the bits left or right. Starting with x=24 as shown above:
    x << 2
    results in
    01100000
    (which is 96)
    x >> 2
    results in
    00000110
    (which is 6)

    Hex codes (anything beginning in 0x)
    Hex code is a number system (hexadecimal) that is easier to convert to binary because it's base 16, so every character is exactly 4 bits. In your example, "f" becomes "1111" in binary, so 0xffff is sixteen 1's, which in a 32bit int looks like:
    0000000000000000111111111111111111

    Bitwise AND &
    This takes two numbers, goes through them bit by bit, and does an AND operation on them. So in your example, let's imagine x is our 24, which for this one I'll go ahead and expand all the way:
    00000000000000000000000000011000
    Now if we do x & 0xffff, in this case it will return the same value as x, because all of the 1 bits are within the 1 bits of 0xffff.
    But let's do a bigger "number" (or really, a series of bits with some of the larger 1s enabled), like:
    00001111000011000000000000001111
    Now, x & 0xffff does something interesting. Because the first 16 bits of 0xffff are 0s, the AND operation for those is now false, so now x & 0xffff results in:
    00000000000000000000000000001111

    In practice in Unity you'll see this a lot in layer masks - there are 32 layers, each layer is one of these bits. So if you want to see if a given GameObject is on layer 5, you can do this:
    Code (csharp):
    1.  int someMask = 0xffff; //"just the first 16 layers"
    2. int thisLayer = gameObject.layer;
    3. int thisLayerAsABit = 1 << thisLayer; // shift the 1 over to the "thisLayer"th bit
    4. if (thisLayerAsABit & someMask) {
    Bitwise OR |
    This is basically the same as bitwise AND, except with an OR operation. You can use this to combine LayerMasks, but that's not very common. Where you're more likely to see this is in enums. In C# you can create enums with specified number values, and if these number values are powers of two, you can do a neat trick where you can | them together, and a single number can contain not just a reference to a single item on the enum, but any number of items on the enum. Here's a basic example:
    Code (csharp):
    1. enum WeaponFlags {IsMelee = 1, IsRanged = 2, IsFire = 4, IsBullet = 8; IsBomb = 16 }
    2.  
    3. int isFireAndMelee = WeaponFlags.IsMelee | WeaponFlags.IsFire;
    4. //isFireAndMelee bits: 00000000000000000000000000000101
    5.  
     
    Kurt-Dekker and Mr-Mechanical like this.
  4. Mr-Mechanical

    Mr-Mechanical

    Joined:
    May 31, 2015
    Posts:
    507
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    14,514
    Adding to the awesome answered by @Yoreki and @StarManta above, let me mention this one extra common operator: the Bitwise NOT. This is indicated by a tilde
    (~)
    .

    If you have a bitmask such as the examples above, but you want the OPPOSITE of it:

    Code (csharp):
    1. int myMaskingBits = ... (however you calculate them, such as above)
    2.  
    3. int myAntiMaskingBits = ~myMaskingBits;
    Now every bit has been inverted, from 0 to 1 and 1 to 0, from myMaskingBits to myAntiMaskingBits.

    This is common when you are querying for JUST one layer, rather than just trying to mask out that one layer.

    Code (csharp):
    1. int terrainLayerNo = 9;
    2.  
    3. int maskTerrain = 1 << terrainLayerNo;
    4.  
    5. int maskAllButTerrain = ~maskTerrain;
     
    Mr-Mechanical likes this.
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    14,514
    And finally there is Bitwise XOR... it is "exclusive OR" and lets you flip specific bits from on to off at will, while leaving the rest of the bits unchanged.

    Bitwise XOR is represented by the caret
    (^)
    and I won't get too much further into specifics of it, but suffice to say you might run into it from time to time.

    Code (csharp):
    1. int myOriginalBitmask = ...
    2.  
    3. int whichBitToToggle = 12;
    4.  
    5. int myMaskWithBitToggled = myOriginalMask ^ ( 1 << whichBitToToggle);
    It's not encountered a lot in Unity, but you might see it here and there.
     
    Mr-Mechanical likes this.
  7. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,444
    This used to be a common trick to save space, back when 48K was a lot of memory. If your character's stats went from 0 to 10, you could use just 4 bits for each, packing 4 stats into a single 16-bit integer. Of course, you needed all of that nonsense to pull them out.

    So, someone has numbers that never get too big (never past 2^16) and decided to save space by packing 2 of them into a single int. Maybe they could have just used
    short unsigned int
    , but maybe some systems use 32-bits for that anyway.
     
    lordofduct and Mr-Mechanical like this.
  8. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    1,893
    I was also thinking that they could've used shorts instead. So i'm actually quite curious @Mr-Mechanical where you found the example, if it was written in C#, and if it was simply created to teach about bitshifts, or if you otherwise found out what the actual reason for doing it like that was.
     
    Mr-Mechanical likes this.
  9. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    7,552
    If they were member fields this could be a consideration being made since field packing is system dependent. But array packing respects the type size (of course it will pad it to deal with the ends being an odd size to the system size).

    And since they use an array in the code posted... :shrug: Maybe the array is destined to be sent to some API that expects it in this format? Or maybe they found bitshifting the values at the index, rather than multiplying the index, easier? That way the value is always at the proposed index... make things like loops more readable I guess (foreach instead of for(+=2)).

    Hell if I know though. But I guess our being like "huh?" lends to OP taking away that maybe this isn't the best example use of bit shifting.
     
    Mr-Mechanical likes this.
  10. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,444
    The other thing that stuck out was using => to define the functions. That's for C# coders who only know how to write LINQ queries. Someone comfortable with bit-shifting would use the standard {} syntax. The way the web is, and Sturgeon's law, my first instinct was "uses a fancy new command many people won't know? Probably has lots of other problems -- skip it".
     
  11. Mr-Mechanical

    Mr-Mechanical

    Joined:
    May 31, 2015
    Posts:
    507
    I'm using Unity Physics package. It's in ConvexConvexDistanceQueries.Result. I'm using some of the functions from Unity.Physics for my ECS project and I was trying to make perfect sense of the output of one of the functions.

    Code (CSharp):
    1. namespace Unity.Physics
    2. {
    3.     // Low level convex-convex distance query implementations
    4.     public static class ConvexConvexDistanceQueries
    5.     {
    6.         // Convex distance result
    7.         public struct Result
    8.         {
    9.             public DistanceQueries.Result ClosestPoints;
    10.             public uint3 Simplex;
    11.             public int Iterations;
    12.             public const uint InvalidSimplexVertex = 0xffffffff;
    13.             public bool Valid => math.all(ClosestPoints.NormalInA == new float3(0));
    14.             public int SimplexDimension => Simplex.z == InvalidSimplexVertex ? (Simplex.y == InvalidSimplexVertex ? 1 : 2) : 3;
    15.             public int SimplexVertexA(int index) => (int)(Simplex[index] >> 16);
    16.             public int SimplexVertexB(int index) => (int)(Simplex[index] & 0xffff);
    17.         }
    Thanks a lot for the help! I'm starting to make sense of things a bit.
     
  12. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    14,514
    If only everybody thought this way! We don't need 27 different new and mysterious syntaxes (syntaxi?) to do what can already be done. Languages are to communicate, not to be some seekrit magicka.

    Rant off now. :)
     
    Mr-Mechanical and lordofduct like this.
  13. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    7,552
    Oh, so it actually wasn't an array.

    It was a uint3 from unity with an indexed getter:
    https://docs.unity3d.com/Packages/com.unity.mathematics@0.0/api/Unity.Mathematics.uint3.html

    OK, so it's likely an API related thing going on here. Sort of explains it more.

    Totally agree though, the lambda method definitions annoy me. I guess it saves keystrokes? But I mean... really?
     
    Mr-Mechanical likes this.
unityunity