Binary to Decimal Converter
Abstract
Converting numbers between bases is the bridge between how we count (base 10) and how computers count (base 2). In this project you build a Python converter that goes both ways between binary and decimal, then extend it to handle octal, hexadecimal, arbitrary bases (2–36), negative numbers via two’s complement, and a peek at how floating-point numbers are stored in IEEE 754. Along the way you implement both the “use the built-in” version and the “from scratch” algorithm so you understand what int(s, 2)int(s, 2) and bin()bin() actually do.
You will learn:
- The mechanics of positional number systems.
- Python’s
int(string, base)int(string, base)andbin()bin()/oct()oct()/hex()hex(). - How to convert any base in pure Python.
- Two’s complement for representing negative integers.
- IEEE 754 single-precision float layout.
- Input validation and friendly error reporting.
Why Bases Matter
- Base 2 (binary) — the physical layer of every digital computer (transistors are on/off).
- Base 8 (octal) — Unix file permissions (
chmod 755chmod 755). - Base 10 (decimal) — humans.
- Base 16 (hex) — RGB colors (
#ff8000#ff8000), memory addresses, MAC addresses, byte dumps.
Prerequisites
- Python 3.6 or above.
- A text editor or IDE.
- Comfort with
input()input(),if/elif/elseif/elif/else, and basic loops.
Getting Started
Create the project
- Create folder
binary-decimal-converterbinary-decimal-converter. - Inside, create
binary_to_decimal.pybinary_to_decimal.py.
Write the code
Binary to Decimal
Source# Binary to Decimal Conveter
print("Binary to Decimal Conveter")
print("Choose one of the following options:")
print("1. Convert a binary number to a decimal number")
print("2. Convert a decimal number to a binary number")
print("3. Exit")
option = int(input("Enter your option: "))
if option == 1:
binary = input("Enter a binary number: ")
decimal = int(binary, 2)
print("The decimal value of", binary, "is", decimal)
elif option == 2:
decimal = int(input("Enter a decimal number: "))
binary = bin(decimal)
print("The binary value of", decimal, "is", binary)
elif option == 3:
exit()# Binary to Decimal Conveter
print("Binary to Decimal Conveter")
print("Choose one of the following options:")
print("1. Convert a binary number to a decimal number")
print("2. Convert a decimal number to a binary number")
print("3. Exit")
option = int(input("Enter your option: "))
if option == 1:
binary = input("Enter a binary number: ")
decimal = int(binary, 2)
print("The decimal value of", binary, "is", decimal)
elif option == 2:
decimal = int(input("Enter a decimal number: "))
binary = bin(decimal)
print("The binary value of", decimal, "is", binary)
elif option == 3:
exit()Run it
C:\Users\Your Name\binary-decimal-converter> python binary_to_decimal.py
Binary ↔ Decimal Converter
1. Binary → Decimal
2. Decimal → Binary
3. Exit
Choice: 1
Binary: 1101
1101₂ = 13₁₀C:\Users\Your Name\binary-decimal-converter> python binary_to_decimal.py
Binary ↔ Decimal Converter
1. Binary → Decimal
2. Decimal → Binary
3. Exit
Choice: 1
Binary: 1101
1101₂ = 13₁₀Step-by-Step Explanation
1. Using built-ins (the right answer for production)
# Binary string → decimal int
decimal = int("1101", 2) # 13
# Decimal int → binary string (with '0b' prefix)
binary = bin(13) # '0b1101'
# Strip the prefix if you want just the digits
binary_clean = bin(13)[2:] # '1101'
# Also available:
hexadecimal = hex(13) # '0xd'
octal = oct(13) # '0o15'# Binary string → decimal int
decimal = int("1101", 2) # 13
# Decimal int → binary string (with '0b' prefix)
binary = bin(13) # '0b1101'
# Strip the prefix if you want just the digits
binary_clean = bin(13)[2:] # '1101'
# Also available:
hexadecimal = hex(13) # '0xd'
octal = oct(13) # '0o15'int(s, base)int(s, base) accepts any base from 2 to 36. Above 10, digits use letters a..za..z (case-insensitive).
2. Menu with validation
def main():
while True:
print("\n1. Binary → Decimal 2. Decimal → Binary 3. Exit")
choice = input("> ").strip()
if choice == "1":
raw = input("Binary: ").strip()
if not all(c in "01" for c in raw) or not raw:
print("Use only 0s and 1s."); continue
print(f"{raw}₂ = {int(raw, 2)}₁₀")
elif choice == "2":
raw = input("Decimal: ").strip()
try:
n = int(raw)
except ValueError:
print("Not an integer."); continue
print(f"{n}₁₀ = {bin(n)[2:]}₂")
elif choice == "3":
break
else:
print("Invalid choice.")def main():
while True:
print("\n1. Binary → Decimal 2. Decimal → Binary 3. Exit")
choice = input("> ").strip()
if choice == "1":
raw = input("Binary: ").strip()
if not all(c in "01" for c in raw) or not raw:
print("Use only 0s and 1s."); continue
print(f"{raw}₂ = {int(raw, 2)}₁₀")
elif choice == "2":
raw = input("Decimal: ").strip()
try:
n = int(raw)
except ValueError:
print("Not an integer."); continue
print(f"{n}₁₀ = {bin(n)[2:]}₂")
elif choice == "3":
break
else:
print("Invalid choice.")Two validation guards — character set for binary, int()int() for decimal — and friendly error messages.
Doing It By Hand
Binary → Decimal
def bin_to_dec(s: str) -> int:
total = 0
for digit in s:
total = total * 2 + int(digit) # Horner's method
return totaldef bin_to_dec(s: str) -> int:
total = 0
for digit in s:
total = total * 2 + int(digit) # Horner's method
return totalTrace for "1101""1101":
start total = 0
'1' total = 0*2 + 1 = 1
'1' total = 1*2 + 1 = 3
'0' total = 3*2 + 0 = 6
'1' total = 6*2 + 1 = 13start total = 0
'1' total = 0*2 + 1 = 1
'1' total = 1*2 + 1 = 3
'0' total = 3*2 + 0 = 6
'1' total = 6*2 + 1 = 13That single multiply-and-add idiom is Horner’s method and works for any base (replace 22 with basebase).
Decimal → Binary
def dec_to_bin(n: int) -> str:
if n == 0: return "0"
if n < 0: return "-" + dec_to_bin(-n)
out = []
while n:
out.append(str(n & 1)) # last bit
n >>= 1 # shift right
return "".join(reversed(out))def dec_to_bin(n: int) -> str:
if n == 0: return "0"
if n < 0: return "-" + dec_to_bin(-n)
out = []
while n:
out.append(str(n & 1)) # last bit
n >>= 1 # shift right
return "".join(reversed(out))n & 1n & 1extracts the least-significant bit (0 or 1).n >>= 1n >>= 1shifts right, dropping that bit.- Building backwards then reversing avoids inefficient string prepend.
Arbitrary-Base Conversions
DIGITS = "0123456789abcdefghijklmnopqrstuvwxyz"
def to_base(n: int, base: int) -> str:
if not 2 <= base <= 36:
raise ValueError("base must be 2..36")
if n == 0: return "0"
sign = "-" if n < 0 else ""
n = abs(n)
out = []
while n:
out.append(DIGITS[n % base])
n //= base
return sign + "".join(reversed(out))
def from_base(s: str, base: int) -> int:
s = s.lower().strip()
sign = 1
if s.startswith("-"): sign, s = -1, s[1:]
n = 0
for ch in s:
digit = DIGITS.index(ch)
if digit >= base:
raise ValueError(f"'{ch}' is not valid in base {base}")
return sign * sum(DIGITS.index(c) * (base ** i)
for i, c in enumerate(reversed(s)))DIGITS = "0123456789abcdefghijklmnopqrstuvwxyz"
def to_base(n: int, base: int) -> str:
if not 2 <= base <= 36:
raise ValueError("base must be 2..36")
if n == 0: return "0"
sign = "-" if n < 0 else ""
n = abs(n)
out = []
while n:
out.append(DIGITS[n % base])
n //= base
return sign + "".join(reversed(out))
def from_base(s: str, base: int) -> int:
s = s.lower().strip()
sign = 1
if s.startswith("-"): sign, s = -1, s[1:]
n = 0
for ch in s:
digit = DIGITS.index(ch)
if digit >= base:
raise ValueError(f"'{ch}' is not valid in base {base}")
return sign * sum(DIGITS.index(c) * (base ** i)
for i, c in enumerate(reversed(s)))Now to_base(255, 16)to_base(255, 16) → "ff""ff", from_base("ff", 16)from_base("ff", 16) → 255255. The exact same algorithm handles base-3, base-7, base-32, base-36.
Negative Numbers and Two’s Complement
Computers represent signed integers as two’s complement. For an 8-bit number:
- Positive 5 →
0000 01010000 0101. - Negative 5 → invert bits, add 1 →
1111 10111111 1011.
def to_twos_complement(n: int, bits: int = 8) -> str:
if n < 0:
n = (1 << bits) + n # wrap into range
return format(n, f"0{bits}b")
def from_twos_complement(s: str) -> int:
bits = len(s)
n = int(s, 2)
return n - (1 << bits) if s[0] == "1" else n
print(to_twos_complement(-5, 8)) # 11111011
print(from_twos_complement("11111011")) # -5def to_twos_complement(n: int, bits: int = 8) -> str:
if n < 0:
n = (1 << bits) + n # wrap into range
return format(n, f"0{bits}b")
def from_twos_complement(s: str) -> int:
bits = len(s)
n = int(s, 2)
return n - (1 << bits) if s[0] == "1" else n
print(to_twos_complement(-5, 8)) # 11111011
print(from_twos_complement("11111011")) # -5This is exactly how a C int8_tint8_t works.
IEEE 754 Float Layout
Python’s structstruct module lets you peek at how 32-bit floats are stored:
import struct
bits = struct.pack(">f", 3.14) # big-endian float
binary = "".join(f"{b:08b}" for b in bits)
print(binary)
# 01000000010010001111010111000011
# sign: 0, exponent: 10000000 (=128 → bias-adjusted 1),
# mantissa: 10010001111010111000011import struct
bits = struct.pack(">f", 3.14) # big-endian float
binary = "".join(f"{b:08b}" for b in bits)
print(binary)
# 01000000010010001111010111000011
# sign: 0, exponent: 10000000 (=128 → bias-adjusted 1),
# mantissa: 10010001111010111000011This is the layout every desktop CPU uses. It explains why 0.1 + 0.2 != 0.30.1 + 0.2 != 0.3 in every language with IEEE 754 floats.
Common Mistakes
| Problem | Cause | Fix |
|---|---|---|
ValueError: invalid literal for int()ValueError: invalid literal for int() | Non-binary characters in input | Validate with all(c in "01" for c in s)all(c in "01" for c in s) |
bin(13)bin(13) returns '0b1101''0b1101' not '1101''1101' | The 0b0b prefix is informative | Slice [2:][2:] |
Manual int(digit) * 2**iint(digit) * 2**i is O(n²) string-wise | Bad algorithm | Use Horner: total = total * base + dtotal = total * base + d |
Negative numbers print as -0b101-0b101 | Built-in bin()bin() includes the sign | Either accept it, or build your own |
| Leading zeros lost | Numbers do not preserve format | Pad: format(n, "08b")format(n, "08b") |
int(s, 0)int(s, 0) infers wrong base | 00 means “guess from prefix” — 0b0b, 0o0o, 0x0x | Pass an explicit base |
Variations to Try
1. Add octal and hex
Extend the menu with Hex → DecimalHex → Decimal, Decimal → OctalDecimal → Octal, etc.
2. Conversion history
Print each conversion plus a timestamp; export to CSV.
3. Batch mode
Read a list of numbers from a file; emit a converted column.
4. CLI flags
python convert.py 255 --from 10 --to 16 # → ff
python convert.py ff --from 16 --to 2 # → 11111111python convert.py 255 --from 10 --to 16 # → ff
python convert.py ff --from 16 --to 2 # → 111111115. GUI version
Tkinter with two big text boxes and base dropdowns. Type in either box; the other updates live.
6. Bitwise visualizer
For each step of the decimal→binary algorithm, show the running value, the bit being extracted, and the resulting binary so far. Great for classrooms.
7. Fractional binary
Extend to fractions: 0.6250.625 ↔ 0.1010.101. Loop value *= 2value *= 2; integer part is the next bit.
8. Two’s-complement signed converter
Use the function above; ask the user how many bits.
9. Color picker
Parse #ff8000#ff8000 → (255, 128, 0)(255, 128, 0) in RGB.
10. Web tool
A Flask form (see Basic Web Server) that converts on submit.
Real-World Applications
- Networking — IPv4
192.168.1.1192.168.1.1to its 32-bit binary representation, subnet calculations. - Color manipulation —
#rrggbb#rrggbb↔ tuples of decimal. - Unix permissions —
chmod 755chmod 755is octalrwxr-xr-xrwxr-xr-x. - Reverse engineering — interpreting hex dumps and memory addresses.
- Cryptography — XOR, bit rotations, hex-encoded keys.
- Embedded programming — register values are hex; bit fields matter.
Educational Value
- Positional number systems — the most fundamental abstraction in computing.
int(s, base)int(s, base)— one of Python’s most underused built-ins.- Horner’s method — fewer multiplications than
digit * base ** idigit * base ** i. - Bitwise operators —
&&,||,^^,<<<<,>>>>. - Two’s complement — the actual representation of signed integers.
Next Steps
- Extend to octal and hex.
- Implement
to_baseto_base/from_basefrom_basefor arbitrary bases. - Add two’s complement handling.
- Build a GUI with live conversion as the user types.
- Read about IEEE 754 to understand why
0.1 + 0.2 != 0.30.1 + 0.2 != 0.3.
Try it here
Click any bit to flip it. Each 11 contributes its place value (128, 64, 32, …), and the
total is the decimal number — the same math your code does:
Conclusion
You built a converter both ways using built-ins, then implemented the math by hand so the int(s, 2)int(s, 2) and bin(n)bin(n) calls stopped looking like magic. The same algorithms power every hex dump, color picker, and networking calculator you have ever used. Full source on GitHub. Find more algorithm projects on Python Central Hub.
If this helped you, consider buying me a coffee ☕
Buy me a coffeeWas this page helpful?
Let us know how we did
