Bitwise operations, IEEE 754 floating point and ASCII
Bit masks with AND/OR/XOR/NOT, the recipe for encoding and decoding IEEE 754 single precision, and ASCII character codes. Each is a guaranteed question on the midterm.
On the previous page you read bits as integers. This page has three new things: logic operations on bits (masks), IEEE 754 for numbers with fractions, and ASCII for characters. All three are solved with a recipe. Whoever knows the recipe gets full marks.
Logic operations (bitwise operations)
These operations are done separately on the bits in the same position of two numbers. There is no carry, the bits do not affect each other.
| A | B | A AND B | A OR B | A XOR B | NOT A |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 1 |
| 0 | 1 | 0 | 1 | 1 | 1 |
| 1 | 0 | 0 | 1 | 1 | 0 |
| 1 | 1 | 1 | 1 | 0 | 0 |
1100 1001 (0xC9)
AND 1111 0000 (0xF0)
---------
1100 0000 (0xC0) the low 4 bits were cleared
Bit mask
The second number chosen to change or read some bits of a number is called a mask. Which operation you choose depends on what you want to do:
| Goal | Operation | Mask rule | Example, X = 0x93 = 1001 0011 |
|---|---|---|---|
| Make bits 0 (clear) | AND | the bits to clear are 0, the others 1 | X AND 0x0F = 0x03 |
| Make bits 1 (set) | OR | the bits to set are 1, the others 0 | X OR 0x0F = 0x9F |
| Flip bits (toggle) | XOR | the bits to flip are 1, the others 0 | X XOR 0x0F = 0x9C |
| Test a bit | AND | only that bit is 1 | X AND 0x08 = 0 → bit 3 = 0 |
A way to remember: with AND, 0 kills; with OR, 1 creates; with XOR, 1 flips. In AND the places where the mask is 1, and in OR and XOR the places where it is 0, leave the number untouched.
TIP · Bit numbers. Bits are numbered from the right, starting at 0. The mask of bit is : bit 0 = 0x01, bit 3 = 0x08, bit 4 = 0x10, bit 5 = 0x20, bit 7 = 0x80.
The LC-3 has only AND and NOT (week 7). You build OR with DeMorgan: .
Numbers with fractions: IEEE 754
Integer representations cannot write 3.75 or . For this the binary form of scientific notation is used: . IEEE 754 single precision (float in C) consists of 32 bits:
| S | Exponent | Fraction |
|---|---|---|
| 1 bit | 8 bits | 23 bits |
| sign (0 = +) | (bias) | the bits after the point |
The leading 1 is the same in every normalized number, so it is not written (hidden bit). This way 24 bits of precision fit in 23 bits. The exponent is stored shifted by 127 (biased) so that negative powers can be written too.
Special cases:
| Exponent | Fraction | Meaning |
|---|---|---|
| 0 | 0 | ±0 |
| 0 | ≠ 0 | denormal: |
| 255 | 0 | ±∞ |
| 255 | ≠ 0 | NaN |
Converting the fractional part to binary
Multiply the fraction by 2, write the integer part (0 or 1), and continue with the fractional part. Stop when the fraction is 0. The bits you wrote are read from top to bottom.
0.375 × 2 = 0.75 → 0
0.75 × 2 = 1.5 → 1
0.5 × 2 = 1.0 → 1 (stop)
0.375 = 0.011
Encode recipe (number → bits)
Worked example: 6.125
1) sign: positive → S = 0
2) 6 = 110
3) 0.125: ×2=0.25→0, ×2=0.5→0, ×2=1.0→1 → .001
6.125 = 110.001
4) normalize: 110.001 = 1.10001 × 2^2 (point moves 2 to the left)
5) exponent = 2 + 127 = 129 = 1000 0001
6) fraction = 10001 and 18 zeros
7) 0 10000001 10001000000000000000000
0100 0000 1100 0100 0000 0000 0000 0000 = 0x40C40000
Worked example: -2.5
2.5 = 10.1 = 1.01 × 2^1
S = 1, exponent = 128 = 1000 0000, fraction = 01000...
1 10000000 01000000000000000000000 = 1100 0000 0010 0000 ... = 0xC0200000
Decode recipe (bits → number)
Worked example: 0x41F40000
1) 0100 0001 1111 0100 0000 0000 0000 0000
2) S = 0 | exponent = 1000 0011 | fraction = 1110100...
3) exponent = 131 → e = 131 - 127 = 4
4) significand = 1.11101
5) 1.11101 × 2^4 = 11110.1 (point moves 4 to the right)
6) 11110 = 30, .1 = 0.5 → 30.5
Worked example: 0xC1100000
1100 0001 0001 0000 ... → S = 1 | exponent = 1000 0010 = 130 → e = 3 | fraction = 001...
1.001 × 2^3 = 1001 = 9 → -9
PITFALL · field boundaries do not line up with hex digits. The first bit of 0x41 is S, the next 7 bits are the start of the exponent; the last bit of the exponent is the first bit of the next hex digit. First write the 32 bits on one line, then draw the split as 1 | 8 | 23.
PITFALL · four mistakes that lose points in IEEE. (1) Forgetting to add or subtract 127. (2) Writing the hidden 1 into the fraction. (3) Filling the fraction from the right: the bits start right after the point, and the rest is padded with 0 on the right. (4) When converting to hex, keeping S separate and breaking the grouping in fours.
Not every decimal number can be written exactly
The binary expansion of 0.1 repeats forever (0.0001100110011…). Because it does not fit in 23 bits, it is rounded. In C, comparisons like 0.1f + 0.2f == 0.3f are dangerous for this reason. Fractions whose denominator is a power of 2 (1/2, 3/8, 5/16) are written exactly.
ASCII: characters
Every character that comes from the keyboard or goes to the screen is a number. ASCII (American Standard Code for Information Interchange) maps each character to an 8-bit code (actually 7 bits are enough, the top bit is 0).
| Character | Hex | Decimal |
|---|---|---|
| NUL (end of string) | x00 | 0 |
| space | x20 | 32 |
| ‘0’ … ‘9’ | x30 … x39 | 48 … 57 |
| ‘A’ … ‘Z’ | x41 … x5A | 65 … 90 |
| ‘a’ … ‘z’ | x61 … x7A | 97 … 122 |
| line feed (LF) | x0A | 10 |
Two properties you need to know:
- Value of a digit = code - x30. ‘7’ = x37 → 7. The reverse: 3 + x30 = x33 = ‘3’.
- Upper and lower case letters differ only in bit 5 (x20 = 32). ‘M’ = x4D = 0100 1101, ‘m’ = x6D = 0110 1101.
| Conversion | Operation |
|---|---|
| convert to lowercase | OR x20 |
| convert to uppercase | AND xDF |
| switch the case | XOR x20 |
The word “CAT” sits in memory as x43 x41 x54. In the LC-3, a string written with .STRINGZ puts each character in one word (16 bits) and adds x0000 at the end (week 10).
Common mistakes
| Wrong idea | The truth |
|---|---|
| OR to erase a bit | Erasing is done with AND, with that bit 0 in the mask |
| XOR = OR | 1 XOR 1 = 0, 1 OR 1 = 1 |
| Write the hidden 1 into the fraction | 1.10101 → fraction 10101 |
| The exponent field = e | The field = e + 127 |
| 0.1 can be written exactly | Infinite repetition, it is rounded |
| Exponent 255 = a very large number | A special value: ∞ or NaN |
| The value of the character ‘7’ is 7 | x37 = 55; subtract x30 to get 7 |
| The difference between ‘A’ and ‘a’ is x10 | x20 (bit 5) |
Test yourself
Exam-style questions. Solve them on paper first, then reveal the answer.
Compute 0x6C AND 0x3A, 0x6C OR 0x3A and 0x6C XOR 0x3A (8-bit). Give hex results.
Show answer
0x6C = 0110 1100, 0x3A = 0011 1010.
AND (1 if both are 1): 0010 1000 = 0x28. OR (1 if at least one is 1): 0111 1110 = 0x7E. XOR (1 if they differ): 0101 0110 = 0x56.
What is NOT 0x0F0F for a 16-bit value?
Show answer
NOT flips every bit: in hex each digit becomes . 0→F, F→0: 0xF0F0.
Let S = 0xB6. Give the operation and mask to (a) set bit 6, (b) clear bit 2, (c) toggle the low 4 bits. Give each result in hex.
Show answer
S = 1011 0110.
(a) Set = OR, in the mask only bit 6 = 1: 0100 0000. The result is 1111 0110 = 0xF6.
(b) Clear = AND, in the mask only bit 2 = 0: 1111 1011 = 0xFB. The result is 1011 0010 = 0xB2.
(c) Toggle = XOR, the low 4 bits are 1: 0000 1111. The result is 1011 1001 = 0xB9.
How can you test whether bit 4 of X = 0x5D is 1? What is the result?
Show answer
To test, use AND with a mask in which only that bit is 1. 0101 1101 AND 0001 0000 = 0001 0000. The result is not zero, so bit 4 = 1.
Encode 10.75 in IEEE 754 single precision. Give the final hex value.
Show answer
10 = 1010, 0.75 = .11 → 1010.11 = 1.01011 × 2^3.
S = 0. Exponent = 3 + 127 = 130 = 1000 0010. Fraction = 01011 and 18 zeros.
0 10000010 01011000000000000000000 → 0100 0001 0010 1100 0000 ... = 0x412C0000.
Encode -0.8125 in IEEE 754 single precision. Give the final hex value.
Show answer
0.8125 × 2 = 1.625 → 1; 0.625 × 2 = 1.25 → 1; 0.25 × 2 = 0.5 → 0; 0.5 × 2 = 1.0 → 1. So 0.1101.
Normalize: 1.101 × 2^-1. S = 1, exponent = -1 + 127 = 126 = 0111 1110, fraction = 101 and zeros.
1 01111110 10100... → 1011 1111 0101 0000 ... = 0xBF500000.
What decimal number does the IEEE 754 single-precision value 0x42290000 represent?
Show answer
0100 0010 0010 1001 0000 ... → S = 0 | exponent = 1000 0100 = 132 | fraction = 0101001...
E = 132 - 127 = 5. 1.0101001 × 2^5 = 101010.01 = 32 + 8 + 2 + 0.25 = 42.25.
What decimal number does 0xC0A80000 represent in IEEE 754 single precision?
Show answer
1100 0000 1010 1000 ... → S = 1 | exponent = 1000 0001 = 129 → E = 2 | fraction = 0101...
1.0101 × 2^2 = 101.01 = 5.25. S = 1 → -5.25.
Which value is represented by 0x7F800000 in IEEE 754 single precision?
Show answer
0 11111111 000...0: the exponent field is 255 (all ones) and the fraction is 0 → infinity. Because S = 0 it is positive infinity. If the fraction were nonzero, it would be NaN (not a number).
The ASCII code of 'q' is 0x71. Which bitwise operation and mask convert it to 'Q'? Give the result.
Show answer
Upper and lower case letters differ only in bit 5 (0x20). To get 'Q', clear bit 5: AND, with bit 5 = 0 in the mask: 1101 1111 = 0xDF. 0111 0001 AND 1101 1111 = 0101 0001 = 0x51 = 'Q'.
A program reads the character '7' from the keyboard (ASCII). Which value must be subtracted to get the integer 7?
Show answer
Digits are consecutive in ASCII: '0' = 0x30, '1' = 0x31, …, '9' = 0x39. '7' = 0x37, 0x37 - 0x30 = 7. In LC-3 programs this is always what is done to convert a digit coming from the keyboard to a number (weeks 7-8 and 10).
Is 0.1 exactly representable in IEEE 754 single precision? Explain briefly.
Show answer
The binary expansion of 0.1 repeats forever: 0.000110011001100… It does not fit in a 23-bit fraction, so it is rounded. The value actually stored is 0.10000000149011612 (0x3DCCCCCD). Fractions whose denominator is a power of 2 (0.5, 0.25, 0.375) are written exactly.
Verification: The bit operations were computed with node; the IEEE 754 encodings were verified at the bit level with DataView.setFloat32/getFloat32; the ASCII codes were checked with charCodeAt (verify/SEN2201/02.mjs).