Mankind evolved, slowly I might say, from number system to number system and, for now, most of us adopted and use the decimal system in our everyday life.
There are many other numeral systems that we still use for specific tasks and some that are long forgotten.
We'll go through some of them.
An additive numeral system
We’ll start with the Roman numeral system that originated in ancient Rome and remained the usual way of writing numbers throughout Europe well into the Late Middle Ages.
It is an additive system and has 7 symbols (I, V, X, L, C, D, and M corresponding in value to 1, 5, 10, 50, 100, 500, and 1000). The letter N was sometimes used to represent 0 as a standalone number.
The rules are the following:
- symbols are placed ascending in value from right to left, much like it happens with decimals.
- any symbol that has a smaller value than the previous one is subtracted from the value of the previous one
If you visit Europe you might see some letters engraved within museums, on buildings, and statues around you. Some of those letters you might also find on your wristwatch.
For example, shown below is the year 1864 (MDCCCLXIV).
Following the previously mentioned rules, MDCCCLXIV = V — I + X + L + C + C + C + D + M and its symbol-by-symbol equivalent in decimal is 1864 = 5–1 + 10 + 50 + 100 + 100 + 100 + 500 + 1000.
As seen above, it is less verbose and easier to read or write 1864 compared to MDCCCLXIV. It is also easier for us, humans, to do some basic math with decimals (add, subtract, multiply, divide).
A curious fact is that the current infamous year, 2020, has the same length in decimal numerals as it has in Roman numerals notation: MMXX.
So there’s no wonder Europeans adopted the decimal system abandoning the Roman one.
A positional numeral system
A positional base n numeral system (with n a natural number greater than 1 known as the radix) has n basic symbols (digits) that correspond to the first n natural numbers including 0. The other numerals are obtained using the position of the symbol in the number. Moving from right to left, each symbol value is multiplied by n raised to the power of its position.
Unlike the Roman system which is additive, the decimal system is positional.
Also called base-10 because it uses 10 digits (0 to 9), it is the numeral system we use in our everyday life.
Following the definition from above and using the year 1864 as an example we get:
The binary system
Mankind evolved, slowly I might say, from number system to number system and, for now, most of us adopted and use the decimal system.
On the other side, computers use in their everyday routines the binary numeral system. This is also called the base-2 and uses 0 and 1 as digits.
Following the above definition, and using the year 1864 as an example, which is 11101001000 in binary, we get:
Comparing 1864 with 11101001000, you can agree that the first one is more human-readable and usable.
From here going forward we’ll use the Dart programming language to exemplify operators, but the science is valid in any programming language.
The operators used within the binary realm are quite different from the ones we use in the decimal system:
AND (&) is used to determine whether a particular bit is 1 or 0. This operator takes two equal-length binary representations and performs the logical AND operation on each pair of the corresponding bits. If both bits in the compared position are 1, the bit in the resulting binary representation is 1 otherwise, the result is 0.
assert(1 & 1 == 1);
assert(1 & 0 == 0);
assert(0 & 1 == 0);
assert(0 & 0 == 0);
OR (|) takes two qual-length binary representations and performs the logical inclusive OR operation on each pair of corresponding bits. The result in each position is 0 if both bits are 0, while otherwise the result is 1.
assert(1 | 1 == 1);
assert(1 | 0 == 1);
assert(0 | 1 == 1);
assert(0 | 0 == 0);
XOR (^) is used to switch particular bits between 1 and 0. This operator takes two equal-length binary representations and performs the logical inclusive OR operation on each pair of corresponding bits. The result in each position is 0 if both bits are 0, otherwise the result is 1.
assert(1 ^ 0 == 1);
assert(0 ^ 1 == 1);
assert(1 ^ 1 == 0);
assert(0 ^ 0 == 0);
NOT or complement (~) performs logical negation on each bit. Bits that are 0 become 1, and those that are 1 become 0.
assert(~1 == 0);
assert(~0 == 1);
assert(~0001 == 1110);
Shift left (<<) shifts the bits to the left and zeros are shifted in as new digit.
assert(1 << 1 == 2);
Shift right (>>) shifts the digits to the right and zeros are shifted in to replace the discarded bits.
assert(1 >> 1 == 0);
If you want, you can read more here on Dart operators.
That was a very short and fast introduction to binary, but I hope is good enough for understanding the basics and this bit masking example.