Integers and Bit Operators in Core
You have probably heard it said that digital information is all made up out of zeros and ones. Integers are one type of data format that can be stored in this way. The integer data type is used to store whole numbers, whereas most Reaktor signals store decimal values using the floating point format. In core you you can choose whether a module uses the floating point or the integer format in the properties:
Float modules have circles for inputs and outputs, while integer modules have equal signs instead:
If you run a float signal into an integer input, the signal will be rounded to the nearest whole value. Running an integer signal into a float input will simply convert to float format. Both operations, however, take CPU and should be avoided when possible.
Integers are stored as base two (binary) numbers, similar to the base ten (decimal) system that we humans prefer. To understand how binary works, let’s first take a look at a decimal number, say 4,793, and see how we determine it’s value.
Each individual digit (the 4, the 7, the 9 and the 3) is multiplied by a power of 10 to get it’s ‘true’ value. For example, the 4 represents a value of 4*10^3, or 4,000. Meanwhile, the 7 represents a value of 7*10^2 (700), the 9 represents 9*10^1, (90), and the 3 is equal to 3*10^0, or 3. When you add all of these values together, you get 4,793.
Binary works in a similar way, except it only has two values (0 and 1) instead of the ten values that decimal has (0-9). So instead of multiplying each value by a power of ten, we instead multiply be a power of 2.
An 8-bit integer might look something like this: 11100100. Again, we multiply each value by a power of 2 determined by it’s position in the number. The rightmost value is multiplied by 2^0, or 1. The leftmost value is multiplied by 2^7, or 128. So the value of this 8-bit integer is equal to 128 + 64 + 32 + 4 = 228.
Don’t worry if this is a little confusing at first, thinking in terms of anything but base 10 math can be tricky.
HOW TO USE INTEGERS
You can use integers to save yourself a bit of CPU if you are careful. First off, you should avoid converting between signal types. A signal will automatically convert to the type required by the input of a module. Here’s an example:
I set the instrument containing this core cell to have 32 voices (Using a large number of voices with simple structures like this can help determine which programming method is more efficient). While the structures are nearly identical, the bottom one is using an integer to read from the array, while the top structure uses a float that must be converted into an integer.
The top structure uses 2.1% of my CPU, the bottom uses only 1.5%! The simple act of converting a float to an integer (or vice versa) can waste CPU if you aren’t careful. Sometimes, of course, converting is a necessity, but if you can avoid it, you should.
Okay, what are integers good for other than reading from arrays? Fast math! You can perform certain operations way faster using integers than you can using floats. These operations are called bit operators, and you can find them in core in the Built-in Module -> Bit folder.
There are six bit operators available to us: Bit AND, Bit OR, Bit XOR, Bit NOT, Bit <<, and Bit >>. Let’s go thru them one by one and see what they can do for us. I’ll mainly focus on the Bit AND, <<, and >> operators as I personally find them to be the most useful within the context of Reaktor programming.
Bit operators work by performing simple operations on integers by using their individual bits, which is why I covered the basic structure of an integer above.
The Bit AND module takes two integers as inputs. For each bit, the values of that bit for each integer are compared. If the bits both equal 1, the output integer also has that bit set to 1. Otherwise that bit is set to 0 in the output integer.
For example, 11100011 AND 10101010 = 10100010 – notice that only the bits that are set to one in both inputs are still set to one in the output value.
The Bit AND module can be used as a modulo operator under the right circumstances – whenever the ‘B’ input would be equal to 2^N-1, where N is any positive number. For example, we could continously read thru a 256 value array (with index value of 0-255) by simply adding a Bit AND module to our previous example like so:
In this structure, the 8 least significant bits of the botton input are all set to 1, while the rest are set to 0. If the integer were 16-bit, it would look like this – 0000 0000 1111 1111 (binary numbers are often split into groups of 4 for easier reading, and conversion to hexadecimal, or base 16 numbers).
Any signal sent to the top input will have all values above 255 shaved off, leaving the same result as a modulo by 255, only substantially faster.
You can even do something simple like
to determine if a signal is even or odd (1 = odd, 0 = even).
The Bit OR operator sets each bit in the output to 1 if either or both of the inputs has that same bit set to 1.
For example, 0011 0000 OR 0000 1100 = 0011 1100.
You can use integers to store several pieces of information. For example, each bit could represent a button that is off if the bit is equal to 0, and on if it is equal to 1. Using the OR command you can turn on bits, for example N OR 0000 1111 will make sure that the last 4 bits will be on, regardless of the value of N. I rarely if ever use the Bit OR module in Reaktor, but that is the most common purpose for which I have used the OR operator in the past.
The Bit XOR (‘exclusive or’) operator sets each bit in the output to 1 if either, but not both, of the inputs has that same bit set to 1.
For example, 1011 0001 XOR 1000 1101 = 0011 1100.
While the Bit OR command can be used to set individual bits to be on, the XOR can be used to toggle those bits (turn them off if they are on, and vice versa). For example N XOR 0000 1111, will toggle the last four bits of N, regardless of the value of N initially.
An interesting piece of computer history – back when I was in high school, programming in Turbo Pascal on a 486, I took a class on assembly language through the local college. In assembly language you can set the value of a register using the MOV command. For example MOV AX, 0 would set register AX to be equal to 0. However, you could also set a value to be equal to 0 by XOR’ing it against itself. XOR AX, AX would do this, and substantially faster than simply using the MOV command. I’m not sure of the reason behind this and I doubt that if it is still true but I always found it fascinating nonetheless.
Unlike the other operators, Bit NOT only has one input. The NOT operator simply inverts the value of each bit in the integer.
For example, 0011 1100 = 1100 0011.
BIT >> AND BIT <<
There are commonly referred to as bit-shifters, or left-shift and right-shift operators.
These modules can be used to multiply or divide by powers of 2. The << operator multiplies by a power of 2 (by shifting each bit to the left), and the << operator divides by a power of 2 by shifting each bit to the right.
For example, 0000 1111 << 2 = 0011 1100. You can see each bit in the original integer has been moved left by two bits. The result is that the value of the integer is multiplied by 2^2 , or 4.
Similarly, 0011 1100 >> 2 = 0000 1111.
The bottom input of each module determines the power of 2 to divide or multiply by. Again, using these operators is faster than normal multiply/divide operations.
Don’t worry if it all seems a little confusing at first, pretty much everybody finds bit operators to be a pretty obtuse method of programming, myself included. However, the speed benefits can often outweigh the difficulty. I rarely use bit operators until I’m trying to optimize something, personally.
If anything needs to be more clear, please let me know in the comments.