Mathematics for Computing 201-N11 • CEGEP in Montreal
Corrected exercises: binary arithmetic and machine numbers (201-N11)
This is the English-language set of corrected exercises on binary arithmetic for Mathematics for Computing 201-N11, the concentration course taken at cégep in Montreal by students in computer science and computer technology programs. Part A is the mechanics: addition and its carry chains, subtraction by borrowing and then by adding a two's complement, multiplication and division by shifting with the trap of the logical against the arithmetic right shift, the two's complement wheel and its asymmetric range, and the reason a carry out and a signed overflow are two different diagnostics. Part B works at exam level: hexadecimal masks on a device status register, binary fractions and Q4.4 fixed point with an error that compounds, a 12-bit sensor whose readings are widened wrongly, and the IEEE 754 single precision format built and decoded bit by bit.
The thread running through the set: a bit pattern carries no meaning of its own. The adder performs the same operation whatever you intended, and every question about whether the result is right is a question about the reading you chose. That is why the same eight bits are 200 and −56, why the flag you must test changes with the declared type, and why widening a number is not the same operation for a signed and an unsigned value.
The traps named explicitly in the solutions: reading the successive multiplications of a fraction in the wrong direction, subtracting 65536 from a 12-bit field whose wheel has only 4096 patterns, treating the carry out as a general error flag, testing a two-flag mask against zero instead of against the mask, and expecting a float to hold every integer a 32-bit integer holds.
Self-checking setType your answers and check them question by question: the page tells you right or wrong before you open the solution. With an account, every answer right on the first try earns points, and your right answers stay filled in when you come back.
•Binary addition: 1+1=102, so write 0 and carry 1. The longest carry chain is all ones plus one, and it is what sets the delay of a ripple-carry adder.
•Subtracting is adding: a−b=a+(two’s complement of b), and the ninth bit that comes out is the 2n that the complement put in.
•Two's complement of x on n bits: invert every bit, add 1. The pattern obtained is 2n−x.
•Signed range on n bits: −2n−1 to 2n−1−1, asymmetric because zero has a single representation and sits on the positive side.
•Shift left by k multiplies by 2k. Shift right by k divides by 2k: logically for unsigned values, arithmetically, sign bit replicated, for signed ones.
•Carry out condemns an UNSIGNED result. Overflow condemns a SIGNED one: same sign in, opposite sign out. Neither implies the other.
•Widening a signed value means sign extension, copying the sign bit left. Filling with zeros turns a small negative number into a large positive one.
•Masks: OR to set bits, AND with the complement to clear them, XOR to toggle them. One hexadecimal digit is exactly four bits.
•A decimal fraction has a finite binary expansion only if its reduced denominator is a power of 2. So 0.1 never terminates.
•IEEE 754 single precision: 1 sign bit, 8 exponent bits biased by 127, 23 stored significand bits plus an implied leading 1. Integers are exact up to 224.
Part A: the mechanics (/50)
Exercise 1: Adding in binary, column by column
Binary addition has exactly four one-bit cases: 0+0=0, 0+1=1, 1+0=1, and 1+1=102, which writes a 0 and carries a 1 into the next column on the left. Everything else is that rule repeated. The layout below shows the weights of an 8-bit word above the columns, so that every answer can be checked in decimal without redoing the addition.
a) Add 101101102+001010112 on 8 bits. Fill in the carry row, then check your result in decimal.
b) Add 11112+12. What happens if the machine only has 4 bits for the result?
c) Add the three numbers 10112+11012+1112 in one column layout. What is different about the carries?
d) Add 100112+11012 and confirm the sum by weights rather than by decimal conversion.
e) A carry can travel across the whole word. Give the 8-bit pair that makes the chain as long as possible, and say what that costs a real adder circuit.
Show the solution
Answers
a)111000012=225=182+43
b)100002=16; on 4 bits the register holds 0000 and the fifth bit is the carry out
c)111112=31; a column of three ones can produce a larger carry
d)1000002=32
e)111111112+000000012=1000000002=256; the worst case sets the delay of a ripple-carry adder
a)
Work right to left. Column of weight 1: 0+1=1, no carry. Weight 2: 1+1=102, write 0 and carry 1. Weight 4: 1+0+1=102, write 0, carry 1. Weight 8: 0+1+1=102, write 0, carry 1. Weight 16: 1+0+1=102, write 0, carry 1. Weight 32: 1+1+1=112, write 1, carry 1. Weight 64: 0+0+1=1, no carry. Weight 128: 1+0+0=1. Result 111000012. Decimal check: 101101102=128+32+16+4+2=182 and 001010112=32+8+2+1=43, so the sum must be 225; and 111000012=128+64+32+1=225. Two independent routes agree, which is the whole point of writing the weights above the columns.
b)
11112=15 and adding 1 gives 16=100002, which needs five bits. On a 4-bit register the stored result is 0000 and the fifth bit leaves the adder as a carry out. The value 16 is not lost because the adder failed; it is lost because nobody kept the carry. This is the difference between the arithmetic, which is always right, and the storage, which has a width.
c)
10112=11, 11012=13, 1112=7, so the sum is 31=111112. Verify: weight 1 column has three 1s, that is 112, so write 1 and carry 1; weight 2 has 1+0+1 plus the carry, that is 112 again. With three operands a column can produce a carry of 2, not just 1, so a carry may have to jump two columns. That is exactly why hardware adds numbers two at a time rather than in one wide gulp.
d)
100112+11012. Align on the right: 100112 has weights 16+2+1 and 11012 has 8+4+1. Adding the weights gives 16+8+4+2+1+1=32, and 32=1000002. The posed addition gives the same: the two 1s of weight 1 make a carry into weight 2, which then meets 1+0, carries again, and the carry ripples all the way to weight 32. Checking by weights is the faster control here because the answer is a single power of two, which is instantly recognisable.
e)
The longest chain is 111111112+000000012: the carry born in the rightmost column is regenerated by every column it enters and only stops after leaving the leftmost one. The sum is 256=1000000002. In a ripple-carry adder each column has to wait for the previous carry, so the delay of the whole circuit is set by this worst case and grows with the word width. That is the reason processors use carry-lookahead adders, which compute the carries in parallel from the operand bits instead of passing them along.
Tick the exercises you have done or want to review: a free account, no password, keeps your ticks and your right answers from one visit to the next, tells you which chapter to tackle next and lets you ask for the exercise you are missing. Create your space, an email is enough.
Exercise 2: Subtracting: by borrowing, then by adding instead
There are two ways to subtract in binary. The school way borrows from the column on the left, exactly as in decimal except that a borrow is worth 2 and not 10. The machine way never subtracts at all: it adds the two's complement of the second operand, which is why a processor needs only one adder for both operations. Both give the same answer, and being able to move between them is what this exercise trains.
a) Compute 110102−10112 by borrowing. Check in decimal.
b) Redo the same subtraction on 8 bits by adding the two's complement of 10112. Show the complement step by step.
c) Compute 10000002−12 by borrowing. How many columns does the borrow cross?
d) On 8 bits, compute 000001012−000011012 by the complement method. Read the answer as a signed number.
e) In b) and d) a ninth bit came out of the adder. Explain why discarding it is correct here, and name the one case where it must not be discarded.
d)111100112 added to 000001012 gives 111110002, signed −8
e)Adding the complement adds 2n, which the dropped bit removes; in unsigned addition the ninth bit is a real carry out
a)
110102−10112, aligned on the right. Weight 1: 0−1 is impossible, borrow from weight 2. That column holds 1, so it becomes 0 and weight 1 becomes 102=2; then 2−1=1. Weight 2: now 0−1, borrow again from weight 4, which holds 0, so the borrow has to come from weight 8. Carrying the borrow through gives 1 at weight 2 and 1 at weight 4. Weight 8: 0−1 after the borrow, take from weight 16, giving 1. Result 11112. Decimal check: 110102=26, 10112=11, and 26−11=15=11112. Borrowing across a run of zeros is where mistakes happen, and the decimal check is what catches them.
b)
Two's complement of 000010112 on 8 bits: invert every bit to get 111101002, then add 1 to get 111101012. As an unsigned pattern that is 245, and 245=256−11, which is the definition of the complement on 8 bits. Now add: 000110102+111101012=26+245=271. In binary 271=1000011112, nine bits. Keep the low eight: 000011112=15. Same answer as a), obtained without a single borrow.
c)
10000002=64 and 12=1, so the answer is 63=1111112. The borrow starts at weight 1 and finds a zero in every column until weight 64, so it crosses six columns and turns each zero it passes into a 1. This is the mirror image of exercise 1e): subtracting 1 from a power of two is the worst case for borrows, just as adding 1 to all ones is the worst case for carries.
d)
Two's complement of 000011012 (that is 13): invert to 111100102, add 1 to get 111100112=243=256−13. Add: 000001012+111100112=5+243=248=111110002. Read as signed 8-bit, the leading bit is 1, so the value is 248−256=−8. And indeed 5−13=−8. Notice that the adder did nothing special: it added two unsigned patterns. The minus sign only appeared when we chose to read the result as signed.
e)
The complement of x on n bits is 2n−x, so adding it adds an extra 2n that lands entirely in bit n, outside the word. Dropping that bit subtracts the 2n back out, which is why the low eight bits are the correct difference. The case where it must not be dropped is unsigned addition: there the ninth bit is a genuine carry out saying the true sum did not fit, and ignoring it turns 300 into 44. Same bit, opposite meaning, decided by the type you declared and not by the hardware.
Exercise 3: Multiplying, dividing, and what a shift really does
Long multiplication in binary is easier than in decimal, because each digit of the multiplier is 0 or 1: every partial product is either the multiplicand shifted, or nothing at all. Multiplying by a power of two is therefore a pure shift, and dividing by one is a shift the other way. The subtlety, and the whole point of parts d) and e), is that a shift to the right is not one operation but two.
a) Compute 10112×1012 by long multiplication. Check in decimal.
b) Compute 1101102×1002 without multiplying. Justify the method.
c) Divide 101101002 by 10002. Give the quotient and the remainder, and say where the remainder is visible in the bit pattern.
d) On 8 bits, 111100002 is shifted right by two places. Give the result for a logical shift and for an arithmetic shift. If the pattern is a signed number, which of the two is division by 4?
e) A processor multiplies an m-bit number by an n-bit number. How many bits does the product need in the worst case? Prove it.
Show the solution
Answers
a)10112+1011002=1101112=55=11×5
b)110110002=216=54×4: a shift of two places
c)Quotient 101102=22, remainder 1002=4, the three bits that fell off
d)Logical 001111002=60; arithmetic 111111002=−4=−16/4, the division
e)m+n bits: (2m−1)(2n−1)<2m+n
a)
10112×1012. The multiplier 1012 has ones at weights 1 and 4, so there are two partial products: 10112 itself, and 10112 shifted two places, that is 1011002. Add them: 10112+1011002=11+44=55=1101112. Decimal check: 10112=11, 1012=5, 11×5=55, and 1101112=32+16+4+2+1=55. There is no multiplication table to remember in binary, only shifting and adding.
b)
1002=4=22, so multiplying by it shifts the pattern two places to the left and appends two zeros: 1101102 becomes 110110002. The justification is positional: shifting left by k multiplies every weight by 2k, hence the whole number by 2k. Check: 1101102=32+16+4+2=54 and 110110002=128+64+16+8=216=54×4. This is why a compiler replaces a multiplication by a constant power of two with a shift.
c)
10002=8=23, so shift right by three: 101101002 becomes 101102, and the three bits pushed off the right end are 1002. Quotient 101102=22, remainder 1002=4. Check: 101101002=180 and 180=8×22+4. The remainder is not computed, it is simply the bits that fell off, which is the fastest possible modulo by a power of two: mask with 2k−1.
d)
A logical shift feeds zeros in on the left: 111100002 becomes 001111002=60. An arithmetic shift replicates the sign bit: it becomes 111111002, which read as signed is 252−256=−4. If the pattern is signed, 111100002 is 240−256=−16, and −16/4=−4: the arithmetic shift is the division, the logical one turns a negative number into a large positive one. Choosing the wrong shift on a signed value does not produce a small rounding error, it produces a number of the wrong sign and roughly the right magnitude, which is exactly the kind of bug that survives testing.
e)
m+n bits are enough, and sometimes necessary. The largest operands are 2m−1 and 2n−1, and their product is 2m+n−2m−2n+1, which is strictly less than 2m+n, so m+n bits always suffice. They can also be needed: 2m−1×2n−1=2m+n−2 already requires m+n−1 bits, and the largest product above requires m+n. This is why hardware multipliers write into a double-width register, and why in C the product of two 32-bit integers overflows unless one operand is widened first.
Exercise 4: Two's complement and the asymmetric range
Two's complement is not a minus sign glued to a magnitude. It is a wheel: with n bits there are 2n patterns arranged in a cycle, and the convention cuts the cycle in half, calling the top half negative by subtracting 2n from it. Addition then works on the wheel without any special case for signs. The wheel below is the 4-bit version, with the bit patterns outside and the signed values inside.
a) Write −27 as an 8-bit two's complement pattern. Show the two steps and check the result.
b) Read 101100112 as a signed 8-bit number, then as an unsigned one.
c) On 8 bits, compute the opposite of −128 using the invert-and-add-one rule. Comment on what you get.
d) Give the range of n-bit two's complement and explain, by counting patterns, why it is not symmetric.
e) The same value −27 is copied into a 16-bit variable. Write the 16-bit pattern and state the general rule.
Show the solution
Answers
a)000110112 inverted then plus one: 111001012=229=256−27
b)Signed 179−256=−77; unsigned 179
c)100000002 again: −128 is its own opposite
d)[−2n−1,2n−1−1]: one zero, taken from the positive half
e)FFE516=65509=65536−27: sign extension copies the sign bit
a)
Start from +27=16+8+2+1=000110112. Invert every bit: 111001002. Add one: 111001012. Check by the definition: as an unsigned pattern, 111001012=128+64+32+4+1=229, and 229=256−27, which is what −27 must be on the 8-bit wheel. A second check costs nothing: adding 000110112 and 111001012 gives 256, that is 000000002 with a carry out, and a number plus its opposite is zero.
b)
Signed: the leading bit is 1, so the pattern sits in the negative half. Its unsigned value is 128+32+16+2+1=179, therefore the signed value is 179−256=−77. Unsigned: the very same bits are simply 179. Nothing in memory distinguishes the two readings; the type declared in the program does. Reading a signed byte as unsigned is how a temperature of −77 becomes a reading of 179.
c)
−128 is 100000002. Invert: 011111112. Add one: 100000002, the pattern we started from. So on 8 bits, −128 is its own opposite: +128 does not exist in the range. This is not a defect of the rule, it is the wheel closing on itself, and it is a real source of bugs. In C, taking the absolute value of the most negative integer returns that same negative number, and a routine that assumes ∣x∣≥0 can loop forever on it.
d)
On n bits the range is −2n−1 to 2n−1−1. Count the patterns: there are 2n of them, split into 2n−1 with a leading 1, all negative, and 2n−1 with a leading 0, covering zero and the positives. Zero uses one pattern from the second half, so only 2n−1−1 remain for the positive values while the negatives keep all 2n−1. The asymmetry is the price of having a single representation of zero, and that single zero is what makes ordinary addition work without sign tests.
e)
−27 on 16 bits is 11111111111001012, that is FFE516. Check: as unsigned that pattern is 65509=65536−27. The rule is sign extension: to widen a signed number, copy the sign bit into every new position on the left, not zeros. Filling with zeros instead is the classic widening bug, because 111001012 becomes 00000000111001012=229, so a small negative number turns into a mid-sized positive one, silently and with no error raised.
Exercise 5: Carry and overflow answer two different questions
The adder produces one extra bit on the left, the carry out, and it produces it whatever the operands mean. Whether the stored result is wrong is a separate question, and its answer depends on how the bits are read. For an unsigned reading the test is the carry out; for a signed reading the test is overflow, which is something else entirely. The two rulers below carry the same bit patterns and disagree about their values.
a) On 8 bits, add 200+100. Give the stored pattern, then decide separately whether the unsigned reading and the signed reading are correct.
b) On 8 bits, add 100+50. Same two questions.
c) State the signed overflow rule in terms of the signs of the operands and of the result, and explain why two operands of opposite signs can never overflow.
d) On 8 bits, add −100+(−50). What is stored, and what happened?
e) Give one 8-bit addition for each of the four combinations: no carry and no overflow, carry without overflow, overflow without carry, and both at once.
Show the solution
Answers
a)001011002=44 with a carry out: unsigned wrong, signed right (−56+100=44)
b)150 stored, no carry: unsigned right, signed wrong (−106)
c)Overflow when both operands share a sign and the result does not; opposite signs keep the sum between them
d)011010102=+106 instead of −150: signed overflow
e)30+40 neither; 200+100 carry only; 100+50 overflow only; −100+(−50) or −128+(−128) both
a)
200=110010002 and 100=011001002. The true sum is 300=1001011002, nine bits, so the stored byte is 001011002=44 and the carry out is 1. Unsigned reading: wrong, and the carry out says so, since 300 does not fit in [0,255]. Signed reading: 110010002 is 200−256=−56 and 011001002 is +100, so the intended sum is −56+100=44, and 44 is exactly what is stored. Correct. One addition, a carry out, and a perfectly right answer.
b)
100+50=150=100101102, eight bits, so there is no carry out. Unsigned reading: correct, 150 is stored. Signed reading: both operands are positive, but the stored pattern has a leading 1 and reads as 150−256=−106. Wrong, and no carry out warned about it. Cases a) and b) are the proof that the carry out is not a general error flag: here it is the mirror image of a), right where a) was wrong and silent where a) shouted.
c)
Signed overflow happens exactly when the two operands have the same sign and the result has the opposite sign. Equivalently, in hardware, when the carry into the sign column differs from the carry out of it. Two operands of opposite signs cannot overflow because the true sum lies strictly between them, and both of them are already inside the representable range, so the sum is inside it too. That is why an overflow check only ever needs to look at three bits, not at the magnitudes.
d)
−100 is 156=100111002 and −50 is 206=110011102. The adder computes 156+206=362, so the carry out is 1 and the stored byte is 362−256=106=011010102. Read as signed, that is +106, while the true answer is −150. Both operands were negative and the result came out positive, so this is overflow by the rule in c). The true value −150 is below −128 and simply has no 8-bit pattern. The carry out here is meaningless: for a signed reading it is a by-product, not a diagnostic.
e)
No carry, no overflow: 30+40=70, which fits both readings. Carry without overflow: 200+100 from a), which is −56+100 read as signed. Overflow without carry: 100+50 from b). Both at once: −100+(−50) from d), or more starkly −128+(−128), where 100000002+100000002 gives a carry out and stores 000000002, so the machine reports that two of the most negative numbers add up to zero. Four combinations, one adder, and the flag you must read depends entirely on the type you declared.
Part B: exam-level problems (/50)
Exercise 6: Hexadecimal, masks and a status register
One hexadecimal digit is exactly four bits, which is the only reason hexadecimal exists: it is binary written four times shorter, with no arithmetic of its own. A peripheral exposes an 8-bit status register whose bit 0 is READY, bit 1 is ERROR, bit 3 is BUSY and bit 7 is POWER. The other bits are reserved by the manufacturer and a driver must leave them exactly as they are. The register currently holds 4916.
a) Write 4916 in binary and state which of the four named flags are on.
b) Turn ERROR on without disturbing any other bit. Give the operation and the new register value in hexadecimal.
c) Turn BUSY off, again without disturbing anything else. Give the mask and the result.
d) Toggle POWER. Give the operation and the result.
e) Write a single test that is true when READY and BUSY are both on, and explain why the obvious shorter test is wrong.
f) Reading BUSY with a mask returns 8, not 1. Give two ways to obtain a clean 0 or 1, and say which one a compiler prefers.
Show the solution
Answers
a)010010012=73: READY and BUSY on, ERROR and POWER off, reserved bit 6 on
b)R OR 0216=4B16=75
c)R AND F716=4116=65
d)R XOR 8016=C916=201
e)(R AND 0916) =0916, true here; =0 only says at least one flag is on
f)Shift the masked value right by 3, or compare it with 0; the compiler prefers the comparison
a)
416=01002 and 916=10012, so 4916=010010012=73. The bits set are those of weight 1, 8 and 64, that is bits 0, 3 and 6. So READY is on, BUSY is on, ERROR is off, POWER is off, and reserved bit 6 is on, which is precisely why every operation below must be surgical.
b)
To force a bit to 1 without touching the others, OR with a mask that has a 1 in that position only: ROR0216. Bitwise OR leaves a bit unchanged when the mask bit is 0, since x+0 in the OR sense is x. Result: 73OR2=75=4B16=010010112. Reserved bit 6 is still on, as required.
c)
To force a bit to 0, AND with the complement of its mask: BUSY is 0816, whose complement on 8 bits is F716=111101112. So RANDF716=73AND247=65=4116=010000012. Every position where the mask has a 1 is preserved, and only bit 3 is cleared. Writing the mask as F7 rather than 247 is not a style choice: in hexadecimal you can see the single zero, in decimal you cannot.
d)
To flip a bit, XOR with its mask: RXOR8016=73XOR128=201=C916=110010012. XOR leaves a bit unchanged against a 0 and inverts it against a 1, which makes it the only one of the three operations that is its own inverse: applying it twice restores the register. Summary worth memorising: OR sets, AND with a complement clears, XOR toggles.
e)
Mask both flags at once and compare with the mask itself: (RAND0916)=0916. Here 73AND9=9, so the test is true. The obvious shorter test, (RAND0916)=0, is a different question: it is true as soon as at least one of the two flags is on. On a register where READY is on and BUSY is off it returns true, and the driver starts a transfer on a device that is not idle. Masking tells you which bits survive; only the comparison tells you how many of them did.
f)
Either shift the masked value down, (RAND0816) shifted right by 3, which gives 8/8=1; or compare, writing the test as (RAND0816)=0, which yields a boolean directly. The comparison is what a compiler prefers, because it maps to a single flag test in the condition register with no shift at all, and because it keeps working unchanged if the flag later moves to another bit position. The shift version has to be edited every time the hardware map changes.
Exercise 7: Binary fractions, fixed point, and the error that accumulates
To the right of the binary point the weights carry on halving: 2−1, 2−2, 2−3, and so on. Converting a decimal fraction is done by successive multiplication by 2, reading the integer parts from top to bottom, which is the opposite reading order from the successive divisions used on the integer part. Whether the process terminates is not a matter of patience, it is a property of the number.
a) Convert 0.6875 to binary by successive multiplication. Check by adding the weights.
b) Convert 0.1 the same way. Carry out at least nine steps and describe what you observe.
c) State exactly which finite decimal fractions have a finite binary expansion, and justify it.
d) A sensor stores values in unsigned Q4.4 format: 8 bits, four for the integer part, four for the fraction. Write 5.75 in that format, and give the resolution and the range.
e) A counter in Q4.4 adds 0.1 ten times. What does it actually hold at the end, and how large is the relative error?
f) A billing system must add thousands of amounts in dollars and cents. Say what it should store, and why.
Show the solution
Answers
a)0.6875=0.10112=0.5+0.125+0.0625
b)0.1=0.000112: the block 0011 repeats for ever
c)Exactly when the reduced denominator is a power of 2: m/2k
d)5.75×16=92=010111002=5C16; resolution 0.0625, range 0 to 15.9375
e)0.1 is stored as 2/16=0.125: 1.25 instead of 1.0, a relative error of 25%
f)Integer cents, converted to dollars once at display
a)
0.6875×2=1.375, digit 1, keep 0.375. 0.375×2=0.75, digit 0. 0.75×2=1.5, digit 1, keep 0.5. 0.5×2=1.0, digit 1, remainder zero, stop. Reading the digits in the order produced gives 0.10112. Check by weights: 0.5+0.125+0.0625=0.6875. The reading order matters and it is the reverse of the integer case: here top to bottom, there bottom to top. Mixing the two is the single most common error in this chapter and it produces a plausible looking wrong answer.
b)
0.1×2=0.2 gives 0; 0.4 gives 0; 0.8 gives 0; 1.6 gives 1, keep 0.6; 1.2 gives 1, keep 0.2; and now 0.2 has already occurred, so the block 0011 repeats for ever: 0.1=0.000112. The process does not stop, and it will not stop with more bits, more memory or a better processor. Every machine that stores 0.1 stores something else, slightly larger or smaller depending on where it cuts.
c)
A fraction has a finite binary expansion if and only if, written in lowest terms, its denominator is a power of 2. A finite expansion with k fractional bits is a number of the form m/2k, so the reduced denominator can only be a power of two; conversely any m/2k is written directly with k bits. Thus 0.5, 0.25 and 0.6875=11/16 are exact, while 0.1=1/10 and 0.2=1/5 are not, because 10 and 5 carry a factor 5 that no power of two can cancel.
d)
Q4.4 means the stored 8-bit integer represents the value divided by 24=16. For 5.75: 5.75×16=92, and 92=010111002, so the integer part is 0101 and the fraction 1100, that is 010111002=5C16. Resolution: one unit of the stored integer is 1/16=0.0625, so nothing finer can be expressed. Range: from 0 to 255/16=15.9375. Fixed point is just integer arithmetic with an agreed scale factor, which is why it is used on processors without a floating point unit.
e)
0.1 is not a multiple of 1/16: 0.1×16=1.6, which rounds to 2, so the stored value is 2/16=0.125. Every addition therefore adds 0.125, not 0.1. After ten additions the counter holds 1.25 instead of 1.0: an absolute error of 0.25 and a relative error of 25%. The error did not creep in, it was decided at the first rounding and then multiplied by ten. This is the argument for choosing a scale that matches the data: in a format scaled by 100 instead of 16, 0.1 is exact.
f)
It should store integer cents, not dollars. Cents are exact integers in any base, additions of integers never round, and the total is converted to dollars once, at the moment of display. Storing dollars as binary fractions means that neither 0.10 nor 0.01 is exact, so each of the thousands of additions can be off by a fraction of a cent and the accumulated drift eventually shows up as a total that does not match the sum of the printed lines. Financial software stores integers or decimal types for this reason alone.
Exercise 8: Five statements to correct
Each of the five statements below is FALSE. Say why, give a counterexample where one exists, and write the corrected statement.
1) "Shifting a binary number one place to the right always divides it by two."
2) "On 8 bits, two's complement represents the integers from −127 to +127."
3) "If an addition produces a carry out, the stored result is wrong."
4) "A 32-bit float can hold every integer that a 32-bit integer can hold, since both use 32 bits."
5) "Two's complement just means putting a minus sign in the leading bit."
Show the solution
Answers
1)False: 7≫1=3, and on −7 the arithmetic shift gives −4, the logical one 124
2)False: −128 to +127
3)False: 200+100 gives a carry and stores 44, the right signed answer
4)False: 24 bits of precision, 16777217 rounds to 16777216
5)False: that is sign-magnitude; 100000012 is −127 in two's complement, −1 in sign-magnitude
1)
FALSE on two counts. First, the quotient is an integer: 7=1112 shifted right gives 112=3, not 3.5, and the discarded bit is the remainder. Second, on a negative number the answer depends on which shift is used: −7 on 8 bits is 111110012, and an arithmetic shift gives 111111002=−4, while a logical shift gives 011111002=124. Corrected statement: an arithmetic right shift by k divides a signed number by 2k and rounds towards minus infinity, while a logical right shift divides an unsigned number by 2k; the two agree only on non-negative values. Note in passing that −7 shifted gives −4 whereas division in C gives −3, so a compiler cannot blindly replace a division by a shift.
2)
FALSE. The range is −128 to +127. Counting settles it: 256 patterns, 128 with a leading 1 for the negatives and 128 with a leading 0 shared between zero and the positives, so the positives get only 127 of them. Corrected statement: on n bits, two's complement covers [−2n−1,2n−1−1], an asymmetric interval, because zero has a single representation and it sits on the positive side.
3)
FALSE. The carry out only condemns an unsigned reading. Counterexample: 200+100 on 8 bits produces a carry out and stores 44; read as signed those operands are −56 and +100, and 44 is the exact answer. Conversely 100+50 produces no carry out at all and yet is wrong when read as signed, since it stores −106. Corrected statement: for an unsigned reading the error flag is the carry out; for a signed reading it is overflow, which is when the operands share a sign and the result does not.
4)
FALSE. A single precision float spends 1 bit on the sign and 8 on the exponent, leaving 23 stored significand bits plus one implied, so only 24 bits of precision. Every integer up to 224=16777216 is exact, but 16777217 is not: it rounds to 16777216. A 32-bit integer, by contrast, is exact up to 231−1. Corrected statement: a float trades precision for range, so it reaches far larger magnitudes than a 32-bit integer while representing far fewer integers exactly.
5)
FALSE. That description is sign-magnitude, a different and older convention, and the two disagree from the very first value: 100000012 is −1 in sign-magnitude but −127 in two's complement. Sign-magnitude also has two zeros, 000000002 and 100000002, and it breaks ordinary addition, since adding 000000012 and 100000012 gives 100000102, which sign-magnitude reads as −2 instead of 0. Corrected statement: in two's complement the leading bit is a normal positional digit whose weight is −2n−1, negative rather than positive, which is exactly what lets one adder serve both signs.
Exercise 9: Problem: a 12-bit sensor and a 16-bit accumulator
A thermocouple board returns temperatures as 12-bit two's complement counts, one count being 0.0625∘C, that is 1/16 of a degree. The firmware copies each reading into a 16-bit signed variable, sums 200 of them and divides to obtain an average. Two things can go wrong on that path, and both have gone wrong in shipped products. The diagram shows the 16-bit word with the 12 sensor bits already in place.
a) A reading comes back as 0F016. Convert it to a temperature.
b) The next reading is F6016. Convert it to a temperature, being careful about the sign.
c) Give the correct 16-bit pattern for the reading of b), in hexadecimal, and justify the four leading bits.
d) A junior developer fills those four bits with zeros instead. What temperature does the system then report, and what does that cost?
e) The firmware sums 200 readings in a 16-bit signed accumulator. Show that this can overflow, and find the smallest accumulator width that cannot.
f) With the probe held at its maximum, 2047 counts, give the average the buggy 16-bit version reports and compare it with the truth.
Show the solution
Answers
a)0F016=240 counts, 240/16=15.0 °C
b)F6016: 3936−4096=−160 counts, −10.0 °C
c)FF6016: the four leading bits copy the sign bit, 65376−65536=−160
f)409400−6×65536=16184, average 80.92/16≈5.06 °C against 127.94 °C
a)
0F016=0000111100002. Bit 11 is 0, so the value is positive and equal to its unsigned reading: 0F016=240 counts. Temperature: 240/16=15.0∘C. Reading the sign bit before anything else is the habit to build here, because the conversion that follows is different in the two cases and looks equally reasonable either way.
b)
F6016=1111011000002, and bit 11 is 1, so the value is negative. On 12 bits the wheel has 212=4096 patterns, so the signed value is 3936−4096=−160 counts. Temperature: −160/16=−10.0∘C. The trap is to subtract 65536 instead of 4096: the field is 12 bits wide, and the wheel size is fixed by the field, not by the variable that will later hold it.
c)
The correct 16-bit pattern is FF6016=11111111011000002. The four leading bits are copies of the sign bit, which is sign extension. The check is arithmetic, not visual: as unsigned, FF6016=65376, and 65376−65536=−160, the same value as on 12 bits. Widening a signed number must preserve its value, and only replicating the sign bit does that.
d)
Filling with zeros gives 0F6016=3936, a positive number, so the system reports 3936/16=246.0∘C. A probe sitting at −10∘C is reported at 246∘C. Nothing raises an error: the arithmetic is valid, the variable is in range, the reading is simply false. On a freezer controller this trips the over-temperature alarm and starts a cooling cycle that is not needed; on a furnace it does the opposite. Note that the bug is invisible in testing as long as the bench temperature stays positive.
e)
A 16-bit signed accumulator holds at most 215−1=32767. With 200 readings of up to 2047 counts, the true sum reaches 200×2047=409400, more than twelve times the capacity, so it overflows. For a width n we need 2n−1−1≥409400; since 218−1=262143 is too small and 219−1=524287 is enough, the smallest workable width is n=20 bits. In practice the accumulator is declared as 32-bit, both because 20 is not a machine width and because it leaves room if the sample count grows.
f)
The true sum is 409400. On 16 bits it wraps modulo 65536: 409400−6×65536=16184, which is below 32768 and therefore reads as a positive 16184. Divided by 200 that is 80.92 counts, so the reported temperature is 80.92/16=5.06∘C, while the probe is actually at 2047/16=127.94∘C. The system reports a comfortable 5 degrees on a component near 128 degrees, with no flag raised anywhere. Sum first in a width that fits, scale once at the end, and the whole class of bug disappears.
Exercise 10: Problem: the IEEE 754 single precision format
A single precision float packs three fields into 32 bits: one sign bit, eight exponent bits stored with a bias of 127, and twenty-three significand bits to which a leading 1 is implied and never stored. The value is (−1)s×1.f×2e−127. Everything surprising about floating point, from the spacing of the numbers to the failure of an equality test, follows from those field widths.
a) Encode +9.75. Give the three fields, then the 32-bit word in hexadecimal.
b) Decode C120000016.
c) Give the gap between two consecutive floats near 1.0, then near 1000. Explain the difference in one sentence.
d) Give the largest integer N such that every integer from 0 to N is exactly representable, and show that the next one is not.
e) Explain why the test comparing 0.1+0.2 with 0.3 fails, and say what to write instead.
f) The exponent fields 00000000 and 11111111 are reserved. Say what for, and why that reservation was worth two of the 256 exponent values.
Show the solution
Answers
a)9.75=1.001112×23: s=0, e=130, word 411C000016
b)s=1, exponent 130−127=3, 1.f=1.25: −10.0
c)2−23≈1.19×10−7 near 1, 2−14≈6.1×10−5 near 1000: constant relative precision
d)N=224=16777216; 16777217 rounds to 16777216
e)Neither operand is exact and the sum rounds one unit above the float of 0.3; use a tolerance, or integer cents for money
f)00000000: zero and subnormals; 11111111: infinities and NaNs, so every operation returns something defined
a)
9.75=1001.112, since 8+1+0.5+0.25=9.75. Normalise: 1001.112=1.001112×23. Sign s=0. Exponent field e=3+127=130=100000102. Significand: the bits after the implied leading 1, that is 00111 followed by eighteen zeros. The full word is 01000001000111000000000000000000, and grouping into fours gives 01000001000111000000000000000000=411C000016. Check the other way: 1+2−3+2−4+2−5=1.21875 and 1.21875×8=9.75.
b)
C120000016=110000010010000000000000000000002. Split by field: s=1, so the number is negative. Exponent field =100000102=130, so the true exponent is 130−127=3. Significand bits =01000…, so 1.f=1+2−2=1.25. Value =−1.25×23=−10.0. Splitting at the right place is the whole exercise: the boundaries fall inside hexadecimal digits, so the pattern has to be expanded to binary first, and trying to read the fields directly off the hexadecimal is how this question is failed.
c)
Near 1.0 the exponent is 0, so consecutive floats differ by one unit in the last significand bit, that is 2−23≈1.19×10−7. Near 1000 the exponent is 9, because 512≤1000<1024, so the gap is 29−23=2−14≈6.1×10−5, roughly five hundred times wider. In one sentence: floats have constant relative precision and therefore absolute precision that degrades in proportion to the magnitude.
d)
N=224=16777216. Up to that point the significand has enough bits to name every integer: with exponent k the representable numbers are spaced by 2k−23, and that spacing stays at most 1 as long as k≤23, which covers everything below 224. At 224 itself the exponent is 24 and the spacing becomes 2, so 16777217 falls between two neighbours and rounds to 16777216. In a float, adding 1 to 16777216 leaves it unchanged, and a counting loop written with floats stops advancing there while continuing to run.
e)
Neither 0.1 nor 0.2 is exactly representable, since their denominators carry a factor 5, so each is stored as the nearest available float. Their sum is rounded again, and the result lands one unit in the last place above the float nearest to 0.3. The bits differ, so the equality test is false, and it is false for a correct reason: the machine is comparing two numbers that were never the ones written in the source. What to write instead is a tolerance test, comparing the absolute difference with a small threshold, or better a relative one scaled to the magnitude of the operands. For money the right answer is not a tolerance at all, it is to store integer cents.
f)
Exponent field 00000000 marks zero and the subnormal numbers, which drop the implied leading 1 and fill the gap between the smallest normal float and zero, so that subtracting two nearly equal numbers cannot silently return zero. Exponent field 11111111 marks the infinities and the NaNs, which let an overflow or a division by zero flow through a computation and be tested at the end instead of aborting it. Two of the 256 exponent codes buy a system in which every arithmetic operation returns something well defined, and that closure is what makes numerical code portable across machines.
Something missing from this set?A type of exercise your teacher gives, a problem you are stuck on, a mistake you spotted: tell me, I read every request.
Sending goes through your free space: that is how I can answer you and tell you when the exercise is added. An email is enough, no password. Create your space.
Rather not create an account? Write to me directly.
Get in touch for a first session. With a B.Sc. in Computer Science from McGill and an M.Sc. in Applied Computer Science from Concordia, I teach binary arithmetic through what actually breaks in production: silent overflows, sign extension bugs and float comparisons.