Notes About Binary Conversion
Introduction:
I have never made a secret of this fact: I am not from a traditional computer science background. Though I have learned a great bit (heh) of programming over the years, some parts still baffle me.
Binary was one such part.
This happened because of a few reasons. First, there are not many resources that explain this part in a simple and approachable language. Second is the problem of accessibility: it is hardly going to help me if you are showing the entire process using an image.
Having said that, I have been lucky enough to find some accessible tutorials and explanations to learn from, and I have finally leveled up. Meaning, that I understand what exactly is binary, how it works, and barring occasional calculation mistakes which are part and parcel of life when it comes to me, I have grasped the subject.
I am writing this guide hoping that someone in my position wouldn't have to worry about muddling through the inaccessible guides and tutorials.
Before you begin though, here are some expectations. I expect you to know how computer programming works. It means that when you see some code, it doesn't look like Egyptian hieroglyphs.
Second, here are some symbols you may not be familiar with. Remember these, for these will be used in the explanations below:
/: Used for division.
%: used for mod/remainder (whatever you want to call it.)
- and -: used for addition and subtraction.
^: the power symbol.
*: Used for multiplication.
Now, let's dive in!
First, what is binary?
Binary is a system on which modern computers run. It uses two values: 0 and 1. These values can represent logical states, with 0 meaning "false" and 1 meaning "true." Together, sequences of 0s and 1s form patterns called binary strings. These binary strings represent numbers, instructions, or data, allowing computers to perform complex tasks.
To understand how computers process this information, it’s essential to know about bits. A bit is the smallest data unit and can only be a 0 or a 1. When bits are grouped, they form larger units of data. For example, 8-bit refers to processing data in groups of 8 bits, allowing for 256 different values (from 0 to 255). In contrast, 16-bit means data is handled in groups of 16 bits, representing 65,536 values (from 0 to 65,535). As the number of bits increases, computers can manage larger sets of data and perform more complex operations, which is why systems have evolved from 8-bit to 32-bit and even 64-bit architectures in modern computing.
Converting a number to binary:
The binary is a base 2 system. Meaning, it only has 2 digits to denote two values: 0, and 1. If your number is larger than 1, you need to use a process to convert it into a series of 0s and 1s. This series will represent the larger numbers, much like how numbers larger than 9 are represented by the combination of the digits 0 to 9.
It is a process that has to be executed step by step. Let's use 10 as an example.
To convert a number from decimal (base 10) to binary (base 2), we need to divide the decimal number by 2. After that, we have to take note of the remainder. These remainders will always be either 0 or 1. After we have done that, we need to reverse the list of the remainder we have. It means we need to put the first digit to the last place, the second to the second last place, the third digit to the third-last place, etc. This goes on until the entire list of the binary digits is reversed.
Let's do this practically, and then it will make more sense.
You need to watch out for two symbols before we begin: First is /, the division symbol. The second is %, the mod symbol. Both of these are used heavily in the process of converting a decimal number to a binary string.
First, get the remainder:
10 % 2 = 0
10 is divided by 2 perfectly, so 0 is the remainder.
Now, do the actual division:
10 / 2 = 5
After the division, the value is reduced. This is important, otherwise, the numbers will remain the same, and we will get an endless series of 0s.
Now, do the same with 5:
5 % 2 = 1
We get a 1 this time because 5 is not divided by 2 perfectly. At this point, let me remind you: You also need to keep track of the 0s and 1s you have gotten so far. For now, it is one 0, and one 1.
Now, the actual division.
5 / 2 = 2
Why 2?
Because 5 is not divisible by 2 perfectly. The actual answer would be a fractional number 2.5.
But for our purposes, we don't want an actual precise answer. So, the fractional part, the numbers after the point "." are dropped. So, we get a 2.
Now, do the same process with 2:
2 % 2 = 0
Then divide it:
2 / 2 = 1
And now, we are at a step where you can end up making a critical mistake. Whenever you are doing this process of converting a decimal number to binary, always take care of this 1 as well. Otherwise, your final result is incomplete.
1 % 2 = 1
1 / 2 = 0
Once again, not exactly. The actual answer would be 0.5, but for our purposes, we are ignoring that fractional part. Now that we have reached 0, our process is done. This is how the binary string looks like currently:
0101
But that is not accurate yet. We need to reverse this string: Put the first digit last, the second digit second last, and continue to do so until the entire string is reversed.
After that reversal is done, the binary string looks like this:
1010
That is it! The decimal number 10 is 1010 in binary.
The process is not exactly that intuitive at first, especially if you are not mathematically inclined so much like me. You might need to practice a lot before it starts to make sense, and starts to feel natural. But once you get this process down, you will realize that at the end of the day, it is nothing but division, with some steps being ignored because performing actual division is not our goal here.
Try it with any number you like. Just be certain to note down the values, as the larger numbers will take you longer, and their binary strings will be larger.
So that is it for turning a decimal number into binary. But what about the reverse? How can we turn a binary string into a decimal number, something that humans can understand? Unless you have not noticed it yet, reading 0s and 1s is not how humans calculate things properly. It may work for computers, but it does not work for us. Therefore, there is a need to convert from binary (base 2) to decimal (base 10).
Converting from binary to decimal:
To convert a decimal number into a binary string, we used division. To convert a binary string into a decimal number, we use multiplication. This is nothing unusual: division and multiplication are inverse of each other. You can undo a division by multiplication, and vice versa.
To convert a binary string into a decimal number, you have to follow a process. It goes like this: you start with the rightmost binary digit of the string, and multiply it with the 2 to the power of 0 (2 ^ 0). As you move to the left, you increase the power of 2. After you have done this, you add up the result, and you will then have the decimal number.
That is the theory of the process. Now, let's try this with a practical example.
I will use binary string 1010 to illustrate this process. The rightmost digit is 0, so I will start from there.
Since it is the first digit, the power of 2 is 0. It simplifies to 1 since 2 to the power of 0 is 1 (2 ^ 0 = 1)
So, we multiply 1 with 0:
1 * 0 = 0
Since any number multiplied by 0 is equal to 0, we get a 0 as an answer.
Now, we move to the left. This time the binary digit is 1. The power of 2 has also increased by 1.
2 ^ 1 = 2, so we multiply 2 with 1.
2 * 1 = 2
So, we have a 2 and a 0 for now. We continue to move to the left. This time, the digit is 0. The same process is repeated, but the power of 2 is 2 now.
2 ^ 2 = 4
4 * 0 = 0
Our final binary digit is 1. So, we repeat the same thing:
2 ^ 3 = 8
8 * 1 = 8
Now, the result looks like this:
8, 0, 2, 0.
We add these four numbers:
8 + 0 + 2 + 0 = 10
They all add up to 10. So, the binary string 1010 equals to decimal number 10.
Just like the process of turning a decimal number into a binary string, this process will also take longer depending upon the size of the binary string. Meaning, that the larger the binary string is, the longer it takes to convert it into a decimal number.
I suppose you could skip all the 0s and calculate the 1s. But you still need to keep track of the powers of 2. If you increase it in the wrong order, your result will be wrong as well. I did not choose to skip 0s in the practical demonstration, because I wanted to demonstrate this process in full.
Here's how the entire thing looks like as one long equation:
(2 ^ 0) 0 + (2 ^ 1) 1 + (2 ^ 2) 0 + (2 ^ 3) 1 = 10
Go ahead, and practice with other binary values. The entire process is straightforward, once you get a hang of it. And I can't believe that I am the one saying this!
Do you know what is better than doing all of this by hand?
Why, make the computer do it of course!
The math I have already described above. However, things may appear a little bit different once implemented in the code. That is because there are a lot of cases we need to handle. First, check the Python function to convert a decimal number to binary below, and then I will talk about it further.
def dec_to_bin(number):
binary_list = []
while number > 0:
binary_list.append(str(number % 2))
number //= 2
binary_string = "".join(reversed(binary_list))
return binary_string
The function takes a number as an input. After that, it goes into a loop where the process of dividing with two is performed repeatedly. The loop ends when the number reaches 0. The remainder are stored as string characters in the list. After that, the list is joined and reversed. The result is stored in a variable, and then this variable is returned as a result.
Notice the things we need to handle here?
While the math is straightforward, the storing and displaying of the 0s and 1s is slightly more involved. You might think of storing the binary digits in an integer variable, but there are a few problems with it. First, it cannot be reversed. Meaning, it won't give us accurate results. Remember, we need to reverse the order of the binary digits, only then we will get the correct binary value of a decimal number.
Second, we do have an option of turning this string into a decimal integer later on, if we want to. It can be accomplished by the use of the int() function. You need to give it an optional argument that supplies the base of the conversion. In this case, the argument should be 2 because binary is base 2.
Therefore, the approach of storing the result in a list, and then joining that list is correct. However I have not taken the extra step of converting the binary string into an actual decimal number because my purpose is to print the string, nothing more than that.
Let's add some assert cases to test the function.
assert dec_to_bin(0) == "0", "Test case 0 failed"
assert dec_to_bin(1) == "1", "Test case 1 failed"
assert dec_to_bin(2) == "10", "Test case 2 failed"
assert dec_to_bin(5) == "101", "Test case 5 failed"
assert dec_to_bin(10) == "1010", "Test case 10 failed"
assert dec_to_bin(255) == "11111111", "Test case 255 failed"
assert dec_to_bin(16) == "10000", "Test case 16 failed"
assert dec_to_bin(1023) == "1111111111", "Test case 1023 failed"
print("All test cases passed!")
Traceback (most recent call last):
File "E:\python-practice\decimal to binary.py", line 12, in <module>
assert dec_to_bin(0) == "0", "Test case 0 failed"
^^^^^^^^^^^^^^^^^^^^
AssertionError: Test case 0 failed
Oops! I forgot to add a conditional test for 0. Let's fix that!
def dec_to_bin(number):
if number == 0:
return "0"
binary_list = []
while number > 0:
binary_list.append(str(number % 2))
number //= 2
binary_string = "".join(reversed(binary_list))
return binary_string
Let's run the tests again:
All test cases passed!
Now they all pass. The function is working properly now.
Let's implement the function to convert a binary string to decimal:
def binary_to_decimal(binary):
if binary == "0":
return 0
number = 0
for i in range(len(binary)):
number += (2 ** i) * int(binary[-(i + 1)])
return number
First, we handle the case of 0. Then we loop through the entire binary string. The result of the operation of multiplying with the powers of 2 is stored in a variable called number, and we use the index of the string to multiply with the powers of 2. After it is done, the variable number is returned by the function.
One thing to note is the use of the index. I could have reversed the string, and then done the multiplication. That way, I would not have to get the index from reverse through the negative indexing. But that approach needlessly complicates things, and when you don't need something, it is best not to add it to your program.
The assert cases:
assert binary_to_decimal("0") == 0, "Test case '0' failed"
assert binary_to_decimal("1") == 1, "Test case '1' failed"
assert binary_to_decimal("10") == 2, "Test case '10' failed"
assert binary_to_decimal("101") == 5, "Test case '101' failed"
assert binary_to_decimal("1101") == 13, "Test case '1101' failed"
assert binary_to_decimal("1010") == 10, "Test case '1010' failed"
assert binary_to_decimal("11111111") == 255, "Test case '11111111' failed"
assert binary_to_decimal("10000") == 16, "Test case '10000' failed"
assert binary_to_decimal("1111111111") == 1023, "Test case '1111111111' failed"
print("All test cases passed!")
All test cases passed!
Since I learned from my mistakes from the previous function, this time I didn't have to go and fix some missing pieces of code. So, all assertions pass smoothly.
So that is it for this article. There are some bits (heh) I have left out in this one. Mostly because I felt these topics could be covered elsewhere. Such as fractional numbers, negative numbers, binary arithmetic, and finally, octal and decimal (base and base 16 respectively.)
So, I will write about these things in the future. Until then, check out my web novel here, it recently crossed 500000 words and 250 chapters. So, give it a read. And I will see you next time.