#FF0000 (red), 0xDEADBEEF (memory debugging), 0x7FFFFFFF (max 32-bit int) — hex is everywhere for developers. Why do computers love hex? And why does 0.1 + 0.2 ≠ 0.3? This guide walks through binary / hex / two's complement / IEEE 754 — the bit patterns hiding inside every integer and float.
Why bases matter
Computers think in 0/1. Humans don't read 32 bits well:
0011 1001 0101 1010 1110 0100 1100 1100 (32 bits)
↑ unreadable at a glanceSame value in different bases:
Binary: 00111001 01011010 11100100 11001100
Hex: 39 5A E4 CC
Decimal: 962,360,012Hex aligns perfectly with binary (4 bits = 1 hex digit). Best visual compression for binary, which is why developers and machines both like it.
Why hex — the magic of 16 = 2^4
4 bits = exactly one hex digit. Conversion is trivial:
Binary Hex
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
0111 7
1000 8
1001 9
1010 A
1011 B
1100 C
1101 D
1110 E
1111 FA 16-bit integer = 4 hex digits. 32-bit = 8 hex digits. 64-bit = 16 hex digits. Direct mapping.
Decimal doesn't have this property — 2^10 = 1024 ≈ 1000, which is why 1 KiB and 1 KB are different. Octal also aligns (3 bits = 1 octal digit), but hex's 4-bit boundary fits the byte better.
Try it — Number Base Converter shows the same value in binary / octal / decimal / hex side by side. Hex Encode / Decode converts text ↔ hex.
Where hex shows up
- Color codes —
#RRGGBB, each channel 8 bits (00-FF).#FF6B35= R 255, G 107, B 53. - Memory addresses — debuggers / crash dumps show
0x7FFFEE00 - SHA / MD5 / UUID — sequences of hex chars
- Error codes / flags — Windows
0x80004005(E_FAIL) - Bit masks —
0xFF00= high byte,0x000F= low nibble - UTF-8 bytes — Korean "안" =
EC 95 88 - Magic numbers —
0xDEADBEEF,0xCAFEBABE(Java class file),0x89 50 4E 47(PNG header) - Password hash markers — part of bcrypt's
$2b$12$...
Positive + negative = two's complement
Positive integers are easy. 5 = 0101. What about negatives?
Naive approach — sign bit (top bit, 0 = +, 1 = -):
+5 = 0101
-5 = 1101 ← just flip the sign bit?Problems:
- Two zeros — +0 (
0000) and -0 (1000) - Addition becomes complex.
+5 + (-5) = -10in binary, not 0.
Solution — two's complement:
-X = (NOT X) + 1
Example: -5 (4 bit)
+5 = 0101
NOT = 1010
+ 1 = 1011
So -5 = 1011
Sanity check — 5 + (-5):
0101
+ 1011
────
10000 ← 5 bits (overflow); low 4 bits = 0000 = 0 ✓Key wins:
- Only one zero (
0000) - Addition and subtraction share circuitry — no separate subtractor needed, just negate then add.
- Comparison — check sign bit, then standard compare
Bit width ranges
8-bit (byte): -128 to +127 (= -2^7 to 2^7 - 1)
16-bit (short): -32,768 to +32,767 (= -2^15 to 2^15 - 1)
32-bit (int): -2.1B to +2.1B (= -2^31 to 2^31 - 1)
64-bit (long): -9.2 × 10^18 to +9.2 × 10^18 (= -2^63 to 2^63 - 1)Positive range is 1 smaller than negative — zero lives on the positive side.
Famous overflow incidents
// 32-bit int max + 1
0x7FFFFFFF + 1 = 0x80000000
= 2147483647 + 1 = -2147483648 (wraps to negative!)
// Y2038 — Unix timestamp at int32 limit
2038-01-19 03:14:07 UTC is the last second
The next second → -2147483648 → December 13, 1901
// Java's Integer.MIN_VALUE
Math.abs(-2147483648) === -2147483648 ← stays negative!Floats — the secret of IEEE 754
Integers aren't enough. How do you store 0.1, π, 1.5 × 10^200?
IEEE 754 (1985) — scientific notation in bits:
x = (-1)^sign × 1.mantissa × 2^exponent
32-bit float (single):
┌─┬───────────┬─────────────────────────────┐
│S│ exponent │ mantissa │
│ │ (8 bit) │ (23 bit) │
└─┴───────────┴─────────────────────────────┘
64-bit double:
┌─┬──────────────┬──────────────────────────────────────────────────┐
│S│ exponent │ mantissa │
│ │ (11 bit) │ (52 bit) │
└─┴──────────────┴──────────────────────────────────────────────────┘Example — 1.5 as 64-bit double:
1.5 = 1.1₂ × 2^0
S = 0 (positive)
exponent = 0 + 1023 (bias) = 1023 = 01111111111
mantissa = 1000...0 (the binary 0.1 = 0.5)
Full: 0 01111111111 1000000000000000000000000000000000000000000000000000
= 0x3FF8000000000000Why 0.1 + 0.2 ≠ 0.3
> 0.1 + 0.2
0.30000000000000004
> 0.1 + 0.2 === 0.3
false0.1 has no finite binary representation. 1/10 in binary is a repeating fraction:
0.1 (decimal) = 0.0001100110011001100... (binary, repeating)
Truncated to 54 mantissa bits (double):
0.1 ≈ 0.1000000000000000055511151231257827021181583404541015625
0.2 ≈ 0.2000000000000000111022302462515654042363166809082031250
Sum: 0.3000000000000000166533453693773481063544750213623046875
≈ 0.30000000000000004Solutions — BigDecimal / Decimal128 for money. Epsilon for compare:
Math.abs(a - b) < 1e-9 // float comparison
// Or work in integer units
// $0.10 + $0.20 → 10 cents + 20 centsSpecial values
NaN (Not a Number):
0 / 0, Math.sqrt(-1)
NaN === NaN → false (the only value not equal to itself)
Use isNaN(x) or Number.isNaN(x)
Infinity:
1 / 0 = Infinity
-1 / 0 = -Infinity
±0 (positive zero, negative zero):
0 === -0 → true
1 / 0 === 1 / -0 → false (Infinity vs -Infinity)
Object.is(0, -0) → falseHex prefix conventions
0x— C / JavaScript / Python (0xFF= 255)0o— Python octal0b— binary (0b1011)#— CSS colors (#FF6B35)$— assembly ($FF)&h— VBA / Visual Basic
Underscores for readability: 0xFFFF_FFFF_DEAD_BEEF (Python / Rust / modern JS).
Common pitfalls
1. JavaScript bitwise operators are 32-bit
0x100000000 | 0 // 4294967296 | 0 = 0 ← truncated to 32 bit!
// 64-bit bitwise requires BigInt
0x100000000n | 0n // 4294967296n2. parseInt's base guess (older environments)
parseInt("010") // old browsers: 8 (octal interpretation!)
parseInt("010", 10) // 10 ← always specify radix
parseInt("0xff") // 255 (hex auto-detected)3. Signed vs unsigned shift
-1 >> 1 // -1 (sign bit preserved, arithmetic shift)
-1 >>> 1 // 2147483647 = 0x7FFFFFFF (zero fill, logical shift)4. Float comparison
// Bad
if (price * 1.1 === expected) { ... }
// Good
if (Math.abs(price * 1.1 - expected) < 0.001) { ... }
// Best (financial)
priceInCents * 110 / 100 // integer arithmetic5. NaN comparison gotcha
NaN === NaN // false
NaN !== NaN // true (only way to identify NaN)
isNaN("foo") // true (strings get coerced!)
Number.isNaN("foo") // false (strict)References
- IEEE 754 — Wikipedia
- Two's complement — Wikipedia
- 0.30000000000000004.com — float precision site
- Goldberg (1991) — "What Every Computer Scientist Should Know About Floating-Point" — Oracle docs
Summary
- Binary lives inside the computer. Hex is the compact form we read (4 bits = 1 hex digit).
- Hex appears in colors, memory, hashes, flags, UTF-8 bytes, magic numbers — everywhere.
- Two's complement encodes negatives so add/subtract share a circuit, no -0, range -2^(n-1) to 2^(n-1) - 1.
- Overflow wraps — Y2038 and int32 max + 1 = negative are the famous cases.
- IEEE 754 floats = sign + exponent + mantissa. 0.1 + 0.2 ≠ 0.3 because 0.1 is a repeating binary fraction.
- Money belongs in BigDecimal or integer units (cents). Float comparison needs an epsilon.
- NaN, Infinity, ±0 — IEEE 754 specials, watch the comparison quirks.
- Try it — Number Base Converter / Hex Encode / Decode.