cs3843 syllabus lecture notes programming assignments recitations Logic Operations in C a && b Logical and. If a is non-zero and b is non-zero, result is 1; otherwise, 0. a || b Logical or. If either a is non-zero or b is non-zero, result is 1; otherwise, 0. !a Logical not. If a zero, result is 1; otherwise, 0. Bit-Level Logic Operations in C These operations are performed on each bit position rather than considering whether the operand (as a whole) is zero or non-zero. a&b a|b ~a Bit-wise and. Each bit position is examined. If corresponding bits in a and b are both 1, the bit in the corresponding position for the result is set to 1; otherwise, it is set to 0. Bit-wise or. Each bit position is examined. If corresponding bits in a and b are both 0, the bit in the corresponding position for the result is set to 0; otherwise, it is set to 1. Bit-wise not. Each bit position in a is examined. If the bit is 1, the bit in the corresponding position for the result is set to 0; otherwise, it is set to 1. De Morgan's Laws 1. "not (a and b)" is the same as "(not a) or (not b)" 2. "not (a or b)" is the same as "(not a) and (not b)" homework a 00 00 08 08 b 00 03 03 00 set up a && b 0 0 1 0 a || b 0 1 1 1 !a 1 1 0 0 The table below shows the values in HEX. a b a & b a | b ~a a && b 0000 0000 0000 0000 FFF 0000 F 0000 0300 0000 0300 FFF 0000 F 0802 0307 0002 0B07 F7F 0001 D ABCD 2345 ? ? ? 0001 a || b 0000 ! a 0001 0001 0001 0001 0000 0001 0000 Is there an easy way for us to bit-wise not a hexadecimal number? x ~x x ~x 0 F 8 7 1 E 9 6 2 D A 5 3 C B 4 4 B C 3 5 A D 2 6 9 E 1 7 8 F 0 1. Use De Morgan's Laws to rewrite each of the following: 1.1 ! (X != 5 && Y == 'N') 1.2 ! (X == 100 || Z == 200) 2. Suppose someone requests all students except those having either a GPA < 3.0 or PAYMENT_HOLD = 'Y'. How do you write the condition for what to accept? Shifting Operations C supports bitwise shifts: value << k Left shift. Shifts the value to the left k bits, padding with k 0s on the right side. Value (binary) value << 2 value << 4 value >> 4 (logical) 0110 0111 1001 1100 0111 0000 0000 0110 value >> 4 (arithmetic ) 0000 0110 1001 0101 value >> k Right shift. Shifts the value to the right k bits; however, the padding on the left side is implementation dependent. It either always pads the left side with k 0s or it pads the left side with whatever is in the leftmost position of value. The later is known as an arithmetic right shift (i.e., propagating the sign). The former (always padding with k 0s) is known as a logical right shift. Multiplying Integers By Powers of 2 On most machines, addition and shifting is at least ten times faster than integer multiplication. Most compilers will attempt to optimize multiplications by constants with equivalent shifts and additions. Note: When multiplying by a variable (instead of a constant), the compiler would generate a multiplication. 0101 0100 0101 0000 0000 1001 1111 1001 How would you force the value to be a logical right shift? It would be easy for a compiler to replace iVal * 2 with iVal << 1 iVal * 4 with iVal << 2 So, for a constant equivalent to 2 k: iVal * 2k is the same as iVal << k Value (decimal) 24 100 -3 Value (binary with 16 bits) 00000000 00011000 00000000 01100100 11111111 11111101 multiplying by 4 value << 2 00000000 01100000 00000001 10010000 11111111 11110100 multiplying by 8 value << 3 00000000 11000000 00000011 00100000 11111111 11101000 To be faster than multiplication, how would you multiply iVal * 5? For w=8, 5 = (0000 0101) Using shifts, adds, and/or subtracts, what could we do? ? To be faster than multiplication, how would you multiply iVal * 7? ? Dividing by Powers of 2 On most machines, shifting is more than 30 times faster than integer division. With integer division, our quotient (result) is an integer without a value to the right of a decimal point. (e.g., 15 / 4 is 3) For positive values, dividing by a power of 2 is much faster using shifting. With integer division, the result is truncated. 5/2 would be 2.5 for real arithmetic. truncating is 2 -5/2 would be -2.5 for real arithmetic, truncating is -2. Value (decimal) 24 100 Value (binary with 16 bits) 00000000 00011000 00000000 01100100 dividing value >> 00000000 00000000 by 4 2 00000110 00011001 dividing value >> 00000000 00000000 by 8 3 00000011 00001100 Other than dividing by powers of 2, can division be replaced by a faster combination of shifts and adds? Except in rare cases, no. Truncating can lead to some interesting compiler-generated assembly language code. We will see that when we look at assembly language later in the course. Bit Map A bit map is a contiguous list of bits where each bit has a meaning. If it needs <= 32 bits, we will use a unsigned long to represent it. Suppose there are 30 students in a class. Each bit position could represent a student. We might have the following bit maps: ulFrBitMap (FR 14788020) 00010100 01111000 10000000 00100000 ulSoBitMap (SO 0A814448) 00001010 10000001 01000100 01001000 ulJrBitMap (JR 01042813) 00000001 00000100 00101000 00010011 ulSrBitMap (SR 00021304) 00000000 00000010 00010011 00000100 Suppose we also ulGradeABitMap 00001001 ulMajorCSBitMap 00001000 have these bit maps: (A 05580F3C) 10011000 00001111 00111100 (CS 08914A29) 10010001 01001010 00101001 Setting a Bit Position in a Bit Map To set a bit (turn it ON) in a bit map in position k (from the right): ulBitMap |= 1 << k; unsigned long ulBitMap = 0x00000000; int k = 1; int bBit = 0; ulBitMap |= 1 << k; // set a bit it position k printf("after setting position %d, bit map is %08X\n" , k, ulBitMap); k = 5; ulBitMap |= 1 << k; // set a bit it position k printf("after setting position %d, bit map is %08X\n" , k, ulBitMap); k = 4; ulBitMap |= 1 << k; // set a bit it position k printf("after setting position %d, bit map is %08X\n" , k, ulBitMap); k = 0; ulBitMap |= 1 << k; // set a bit it position k printf("after setting position %d, bit map is %08X\n" , k, ulBitMap); k = 20; ulBitMap |= 1 << k; // set a bit it position k printf("after setting position %d, bit map is %08X\n" , k, ulBitMap); k = 30; ulBitMap |= 1 << k; // set a bit it position k How can we determine students (who are in the class) who are either Jr or Sr? ? How can we determine students who are not seniors? ? How can we determine students who are CS majors, seniors, and are making an A? ? How can we determine students who are not CS majors, but they are making an A? ? printf("after setting position %d, bit map is %08X\n" , k, ulBitMap); //Output after after after after after after Clearing A Bit Position in a Bit Map To clear a bit (turn it OFF) in a bit map in position k (from the right): ulBitMap &= ~(1 << k); What did that do? setting setting setting setting setting setting position position position position position position 1, bit map is 00000002 5, bit map is 00000022 4, bit map is 00000032 0, bit map is 00000033 20, bit map is 00100033 30, bit map is 40100033 // clearing position k k = 4; ulBitMap &= ~(1 << k); printf("after clearing position %d, bit map is %08X\n" , k, ulBitMap); k = 1; ulBitMap &= ~(1 << k); printf("after clearing position %d, bit map is %08X\n" , k, ulBitMap); //Output after clearing position 4, bit map is 40100023 after clearing position 1, bit map is 40100021 Accessing a Bit Position in a Bit Map To access a bit in a bit map at position k: ulBit = ulBitMap & (1 << k); Note that the actual value of ulBit will be either 0 or non-zero. If position 3 is ON and we execute: ulBit = ulBitMap & (1 << 3); The value of ulBit will be 8. If we want the value to be 0 or 1 instead, we can also logical and it with 1. bBit = (ulBitMap & (1 << k) ) && 1; Exclusive OR (XOR) The exclusive or operator means "one or the other but not both". C provides a bit level xor using the ^ symbol. // accessing a bit at a position ulBitMap = 0x401002A9; k = 2; bBit = (ulBitMap & (1 << k)) && 1; printf("for bit position %d, bit map is %08X, bit is %d\n" , k, ulBitMap, bBit); k = 3; bBit = (ulBitMap & (1 << k)) && 1; printf("for bit position %d, bit map is %08X, bit is %d\n" , k, ulBitMap, bBit); //Output for bit position 2, bit map is 401002A9, bit is 0 for bit position 3, bit map is 401002A9, bit is 1 a 0 0 1 1 b 0 1 0 1 a and b 0 0 0 1 a or b 0 1 1 1 a xor b 0 1 1 0 // toggle a bit using exclusive or Suppose we want to toggle a bit in position k. If it was 1, it should be toggled to 0. If it was 0, it should be toggled to 1. A toggle in C is: ulBitMap ^= 1 << k; ulBitMap = 0x401002A9; k = 10; ulBitMap ^= 1 << k; printf("after toggling position %d, bit map is %08X\n" , k, ulBitMap); k = 5; ulBitMap ^= 1 << k; printf("after toggling position %d, bit map is %08X\n" , k, ulBitMap); k = 10; ulBitMap ^= 1 << k; printf("after toggling position %d, bit map is %08X\n" , k, ulBitMap); //Output after toggling position 10, bit map is 401006A9 after toggling position 5, bit map is 40100689 after toggling position 10, bit map is 40100289