BitAnd(), BitClear(), BitLShift(), BitNot(), BitOr(), BitRShift(), BitSet(), BitTest(), BitXor()
All these functions manipulate bit values as 4-byte signed integers. Visual FoxPro has the full complement of AND, OR, NOT and XOR operations. In addition, we have simple and accessible SET, CLEAR, TEST as well as Left and Right SHIFT commands.For those of you who haven't had to slice and dice bits since school, a little base number review is in order here. Base 10, the decimal system, is the most common way of counting. From right to left, the digits are the ones place, the tens place, and the hundreds place. These can also be expressed as 10 to the zeroth power, 10 to the first power, and 10 to the second power. Base 2, or binary math, works the same way. The first place is the ones place, 2 to the zeroth. The second place (from the right) is the twos place, two to the first power. The progression continues—third place is four, two squared; fourth place is eight, two to the third. Let's try to make this into a table and see what happens:
Binary Place� |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
Holds the value� |
128 |
64 |
32 |
16 |
8 |
4 |
2 |
1 |
Decimal Value� |
Represented in Binary |
|||||||
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
2 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
3 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
1 |
4 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
16 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
127 |
0 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
128 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
255 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
Table 1: How decimal values (left column) are represented in binary.
It's hard to fit more than eight bits across the page in a table that makes sense—check out the sample Num2Bit.PRG, below, for a full 32-bit display. So when we're talking about the value 128, our computer is really thinking 10000000. And 127, not very different to us, is very different to the computer, which represents it internally as 01111111. So why would any human being in his right mind want to play with all these ones and zeroes? This was the question we asked our algebra teachers, and hardly ever received a satisfactory answer. Well, here, finally, we have an answer. Space. The final frontier. Uh, no, not that kind of space—space of the hard disk kind of space. What if you had to store six yes/no answers in a table? You could create a character field of four spaces, storing "Yes," "No," "Ya," "Nein," and "Nyet," but parsing and case-testing would probably be far too time-consuming. A logical field seems more efficient. But six logical fields take six bytes, perhaps not a major amount with multi-gigabyte hard drives in the hundreds of dollars, but millions of these bytes clog the lines of communication. Multiply these by a few dozen fields in the more complex tables and you have the formula for bogging down whatever system you choose in wasted I/O, sloshing padding characters around. In order to save space, many functions use individual bits within a byte to store simple yes/no information. For example, DBF headers store information on whether or not the file uses CDXs, memo fields, and whether the file is a DBC or not—all in byte 28. Divining this information is far easier thanks to the BIT functions. Other places where bit-splitting comes in handy are in deciphering the buttons pressed and modifier keys in MouseDown, MouseMove and related events, or in hacking SCX datestamp fields. Obviously, Microsoft has had these functions available to them internally for quite some time—it is great that they exposed them for us to use as well.A little terminology primer is needed here. If a bit contains a 1, it is said to be "set" or "on." If a 0 instead occupies that place, the location is said to be "cleared" or "off." The operations of setting these bits can therefore be referred to as "setting" and "clearing."All BIT functions operate on numbers in the range from 0 to �(2^31 - 1), using 31 bits in a total of four bytes. The 32nd bit (bit 31—remember, we started counting at zero) is reserved for the negative flag. If bit 31 is set on, the value is considered negative, and the value of the following bits is different from what they mean for positive values. Negative 1 is expressed as 32 bits set to 1, negative two as 31 one-bits followed by a zero, negative three as 30 one-bits followed by zero-one, and negative four as 30 one-bits followed by two zeroes. We seem to remember this as "two's complement math," but we've been out of school way too long to think this way. This must make some sense to the computer, but little to us. Our advice: Use caution when manipulating bit 31 or shifting bits left, which could change the sign of the number. Combining BIT functions with addition or subtraction under these conditions can lead to inaccurate results.
Usage |
nRetVal = BitAnd( nFirstArg, nSecondArg [, nThirdArg [ ... ] ] ) nRetVal = BitNot( nFirstArg ) nRetVal = BitOr( nFirstArg, nSecondArg [, nThirdArg [ ... ] ] ) nRetVal = BitXOr( nFirstArg, nSecondArg [, nThirdArg [ ... ] ] ) |
Parameter |
Value |
Meaning |
nFirstArg |
Numeric |
First numeric value to process, ranging from 0 to �(2^32 - 1). |
nSecondArg |
Numeric |
Value to process with the first numeric value, restricted to the same range. |
nThirdArg |
Numeric |
Value to process with the first two numeric values, restricted to the same range. |
� |
Numeric |
Up to 23 more values to process with the others, restricted to the same range. |
Usage |
nRetVal = BitClear( nValue, nBitPos ) nRetVal = BitSet( nValue, nBitPos ) lRetVal = BitTest( nValue, nBitPos ) |
Parameter |
Value |
Meaning |
nValue |
Numeric |
Value to process, ranging from 0 to �(2^32 - 1). |
nBitPos |
Numeric |
Bit position to modify or test. Ranges from 0 to 31. |
nRetVal |
Numeric |
The result is the numeric value of taking the supplied nValue and SETting or CLEARing the specified bit. |
lRetVal |
.T. |
Position nBitPos is set in nValue. |
.F. |
Position nBitPos is clear in nValue. |
Example |
* Num2Bit - return character representation of numeric bitmap LPARAMETERS number LOCAL I LOCAL lcRetString IF TYPE('number') <> "N" OR ABS(number) > 2^32 lcRetString = "Must be number -2^32 to +2^32" ELSE lcRetString = "" FOR I = 31 TO 0 STEP -1 lcRetString = lcRetString + IIF(BITTEST(number,I),"1","0") NEXT ENDIF RETURN lcRetString |
Usage |
nRetVal = BitRShift( nValue, nAmt2Shift ) nRetVal = BitLShift( nValue, nAmt2Shift ) |
Parameter |
Value |
Meaning |
nValue |
Numeric |
Value to shift, ranging from 0 to �(2^32 - 1). |
nAmt2Shift |
Numeric |
Number of bits to shift, positive or negative. |
nRetVal |
Numeric |
The numeric value from taking the supplied number and shifting the bitmap the specified amount to the right or left. |
******************************************************************** * Program....: STAMP2T6.PRG * Version....: 1.0 * Author.....: Ted Roche * Date.......: May 31, 1998 * Notice.....: Copyright � 1998 Ted Roche, Click for license. * Compiler...: Visual FoxPro 06.00.8093.00 for Windows * Abstract...: VERSION SIX AND LATER ONLY!!! * ...........: Simpler version of Stamp2DT written for HackFox3 and * ...........: also published in FoxPro Advisor magazine * Changes....: ******************************************************************** LPARAMETERS tnStamp #DEFINE SecondsMask 15 && 00001111 #DEFINE MinutesMask 63 && 00111111 #DEFINE HoursMask 31 && 00011111 #DEFINE DaysMask 31 && 00011111 #DEFINE MonthsMask 15 && 00001111 #DEFINE YearsMask 63 && 00111111 #DEFINE SecondsOffset 1 && Note this is a LEFT shift, not RIGHT #DEFINE MinutesOffset 5 #DEFINE HoursOffset 11 #DEFINE DaysOffset 16 #DEFINE MonthsOffset 21 #DEFINE YearsOffset 25 #DEFINE fMonth BITAND(BITRSHIFT(tnStamp,MONTHSOFFSET ),MONTHSMASK) #DEFINE fDay BITAND(BITRSHIFT(tnStamp,DAYSOFFSET ),DAYSMASK) #DEFINE fYear 1980+BITAND(BITRSHIFT(tnStamp,YEARSOFFSET ),YEARSMASK) #DEFINE fHour BITAND(BITRSHIFT(tnStamp,HOURSOFFSET ),HOURSMASK) #DEFINE fMinute BITAND(BITRSHIFT(tnStamp,MINUTESOFFSET),MINUTESMASK) #DEFINE fSecond BITAND(BITLSHIFT(tnStamp,SECONDSOFFSET),SECONDSMASK) IF TYPE("VERSION(5)") = "U" MESSAGEBOX("This routine only works with Visual FoxPro 6.x or later.") RETURN .F. ENDIF LOCAL ltReturn ltReturn = IIF(tnStamp = 0, {//::}, ; DATETIME(fYear, fMonth, fDay, fHour, fMinute, fSecond)) RETURN ltReturnThis is a really handy function to check the last date that form elements were modified. Open up an SCX file and issue the command:
BROWSE FIELDS platform, uniqueid, timestamp, realtime=stamp2t6(timestamp),; classname=LEFT(class,20), namebaseclass=LEFT(baseclass,20)This example requires some explanation. The TIMESTAMP field in SCX's and VCX's is composed of bitmaps of the year, month, day, hour, minute and second. In order to compress all this information into the smallest amount of space, the designers examined each of the values above, and calculated the amount of space each would take, in bits. The seconds field was trimmed on the right (using a BitRShift()) so the precision of this field is within two seconds, probably close enough. Then these values are mapped into a continuous range of 30 bits:
Value |
Range |
Number of Bits Required |
Starting Position |
Years since 1980 |
0 – 64 |
6 |
25 |
Months |
1 – 12 |
4 |
21 |
Day |
1 – 31 |
5 |
16 |
Hours |
0 – 23 |
5 |
11 |
Minutes |
0 – 59 |
6 |
5 |
Seconds |
even 0 – 58 |
5 |
0 |
By the way, this method of storing datetimes is archaic—the datetime data type will store the same amount of information in 20 percent less space, while providing a far better clue to the hapless programmer trying to decode the TIMESTAMP field. We hope we'll see the change to using datetime fields in a future VFP version. |
See Also |