At the absolute core of all computing lies a simple truth: computers are really dumb. They do not understand your memes, they do not read your poetry, and they do not know what the number nine means. To a computer, the entire universe is just a massive collection of light switches that are either turned on or turned off.
While humans count in Decimal (Base 10) because we happened to grow ten fingers, computers operate in Binary (Base 2) because their circuits are made of transistors that have two states. To bridge the gap between machine code and human brains, programmers rely on Hexadecimal (Base 16) and Octal (Base 8).
Understanding how these systems work and how to translate values between them is a fundamental skill for developers, networking engineers, and system administrators. In this guide, we will look at the mechanics of each system, explain the manual math behind conversions, write some JavaScript code to handle conversions programmatically, and check out some common bugs that happen when you convert bases.
Why do computers use binary instead of decimal?
To understand binary, we have to look at computer hardware. Inside a modern CPU, you will find billions of tiny electronic switches called transistors.
Each transistor can either block electrical current or let it pass. This means a transistor has only two stable states:
- OFF: Low voltage, which we represent as
0. - ON: High voltage, which we represent as
1.
This system is called binary because there are only two choices.
But why did computer designers choose this over a decimal system? Why not build transistors that can output ten different voltage levels representing zero through nine?
The answer is reliability. Electronic signals are messy. They suffer from electrical noise, temperature changes, and wear over time. If a computer had to distinguish between 1.2 volts (representing a 3) and 1.5 volts (representing a 4), a tiny spike in power would corrupt your data. By using only two states, the computer only needs to distinguish between “hardly any voltage” and “full voltage.” This makes digital systems highly resistant to signal noise and incredibly stable.
What are the four main number systems used in computing?
Let us take a close look at the four base systems that dominate the digital world.
Decimal (Base 10): The human default
Decimal is our everyday counting system. It uses ten unique symbols: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.
When you write a decimal number like 365, you are using positional notation. Each digit represents a value multiplied by a power of ten.
- The
5is in the units place ($10^0 = 1$) - The
6is in the tens place ($10^1 = 10$) - The
3is in the hundreds place ($10^2 = 100$)
So, 365 is:
$$3 \times 100 + 6 \times 10 + 5 \times 1 = 300 + 60 + 5 = 365$$
Binary (Base 2): The machine core
Binary uses only two symbols: 0 and 1. Each digit is called a bit.
Because it is Base 2, place values are powers of two instead of powers of ten.
- First place: $2^0 = 1$
- Second place: $2^1 = 2$
- Third place: $2^2 = 4$
- Fourth place: $2^3 = 8$
- Fifth place: $2^4 = 16$
- Sixth place: $2^5 = 32$
If you have the binary number 1101, you read it from right to left:
1in the ones place ($1 \times 1 = 1$)0in the twos place ($0 \times 2 = 0$)1in the fours place ($1 \times 4 = 4$)1in the eights place ($1 \times 8 = 8$)
Add them up: $8 + 4 + 0 + 1 = 13$ in decimal.
Hexadecimal (Base 16): The programmer’s shorthand
Binary is fine for computers, but it is a nightmare for humans. Writing a number like 255 in binary requires eight digits: 11111111. If you need to write a large memory address, you will end up with a string of zeros and ones that goes off the edge of the screen. It is easy to make mistakes when copying it.
Hexadecimal solves this. It uses sixteen symbols: 0-9 and A, B, C, D, E, F.
A= 10B= 11C= 12D= 13E= 14F= 15
Positional values in hex are powers of sixteen: $16^0 = 1$, $16^1 = 16$, $16^2 = 256$, $16^3 = 4096$, and so on.
The magic of hex is that one hex character represents exactly four binary digits (known as a nibble). This makes converting binary to hex incredibly fast and neat. For example, 1111 in binary is F in hex, and 1111 1111 is FF.
Octal (Base 8): The legacy permission mapper
Octal uses eight symbols: 0, 1, 2, 3, 4, 5, 6, 7.
Its place values are powers of eight: $8^0 = 1$, $8^1 = 8$, $8^2 = 64$, $8^3 = 512$.
While octal was popular in the era of 36-bit mainframe computers, it is less common today. However, it remains a standard in Unix file systems for permissions.
When you run chmod 755 file.txt in a terminal, you are using octal.
- The first digit
7represents owner permissions. - The second digit
5represents group permissions. - The third digit
5represents public permissions.
Each octal digit translates to a three-bit binary sequence. A 7 is 111 (Read, Write, and Execute are all turned on), and a 5 is 101 (Read and Execute are on, Write is off).
How do you convert between bases manually?
Let us walk through how to convert numbers between these bases using a pen and paper.
Converting from any base to Decimal
To convert any number from another system back to decimal, write out the place values of that system. Then, multiply each digit by its corresponding place value and sum the results.
Let us convert the hex number 2F to decimal.
- Identify the base: Hexadecimal is Base 16.
- Write out the powers of 16 for each position:
- Right position: $16^0 = 1$
- Left position: $16^1 = 16$
- Match the digits:
2in the sixteens position.F(which equals 15) in the ones position.
- Multiply and add: $$(2 \times 16) + (15 \times 1) = 32 + 15 = 47$$
So 2F in hex is 47 in decimal.
Converting from Decimal to Binary
To convert a decimal number to binary, you use successive division by two. You divide the number by two, write down the remainder, and divide the quotient by two again. You repeat this until you reach zero. Then, you read the remainders from bottom to top.
Let us convert the decimal number 29 to binary:
- $29 / 2 = 14$ with a remainder of
1 - $14 / 2 = 7$ with a remainder of
0 - $7 / 2 = 3$ with a remainder of
1 - $3 / 2 = 1$ with a remainder of
1 - $1 / 2 = 0$ with a remainder of
1
Now read the remainders from the last step back to the first: 11101.
Let us check our work: $$1 \times 16 + 1 \times 8 + 1 \times 4 + 0 \times 2 + 1 \times 1 = 16 + 8 + 4 + 0 + 1 = 29$$ The math checks out perfectly.
The shortcut: Converting Hex to Binary and Binary to Hex
Converting between binary and hex is incredibly easy. You do not need to convert to decimal first. You just need to group your bits.
Because $2^4 = 16$, one hex digit matches exactly four binary bits.
To convert binary to hex, group the bits into sets of four starting from the right. If the leftmost group has fewer than four bits, add zeros to the left to pad it.
Let us convert 11010111 to hex:
- Split it into groups of four:
1101and0111. - Convert each group to decimal:
1101is $8 + 4 + 0 + 1 = 13$, which isDin hex.0111is $0 + 4 + 2 + 1 = 7$, which is7in hex.
- Combine them:
D7.
To go from hex to binary, do the reverse. Turn each hex digit into its four-bit binary equivalent.
A(10) becomes1010.3becomes0011.- So
A3in hex is10100011in binary.
How do you write number base conversion code in JavaScript?
Writing base conversions in JavaScript is simple because the language has built-in features for it.
Using built-in parseInt() and toString()
You can convert any base to decimal using the parseInt(string, radix) function. The radix parameter is the base you are converting from.
To convert a decimal number to another base, use the number.toString(radix) method.
// Converting hex string to decimal number
const hexString = "2F";
const decimalNum = parseInt(hexString, 16);
console.log(decimalNum); // 47
// Converting binary string to decimal
const binaryString = "11101";
const decimalFromBinary = parseInt(binaryString, 2);
console.log(decimalFromBinary); // 29
// Converting decimal number to binary string
const num = 29;
const binaryStr = num.toString(2);
console.log(binaryStr); // "11101"
// Converting decimal number to hex string (capitalized)
const hexStr = num.toString(16).toUpperCase();
console.log(hexStr); // "1D"
Writing a custom Decimal to Binary converter from scratch
If you are preparing for a technical interview, you should know how to do this conversion without using built-in methods. Here is a custom function that uses the successive division method:
function decimalToBinary(decimalVal) {
// Handle edge cases
if (decimalVal === 0) return "0";
if (typeof decimalVal !== 'number' || decimalVal < 0 || !Number.isInteger(decimalVal)) {
return "Invalid input";
}
let num = decimalVal;
let binaryDigits = [];
while (num > 0) {
const remainder = num % 2;
binaryDigits.push(remainder);
num = Math.floor(num / 2);
}
// Reverse the array to get the correct order and join to string
return binaryDigits.reverse().join("");
}
console.log(decimalToBinary(29)); // "11101"
console.log(decimalToBinary(0)); // "0"
What are common pitfalls in number system conversions?
Base conversions seem simple, but they can cause massive headache bugs in software development. Let us look at two of the most common issues.
Floating-point precision errors (the 0.1 + 0.2 mystery)
If you open your browser console and type 0.1 + 0.2, you will get a weird output: 0.30000000000000004.
This is not a bug in JavaScript. It is a fundamental limitation of binary arithmetic.
Computers represent numbers using the IEEE 754 standard for floating-point math. In this system, numbers are stored in binary. While a number like 0.1 looks clean in decimal, it cannot be represented exactly in binary.
To see why, think about the fraction $1/3$ in decimal. It is a repeating fraction: 0.333333.... You cannot write it down exactly with a finite number of digits.
Similarly, 0.1 in binary is a repeating fraction: 0.0001100110011.... Because computers have finite memory (64 bits for a double-precision float), they must round the fraction. When you add two rounded fractions together, the rounding errors compound, resulting in that tiny extra value at the end.
| Decimal Value | Binary Representation | Exact or Repeating? |
|---|---|---|
| 0.5 | 0.1 | Exact (1/2) |
| 0.25 | 0.01 | Exact (1/4) |
| 0.1 | 0.0001100110011… | Repeating (infinite series) |
| 0.2 | 0.001100110011… | Repeating (infinite series) |
To avoid bugs, never compare floating-point numbers directly in your code. Instead, check if the difference between them is smaller than a tiny tolerance value:
const result = 0.1 + 0.2;
const expected = 0.3;
const tolerance = Number.EPSILON; // The smallest difference between two representable numbers
const isCorrect = Math.abs(result - expected) < tolerance;
console.log(isCorrect); // true
Overflow errors with large integers in JavaScript
In JavaScript, numbers are stored as double-precision floats. This means integers are only safe to parse up to a limit: Number.MAX_SAFE_INTEGER, which is 9,007,199,254,740,991 ($2^{53} - 1$).
If you try to parse or convert integers larger than this using standard numbers, you will lose precision. Digits will change, and conversions will output wrong results.
To fix this, you must use the BigInt type, which can handle arbitrarily large integers without precision loss.
// Standard numbers fail on large values
const largeHex = "1FFFFFFFFFFFFFFFFF"; // Hex value larger than safe limit
const badDecimal = parseInt(largeHex, 16);
console.log(badDecimal.toString(16)); // "200000000000000000" (lost precision!)
// BigInt handles it correctly
const bigIntNum = BigInt("0x" + largeHex);
console.log(bigIntNum.toString(16).toUpperCase()); // "1FFFFFFFFFFFFFFFFF" (correct!)
Note that when using BigInt constructor with hex strings, you should prepend 0x to signal that the input is a hexadecimal format.
How does the TextSorter Base Converter work?
If you are working on a project that requires converting dozens of values, doing the calculations manually is a waste of time. Writing custom scripts can also take too long if you need to double-check formatting or parse ASCII text.
Our free online Base Converter does all the calculations for you.
Real-time conversion walkthrough
Here is how you can use the tool:
- Paste your source number: Find the input field for the base you have (Binary, Octal, Decimal, or Hexadecimal) and paste your value.
- See instant translations: As you type, the tool converts the value and updates the other three fields in real-time. You do not need to click a submit button.
- Read ASCII translations: If you enter a binary sequence, the tool automatically decodes the binary bits into ASCII characters. This is useful for decoding strings or debugging communication protocols.
- Work offline: The tool runs 100% locally in your browser using JavaScript. No data is sent to external servers, so you can convert private keys, address spaces, or configuration flags safely.
Check out the tool at TextSorter Base Converter.
Quick Reference Conversion Table
Here is a handy lookup table for decimal values 0 to 16, showing how they translate to binary, hex, and octal.
| Decimal (Base 10) | Binary (Base 2) | Hexadecimal (Base 16) | Octal (Base 8) |
|---|---|---|---|
| 0 | 0000 | 0 | 0 |
| 1 | 0001 | 1 | 1 |
| 2 | 0010 | 2 | 2 |
| 3 | 0011 | 3 | 3 |
| 4 | 0100 | 4 | 4 |
| 5 | 0101 | 5 | 5 |
| 6 | 0110 | 6 | 6 |
| 7 | 0111 | 7 | 7 |
| 8 | 1000 | 8 | 10 |
| 9 | 1001 | 9 | 11 |
| 10 | 1010 | A | 12 |
| 11 | 1011 | B | 13 |
| 12 | 1100 | C | 14 |
| 13 | 1101 | D | 15 |
| 14 | 1110 | E | 16 |
| 15 | 1111 | F | 17 |
| 16 | 0001 0000 | 10 | 20 |