00:00 In the previous lesson, you learned about bitwise operations. In this lesson, you’ll learn all about the different ways binary is used to represent numbers and why that makes bitwise operations have weird results.
00:13 Binary, outside of the computing world, is a series of powers of 2, which on its own can only represent numbers 0 or greater. Computers use binary to represent numbers, but they store those values in a finite space in memory. A group of eight bits is called a byte.
The C programming language has four different ways of storing integers:
long. Each of these is comprised of a different number of bytes. “And what,” you might say, “about negative numbers?” Such an intelligent question.
02:08 Let’s talk about negative numbers. A lot! The next seven slides are all about negative numbers. What might seem simple on the surface, decidedly is not. Signed-magnitude is one strategy for representing a negative number.
It uses the leftmost, or most significant, bit to indicate whether a number is positive or negative. If the leftmost bit is 1, the number is negative. Positive numbers for a single-byte signed-magnitude integer range from 0 to
0111 1111 in binary.
02:59 Well, that’s a little odd. 1 and all 0s is 0, but with the sign bit set. This is called ambiguous zero. There are two different ways of representing zero in signed-magnitude representation. Two zeros isn’t the only problem. Addition is also problematic.
04:07 An early solution to the problems with signed-magnitude representation was ones’ complement. The leftmost bit still indicates the sign, but the negative value is stored as its complement—i.e. all its bits are flipped.
04:22 This weird little trick actually solves the addition problem. Positive values are the same as signed-magnitude, only the negative values are different. Ones’ complement, like signed-magnitude, still has an ambiguous zero.
04:51 It flips the sign bit and complements, or inverts, all the other bits. These are the two representations for zero in ones’ complement. Here’s positive one. Negative one is the invert of positive one. Here’s two.
05:25 What’s better than one compliment? Well, two compliments, of course! This is the most common mechanism for storing negative numbers in modern machines. It removes the ambiguous zero problem and makes a couple of the corner cases in addition easier. The leftmost bit is still used for the sign.
05:43 Negative numbers are still a complement, but this time, they’re the complement plus 1. This is called an asymmetrical representation. There are more negative values than positive ones. For an eight-bit byte the range is -128 to 127.
07:01 Consider these three different representations and what it would mean to perform bitwise operations on them. Most of the time you’re doing bitwise operations, you really don’t care about the negative representation. You’re tracking bits, turning them on and off. Being explicit about this and using an unsigned integer is your best bet.
In the following table, I’m going to show you the three types of a negative number representation. For each case, I’ll show a value, the value NOT-ed, and the resulting decimal numbers in the three representations. 0110 NOT-ed is
The decimal value in signed-magnitude is 6, and the NOT-ed value is -121. The same two values in ones’ complement give 6 and -6. And 6 and -7 in two’s complement. The same two binary values give four different variations on what they mean. Let’s try it again, this time with
09:39 You don’t typically do bitwise math on these types of numbers, so if the details of how these are stored doesn’t interest you, I’ll see you at the next lesson. Floating-point numbers are based on the IEEE standard 754.
09:54 These are a lossy representation of non-integer numbers. They’re lossy because they aren’t precise. As you saw in the second lesson, you can store sixteen digits of pi, but that’s it. Good enough for most cases, but it’s still not technically pi.
10:11 A floating-point number is composed of three parts: a sign bit, an exponent, and a mantissa. This is similar to scientific notation. The name floating point comes from the fact that the number of digits to the right of the decimal isn’t fixed. The decimal point floats around.
10:32 The standard defines several variations. The two most common precisions are single float, which is stored as thirty-two bits or four bytes, with a sign bit, eight bits for the exponent, and twenty-three bits for the mantissa.
11:14 The number is treated as an unsigned value. To get small fractional numbers, you need a negative power. That’s done by biasing the exponent. That means the raw number actually represents 127 more than the actual number. Subtract the bias, and you get the right value.
11:32 The mantissa is twenty-three bits. This part represents a fraction where each bit is a negative power of two. For all exponent values except zero, the fractional portion assumes a twenty-fourth bit to the left that has a value of one.
11:50 That gives you twenty-four bits of precision packed into twenty-three bits of space. And it means you have to add one to the raw mantissa to get the number it actually represents. Let’s look at an example to clear up this messiness.
12:06 All that stuff on the previous slide breaks down into this formula. The value the binary digits represent is minus one to the power of the sign bit times two to the power of the biased exponent times the mantissa plus one.
12:55 and look at that! Pi. If you remember the pi from lesson two, this one has less precision. The pi in lesson two was a double float. This one being a single means there are fewer significant digits to the right of the decimal point.
13:11 Floating-point numbers are only approximations. Some of them cannot be precisely described. You can see this easily in practice with the handy REPL. I might have to dig out my calculator again, but I’m pretty sure that’s not right.
13:47 As long as you don’t overflow either of those two parts, you’re good. Although fixed point doesn’t have the approximation problem that floating point does, it also doesn’t have a dedicated part of the CPU that specializes in doing math with them.
Want even more precision for your numbers? Python has the
fractions module that represents the numerator and denominator of a fraction separately. It supports doing operations on these objects, finding the lowest common denominator of the result.
Become a Member to join the conversation.