This is the first of a two-part post on how to use bitwise functions in the ShapeSheet. The main driver for this is to make it easier to build complex, but maintainable shapes enabling you to quickly swap between states, both in terms of behaviour and visual appearance.
Before I get into the ‘how’, in the next post, I thought I’d deal with the ‘what’ in this one. If you’re already familiar with bitwise operations then feel free to skip to the next post – if not, then don’t worry, the logic here is pretty simple once you understand what’s going on…
Binary
Positional number systems have different bases. What does that mean? Well, a positional number system is one where the individual digits (or characters) derive their value from being a multiple of the position in which they sit within the overall number. The base, on the other hand, is the greatest unique digit that the system supports.
If you look at decimal, this has a base of ten (0-9 unique digits) and so you can calculate the value of a digit by multiplying it by the base (10), with an exponent of the digit’s position. In other words ten to the power of the position. For example, the number 1066 is really: one thousand (1 x 103) + no hundreds (0 x 102) + six tens (6 x 101) + six ones (6 x 100)
Binary has a base of two, which means that a digit can be either 1 or 0 (ie two unique digits). So the binary representation of same number, 1066, is one 1024 (1 x 210), one 32 (1 x 25), one 8 (1 x 23) and one 2 (1 x 21) – all the rest are zero:
The boolean nature of binary, where by any single position can be interpreted as TRUE or FALSE, means that you can look at a number and identify whether a particular position is in use or not, and any combination of these positions will give you a unique value. For example, the decimal number of 12 can only be made up of one 8 and one 4. Likewise the number 13 can only be one 8, one 4 and one 1.
Flags
Given the unique qualities of binary positions, if you then assign some meaning to those positions you have the basis for identifying a series of boolean variables and these are called ‘Flags’. For example, thinking about a drinks machine you might define the following flags:
Adding 2, 4 and 8 together will give you a value of 14 and this will see you receiving a white coffee with sugar and, as long as you have some logic to ensure that not all of your values are true at the same time, you now have some data that you can work with.
Bitwise functions and masks
So now you’ve got some meaningful flags, but clearly the key here is being able to easily identify which flags are true and which are not – this is where the Bitwise functions come in.
Visio has four bitwise functions:- BITAND, BITNOT, BITOR and BITXOR. With the exception of BITNOT they all work by comparing the binary representation of two numbers and returning a result based on the equality (or otherwise) of each bit.
Before I move on to look at each of the functions, there’s two other elements to think about – bit length and masks.
- Bit length - In computing a bit represents a single binary digit so the 1066 number above can be seen as 11 bits. A 16 bit representation of the same number would carry exactly the same value, but just be padded with five extra zeros to its left. All of the Visio bitwise functions deal in 16 bit binary numbers and it’s worth remembering this as it contrasts with other bitwise implementations such as JavaScript, which deals with 32 bit numbers.
- Masks – In order to find out whether a binary number contains a specific flag value, you’ll want to test for that by passing the correct flag. For example, if you want to know whether the above drink contains sugar, the only bit you’re interested in is the third one (from the right - 4) and therefore you want all of the other bits to be zero. These zeros are referred to as a mask – masking all of the information that you’re not interested in.
BITAND – compares two numbers and returns a result in which each bit is 1 where the respective bits in the input numbers were both 1 as well.
Here you can see that only the fifth bit in the first and the second numbers were true and so this is reflected in the (binary representation) of the returned value (16).
BITNOT – takes a single number and returns the inverse of it. This is somewhat similar to the NOT ShapeSheet function, where NOT(TRUE) = FALSE. In the BITNOT version all bits that were 1 become 0 and all bits that were 0 become 1.
BITOR – compares two numbers and returns a result in which each bit is 1 where the respective bits in either input number is 1 as well.
In this function you can see that if either or both of the top two numbers have a bit set to 1, the corresponding bit in the returned number is also set to 1.
BITXOR – compares two numbers and returns a result in which each bit is 1 where the respective bits in either, but not both input numbers is 1 as well. If you look at the same example (as the BITOR above) you’ll see how the returned number differs:
The thing to remember with each of these functions is that all of the comparison and result is done in binary, but both the input and returned values are in decimal.
Real world use
Well, all that theory is fine, but really what you want to do is check whether a flag exists in a given number, add a particular flag or remove a flag, so let’s look at how you’d go about that.
To check if a flag exists you can use the BITAND function. You don’t really care about the result since, as long as it’s greater than zero it’s TRUE, otherwise it’s FALSE :
=IF(BITAND(User.MyFlags,16)>0,
"Exists", "Does not exist")
To add a flag if it doesn’t exist already you can use the BITOR function in combination with a SETF function to push the value into the target cell:
=SETF(GetRef(User.MyFlags),
BITOR(GetVal(User.MyFlags),2))
Finally, to remove a flag you use both BITAND and BITNOT. For example to remove the 8 flag, you first wrap the 8 in a BITNOT function which returns a 16 bit number with all of the bits set to 1, apart from the fourth bit (8), which is set to zero. You then BITAND this with your original flags and the result is the the original flags but with the fourth bit set to zero, thereby ‘turning off’ that particular flag.
=SETF(GetRef(User.MyFlags),
BITAND(GetVal(User.MyFlags),BITNOT(8)))
So that wraps up the ‘what’ side of things. In the next post I’m going to look at ‘how’ you put these functions to work to help create more maintainable shapes. In the meantime, I’ve put together a Bitwise function test shape that you can use to experiment with: