The basis of each computer operation is binary logic. Zeros and ones are transformed with simple operations into zeros and ones again. FileMaker is hiding its binary roots very successfully. Only now and then the user can catch a glimpse of it. Some custom functions use binary elements, e. g. Get( ActiveModifierKeys ). And of course you can build logical expressions for If and Case functions, using and/or/not operators, but every value is used as a single logical value. Every number different from 0 is taken as the logical value 1 (True). There is no difference between 1 and 999, for FileMaker both values are a logical 1.
Bitwise operation
I need bitwise opertion for my projects. You might run into the same situation, perhaps managing a collection of flags. Each bit represents a flag. To set flags or ready the status of one or more flags requires binary calculations. In that case you have to take whatever FileMaker has to offer and build the missing parts yourself.
I wrote some custom functions to provide basic logical operations: bit.not, bit.and, bit.or, and bit.xor. Two more functions complete the package; bit.isSet for bit testing and bit.bitset to display decimal numbers in their binary representation.
Binary representation
For a visual help to better understand binary operations, I wrote a custom function converting a decimal integer value into a string of 0s and 1s. For example, the numeric value 42 will be displayed as “101010″. The lowest bit corresponds with 20 = 1. The next bit is 21 = 2, then 22 = 4, 23 = 8, and so on.
Binary representation of positive values is easy to understand. But processing negative numbers becomes a little bit more complicated. In computer technology a value with its highest bit set could be seen as a negative value. In theory, there is no final highest bit. You could always add more bits to it. For a positive number these bits are 0s, for a negative values these bit are 1s. A value with every bit set equals -1, with every bit except the lowest bit set equals -2. It is very similar to the bit pattern for positive numbers, but for negative numbers the 0s and 1s are exchanged.
My function to display the bit pattern was original created to display positive values. To process negative values as well, I invert every bit, continue the function as usual, and switch 0s and 1s in the final result. Inverting every bit is done with the two’s complement: Xinv = −( X + 1)
CF bit.bitset( number; bitLength )
The function bit.bitset requires two parameters. The first is the value to be displayed in its binary representation. It should be an integer value. The second parameter defines the number of bits to returned. If it is empty, the bit length is calculated from the first parameter. The highest significant bit is defining the length. For positive values it is the the highest bit set (1), for negative numbers the highest bit not set (0).
bit.bitset( number; bitLength )
Let( [
_number = Int( GetAsNumber( number ) );
_inverse = ( _number < 0 );
_number = If( _number < 0;
-( _number + 1 );
// Else
_number
);
_bitLength = Case(
bitLength = "" and _number <= 1;
1;
bitLength = "" and _number > 1;
Truncate( Lg( _number ); 0 ) + 1;
// Else
Abs( GetAsNumber( bitLength ) )
);
_bitset = If( _number = 0 or _number = 1; // Break condition;
_number;
// Else
bit.toBitset( Div( _number; 2 ); "" ) & GetAsText( Mod( _number; 2 ) )
);
_length = Length( _bitset );
_result = If( _length < _bitLength;
text.repeat( "0"; _bitLength - _length ) & _bitset;
// Else
Right( _bitset; _bitLength )
)
];
If( _inverse;
Substitute( _result; [ "1"; "x" ]; [ "0"; "1" ]; [ "x"; "0" ] );
// Else
_result
)
)
Examples
bit.bitset( 42; "" ) ⇒ "101010" bit.bitset( 42; 8 ) ⇒ "00101010" bit.bitset( 3; 8 ) ⇒ "00000011" bit.bitset( -1; 8 ) ⇒ "11111111" bit.bitset( -42; 8 ) ⇒ "11010110"
Bit testing
My function for bit testing is based on a formula from Mikhail Edoshin found on his website. I included steps to ensure valid parameter values. The function will test only a single bit. For testing multiple bits at the same time you can use the logical functions discussed later in this article.
CF bit.isSet( number; bit )
The function bit.isSet expects two parameters. The first parameter is the decimal value of the bitset, the second the number of the tested bit. In information technology the lowest bit (on the right side of the value) is usually called bit 0, next to it is the bit 1, and so on. For my custom function however I decided to use the counting from everyday speech: 1, 2, 3, ….
bit.isSet( number; bit )
Let( [ _number = Int( GetAsNumber( number ) ); _bit = Abs( Int( GetAsNumber( bit ) ) ) ]; Mod( Div( _number; 2^( _bit - 1 ) ); 2 ) )
Examples
bit.isSet( 42; 1 ) ⇒ 0 (False) bit.isSet( 42; 2 ) ⇒ 1 (True)
Binary NOT
The bitwise NOT operation is switching all 1s with 0s and vice versa. The table shows this simple operation:
| Bit A | Result |
|---|---|
| 0 | 1 |
| 1 | 0 |
CF bit.not( number )
The function bit.not expects one integer value. The actual operation is very simple. I described it earlier in this article. Inverting every bits is done with the two’s complement: Xinv = −( X + 1).
bit.not( number )
Let( [ _number = Int( GetAsNumber( number ) ) ]; -( _number + 1 ) )
Binary AND
In the bitwise AND calculation the result is true when both bits are true. The table shows the various combinations.
| Bit A | Bit B | Result |
|---|---|---|
| 0 | 0 | 0 |
| 1 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 1 | 1 |
CF bit.and( number1, number2 )
The function bit.and takes two numbers and performs a bitwise AND operation. The lowest bits are processed first, then, with the remaining bits, the function is called recursively. This is done until the highest significant bit. This works well with two positive numbers or with a positive and a negative value. But some problems arise, when both parameters are negative. Then all their higher bits are 1s.
I do not want to go to much into details of binary calculations, but you should know, an AND operation can be replaced with NOT and OR operations. The same is true the other way around. An OR operation can be accomplished with NOT and AND operations. And this is my easy solution for the problem with two negative numbers. I treat the 1s as 0s and the other way around. I invert both parameters with the function bit.not and process them with the bit.or function. The result is inverted again to return the correct answer.
bit.and( number1; number2 )
Let( [
_n1 = Int( GetAsNumber( number1 ) );
_n2 = Int( GetAsNumber( number2 ) )
];
Case(
( _n1 < 0 ) and ( _n2 < 0 );
bit.not( bit.or( bit.not( _n1 ); bit.not( _n2 ) ) );
( ( _n1 = 0 ) or ( _n1 = -1 ) ) and ( ( _n2 = 0 ) or ( _n2 = -1 ) ); // Break condition
_n1 and _n2;
// Else
bit.and( Div( _n1; 2 ); Div( _n2; 2 ) ) * 2 + ( Mod( _n1; 2 ) and Mod( _n2; 2 ) )
)
)
Examples
bit.and( 42; 27 ) ⇒ 10 (False) bit.and( -27; -43 ) ⇒ -59
Problems when creating function
Because this function calls bit.or and bit.or itself calls bit.and, you might run into some problems, when you create the custom functions in your database. First create an empty function bit.or. Just define the name and the two parameters, but keep the function body empty. Now create function bit.and with name, parameters and code in the function body. Finally open the function bit.or again and insert the code into the body.
Binary OR
In the bitwise OR calculation the result is true when both bits are true. The table shows the various combinations.
| Bit A | Bit B | Result |
|---|---|---|
| 0 | 0 | 0 |
| 1 | 0 | 1 |
| 0 | 1 | 1 |
| 1 | 1 | 1 |
CF bit.or( number1, number2 )
Like the bit.and the function bit.or takes two numberic parameters. With them it performs a bitwise OR operation. Again this is done recursively, starting with the lowest bits up to the highest significant bit. Actually, only when both parameters have positive values this is executes in bit.or. If at least one parameter is negative, the calculation is transferred to the function bit.and with inverted (bit.not) parameters. The returned value is inverted again to get the correct answer.
bit.or( number1; number2 )
Let( [
_n1 = Int( GetAsNumber( number1 ) );
_n2 = Int( GetAsNumber( number2 ) )
];
Case(
( _n1 < 0 ) or ( _n2 < 0 );
bit.not( bit.and( bit.not( _n1 ); bit.not( _n2 ) ) );
( ( _n1 = 0 ) or ( _n1 = -1 ) ) and ( ( _n2 = 0 ) or ( _n2 = -1 ) ); // Break condition
_n1 or _n2;
// Else
bit.or( Div( _n1; 2 ); Div( _n2; 2 ) ) * 2 + ( Mod( _n1; 2 ) or Mod( _n2; 2 ) )
)
)
Examples
bit.or( 42; 27 ) ⇒ 59 bit.or( -27; -43 ) ⇒ -11
Binary XOR
With the binary functions AND, OR, and NOT you can build any other binary operation. I like to demonstrate this with the XOR operation. XOR sets a bit when exactly on parameter bit is set (1). If both bits are 0 or both bits are 1, then the result is 0. The table shows the various combinations.
| Bit A | Bit B | Result |
|---|---|---|
| 0 | 0 | 0 |
| 1 | 0 | 1 |
| 0 | 1 | 1 |
| 1 | 1 | 0 |
CF bit.xor( number1; number2 )
XOR can be build with AND, OR, and NOT: A XOR B = ( A AND NOT B ) OR ( NOT A AND B )
This can easily be done in a custom function bit.xor:
bit.xor( number1; number2 )
bit.or( bit.and( number1; bit.not( number2 ) ); bit.and( bit.not( number1 ); number2 ) )
Examples
bit.or( 42; 27 ) ⇒ 49 bit.or( -27; -43 ) ⇒ 48
Conclusion
Now you have all basic binary functions. You can use and combine them to build more complex operations, starting with NOR (NOT OR) and NAND (NOT AND). Of course, for really complex operations like a flip-flop, you have to integrate a trigger signal. Perhaps the timer trigger in FileMaker 10 could be helpful.