Type 0.1 + 0.2 into Python or JavaScript and the answer comes back as 0.30000000000000004. This is not a bug; it is a direct consequence of how computers store decimal numbers. Understanding floating-point representation explains this puzzle, helps you avoid serious errors in scientific and financial programs, and is a standard topic in A Level and university computer science.
The Idea: Scientific Notation in Binary
Scientists write very large and very small numbers compactly as, for example, \(6.02 \times 10^{23}\) or \(1.6 \times 10^{-19}\). The decimal point "floats" to just after the first significant digit, and the exponent records where it really belongs.
Computers do the same in base 2. Every non-zero number can be written as
$$ x = (-1)^s \times 1.f \times 2^{E} $$
where \(s\) is the sign bit, \(1.f\) is the significand (or mantissa) in binary, and \(E\) is the exponent. Because the leading digit of a normalised binary number is always 1, it does not need to be stored. This free extra bit is called the hidden bit.
The IEEE 754 Standard
Since 1985, almost all computers follow the IEEE 754 standard. The two most common formats are:
| Format | Sign bits | Exponent bits | Fraction bits |
|---|---|---|---|
| Single precision (32-bit) | 1 | 8 | 23 |
| Double precision (64-bit) | 1 | 11 | 52 |
The exponent is stored with a bias so that it is never negative: the stored value is \(E + 127\) for single precision and \(E + 1023\) for double. So a single-precision number is decoded as
$$ x = (-1)^s \times 1.f \times 2^{e - 127} $$
where \(e\) is the 8-bit stored exponent.
Worked Example 1: Encoding −6.75 in Single Precision
Step 1: Sign. The number is negative, so \(s = 1\).
Step 2: Convert 6.75 to binary. The whole part is \(6 = 110_2\). The fraction 0.75 gives \(0.75 \times 2 = 1.5\) (digit 1) and \(0.5 \times 2 = 1.0\) (digit 1), so \(0.75 = 0.11_2\). Hence
$$ 6.75 = 110.11_2 $$
Step 3: Normalise. Move the binary point two places left:
$$ 110.11_2 = 1.1011_2 \times 2^2 $$
So \(E = 2\) and the fraction bits are 1011 followed by zeros.
Step 4: Bias the exponent. \(e = 2 + 127 = 129 = 10000001_2\).
Step 5: Assemble the 32 bits.
- Sign: 1
- Exponent: 10000001
- Fraction: 10110000000000000000000 (23 bits)
Written out: 1100 0000 1101 1000 0000 0000 0000 0000, which in hexadecimal is C0D80000.
Check by decoding: \(1.1011_2 = 1 + 0.5 + 0.125 + 0.0625 = 1.6875\), and \(-1.6875 \times 2^{129 - 127} = -1.6875 \times 4 = -6.75\). Correct.
Worked Example 2: Decoding a Bit Pattern
What number is stored as 41200000 (hex) in single precision?
Step 1: Expand to binary. \(4 = 0100\), \(1 = 0001\), \(2 = 0010\), then zeros:
0100 0001 0010 0000 0000 0000 0000 0000
Step 2: Split the fields.
- Sign: 0, so positive.
- Exponent: 10000010, which is \(130\).
- Fraction: 0100000... (the bits after the exponent).
Step 3: Decode. The significand is \(1.01_2 = 1.25\), and the true exponent is \(130 - 127 = 3\).
$$ x = 1.25 \times 2^3 = 10 $$
Sanity check: 10 in binary is \(1010_2 = 1.010_2 \times 2^3\), giving exponent 3 and fraction bits 010, which matches.
Why 0.1 Cannot Be Stored Exactly
Converting 0.1 to binary by repeated doubling gives
$$ 0.1 = 0.0001100110011\ldots_2 $$
with the block 0011 repeating forever, just as \(\frac{1}{3} = 0.333\ldots\) never ends in decimal. A fraction terminates in binary only if its denominator, in lowest terms, is a power of 2. Since \(\frac{1}{10}\) has a factor of 5 in the denominator, it cannot terminate.
The computer must round after 23 bits (single) or 52 bits (double). In single precision, the stored value of 0.1 is actually
$$ 0.100000001490116\ldots $$
In double precision, 0.1 and 0.2 are each stored with a tiny error, and their rounded sum lands on the nearest double to 0.30000000000000004 rather than the one nearest 0.3. That is why 0.1 + 0.2 == 0.3 is false in most languages.
Precision, Range and Machine Epsilon
Machine epsilon is the gap between 1 and the next representable number: \(2^{-23} \approx 1.19 \times 10^{-7}\) for single precision and \(2^{-52} \approx 2.22 \times 10^{-16}\) for double. This means single precision keeps about 7 significant decimal digits and double about 15 to 16.
The exponent field sets the range. The largest single-precision value is about \(3.4 \times 10^{38}\) and the largest double about \(1.8 \times 10^{308}\).
Some exponent patterns are reserved for special values:
- Zero, with both \(+0\) and \(-0\).
- Infinity, produced by overflow or division such as \(1/0\).
- NaN ("not a number"), produced by undefined operations like \(0/0\) or \(\sqrt{-1}\).
- Subnormal numbers, which fill the gap near zero by dropping the hidden bit.
Practical Consequences for Programmers
- Never test floats for exact equality. Use a tolerance instead, for example \(|a - b| < 10^{-9}\).
- Beware catastrophic cancellation. Subtracting two nearly equal numbers destroys significant digits. In single precision, \(1.0000001 - 1.0\) does not give exactly \(10^{-7}\) because 1.0000001 itself was rounded.
- Adding small numbers to large ones can lose them. In double precision, \(10^{16} + 1\) evaluates to \(10^{16}\), because the spacing between doubles near \(10^{16}\) is 2.
- Use integers or decimal types for money. Banks store amounts in whole paisa or cents, not floating-point rupees.
Did You Know?
- In 1991, during the Gulf War, a Patriot missile system's clock drifted because 0.1 seconds could not be stored exactly; after about 100 hours the timing error was around 0.34 seconds, and the system failed to intercept an incoming missile.
- William Kahan, the main architect of IEEE 754, received the Turing Award in 1989 for his work on floating-point computation.
- Machine learning often uses 16-bit "half precision" and bfloat16 formats, trading accuracy for speed and memory.
Key Takeaways
- Floating-point numbers store a sign, a biased exponent and a fraction, like scientific notation in base 2.
- Single precision uses 1 + 8 + 23 bits with bias 127; double uses 1 + 11 + 52 bits with bias 1023.
- Decimals such as 0.1 have infinite binary expansions, so they are rounded when stored.
- Machine epsilon measures precision: about 7 digits for single and 15 to 16 for double.
- Compare floats with a tolerance, watch for cancellation, and avoid floats for money.
Need help with IEEE 754 conversions, numerical methods or a programming assignment? Amathify's experts and PhD scholars provide step-by-step solutions through WhatsApp or the Ask a Question form.
Stuck on a problem or assignment?
Our experts and PhD scholars solve maths, physics and chemistry — step by step.