Why Binary Fractions Create Rounding Error
A worked explanation of why binary fractions create rounding error, including the key limitation and a checkable example.
Adding 0.1 ten times in double-precision floating-point gives 0.9999999999999999, not 1.0. That is why binary fractions create rounding error: the decimal fraction 0.1 has no finite binary representation, so the computer stores an approximation instead.
The core issue is a mathematical property of base-2 fractions. A binary fraction is a sum of powers of 2 with coefficients 0 or 1. For example, 0.101₂ equals 1×2⁻¹ plus 0×2⁻² plus 1×2⁻³, which is 0.5 plus 0.125, or 0.625. Every finite binary fraction must be expressible as such a sum using a finite number of terms. The decimal fraction 0.1 cannot be written that way. The binary expansion of 0.1 is a repeating pattern: 0.1₁₀ equals 0.00011001100110011...₂ where the digits 0011 repeat infinitely. No finite number of bits can capture that infinite sequence exactly.
The IEEE 754 standard defines how computers store floating-point numbers. Double-precision uses 1 sign bit, 11 exponent bits, and 52 mantissa bits. When you assign 0.1 to a double-precision variable, the system must round the infinite binary expansion to fit those 52 mantissa bits. The IEEE 754 default rounding rule is round-to-nearest, ties to even. Applying that rule to the infinite binary of 0.1 produces a stored value that is not exactly 0.1.
Why Binary Fractions Create Rounding Error Example
Work through the storage of 0.1 in IEEE 754 double-precision step by step. First write the exact binary expansion: 0.1₁₀ = 0.0001100110011001100110011001100110011001100110011001100110011...₂ with the pattern 0011 repeating. Normalise this representation so the binary point is after the leading 1. The normalised form is 1.1001100110011...₂ × 2⁻⁴. The mantissa field stores the fraction part after the leading 1, using 52 bits.
Truncate the infinite pattern to 52 bits. The 52nd bit is a 1, and the 53rd bit is a 1. The round-to-nearest rule looks at the 53rd bit and sees a 1, so it rounds the 52nd bit up. This rounding changes the stored value. The exact stored double for 0.1 is 0.1000000000000000055511151231257827021181583404541015625. That is the number the computer actually holds. The tiny error is 5.55×10⁻¹⁷, which is about one part in 10¹⁶.
Now add 0.1 ten times using floating-point arithmetic. Each addition introduces another rounding error because the intermediate sums also cannot be stored exactly. After ten additions the accumulated error reaches roughly 1×10⁻¹⁶, producing 0.9999999999999999 instead of 1.0. This is not a bug. It is the inevitable result of storing an approximation and then performing arithmetic on approximations.
Calculation Steps
The specific failure case is a financial calculation. If a bank system uses double-precision floating-point to track cents, adding 0.10 ten times should give 1.00. The system stores 0.10 as the approximation 0.1000000000000000055511151231257827021181583404541015625. After ten additions the accumulated value is 0.9999999999999999. Display that value as currency with two decimal places and the system shows 1.00, hiding the error. But if the system compares the result to 1.00 using an equality check, the comparison fails. The expression (0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 == 1.0) evaluates to false in most programming languages.
To see the exact stored value, use a rounding calculator that shows the IEEE 754 double-precision representation. The stored mantissa for 0.1 is the 52-bit value 1001100110011001100110011001100110011001100110011010₂. That last four bits differ from the infinite pattern because rounding changed bit 52. The difference between the stored value and the true decimal 0.1 is 5.5511151231257827×10⁻¹⁷. Every floating-point operation involving 0.1 starts from this approximation.
The limitation is absolute: any decimal fraction whose denominator is not a power of 2 after simplification cannot be stored exactly in binary. Fractions like 0.2, 0.3, 0.4, 0.6, 0.7, 0.8, and 0.9 all have this problem. The fractions 0.5, 0.25, 0.125, and 0.0625 store exactly because they are powers of 2. Every other decimal fraction with a finite decimal representation is an approximation in binary.
Why the Method Works
The mechanism enters at the point where the computer converts your decimal input to the binary representation stored in floating-point memory. The conversion requires rounding because the infinite binary expansion must fit into a fixed number of mantissa bits. That rounding is where the error enters the storage. The displayed decimal you see later is the result of converting that rounded binary value back to decimal, and the conversion routine usually rounds to a short string like 0.1 for readability, hiding the approximation.
Why Displayed Decimals Can Differ
When you print the stored value, the system applies a decimal conversion that rounds to a specified number of digits. Printing 0.1000000000000000055511151231257827021181583404541015625 with default precision shows 0.1 because the default precision is typically 15 or 16 significant digits. The conversion routine rounds the stored value to 0.1 for display. If you request 17 digits, the system shows 0.10000000000000001. At 20 digits you see the full stored value. The mismatch between displayed decimal and stored value is not a display bug. It is the system choosing readable output over exact representation.
What to Do Instead
Do not use floating-point types for money, counting, or equality comparisons. Use a decimal type that stores the value as an integer scaled by a power of 10. In languages without a decimal type, store cents as an integer and perform arithmetic in integer math. When you must use floating-point, never check equality with ==. Use a tolerance: abs(a - b) < 1e-9. The single most practical action is to treat every floating-point number as an approximation by default and design your code around that fact.
The Single Most Practical Thing
The thing that most often goes wrong is assuming floating-point arithmetic behaves like decimal arithmetic. Developers write (0.1 + 0.2 == 0.3), get false, and spend hours debugging. The fix is simple: stop using floating-point for exact values. Store currency as integer cents. Compare floating-point results with a tolerance. Print with explicit precision. The error is not a bug in the hardware or the language. It is a mathematical property of base-2 representation that will never change.
Common Questions
Why can 0.1 be impossible to store exactly in binary?
The decimal fraction 0.1 has an infinite repeating binary expansion 0.0001100110011...₂. No finite number of bits can capture that infinite pattern exactly. The IEEE 754 standard must round the pattern to fit the mantissa, producing an approximation.
Which decimal fractions store exactly in binary?
Only fractions whose denominator is a power of 2 after simplification store exactly. Examples are 0.5, 0.25, 0.125, 0.0625. Fractions like 0.2, 0.3, 0.4, 0.6, 0.7, 0.8, and 0.9 all require rounding.
What is the exact stored value of 0.1 in double-precision?
The stored double for 0.1 is 0.1000000000000000055511151231257827021181583404541015625. That is the value the computer holds after rounding the infinite binary expansion to 52 mantissa bits.
Why does printing 0.1 show 0.1 and not the exact stored value?
The default print precision in most languages is 15 or 16 significant digits. The system rounds the stored value to 0.1 for display. Requesting 17 or more digits reveals the full stored value.
Does adding 0.1 ten times equal 1.0?
No. Each addition introduces rounding error. After ten additions the accumulated error gives 0.9999999999999999, not 1.0. An equality check (0.1 + 0.1 + ... + 0.1 == 1.0) returns false in most languages.
How do I avoid binary rounding errors in my code?
Use a decimal type or store money as integer cents. When you must use floating-point, never compare with ==. Use a tolerance such as abs(a - b) < 1e-9.
Does single-precision have the same problem?
Yes. Single-precision uses 23 mantissa bits instead of 52, so the rounding error is larger. The stored value for 0.1 in single-precision is 0.100000001, which is a bigger error than the double-precision 0.10000000000000000555.