Java uses the IEEE 754 standard for floating-point arithmetic. Understanding how decimals are stored in binary explains why precision errors happen and why double is the default choice.
Demystifying the Fraction-to-Binary Math
The Quote Explained
“Add the fraction to itself and retain the integer until the entire sequence becomes 0”
This describes the standard algorithm for converting a decimal fraction (like 0.625) into binary. Adding a number to itself () is mathematically identical to multiplying by 2 (). Proof
Step-by-Step Example: Converting to Binary
Multiply the fraction by 2.
Separate the result into its Integer part (which becomes our binary digit: 0 or 1) and the New Fraction.
Repeat the process with the new fraction until it becomes 0 (or until you run out of precision bits).
Step
Operation
Result
Extracted Integer (Binary Digit)
Remaining Fraction
1
1
2
0
3
1
(Stop!)
Reading the extracted integers from top to bottom gives us ().
Why some numbers never terminate
Try this with 0.3:
(Int: 0, Frac: )
(Int: 1, Frac: )
(Int: 0, Frac: )
(Int: 0, Frac: )
(Int: 1, Frac: ) — Infinite loop begins!
This is why fractions like 0.1 or 0.3 cannot be represented precisely in binary, leading to classic floating-point inaccuracies like 0.1 + 0.2 = 0.30000000000000004.
Representation in Java (float vs. double)
Java implements the IEEE 754 binary floating-point standard. Every floating-point number is stored using three components:
Sign bit (): for positive, for negative.
Exponent (): Represents the magnitude (scaled/biased).
Mantissa / Fraction (): Represents the actual precision digits.
If you write a raw decimal literal in Java (e.g., double x = 5.2;), the compiler automatically treats it as a double.
// 5.2 is treated as a double by default. // Compilation error if you try to assign directly to a float without a cast or 'f' suffix:float f = 5.2f; // Correct suffix
Reasons why double is the default
Precision Requirements:
Modern applications require high accuracy. float’s ~7 digits of precision accumulate rounding errors very quickly in standard business or scientific calculations.
Historical & Hardware Architecture:
Modern CPU architectures (64-bit ALUs/Floating-Point Units) are heavily optimized for 64-bit double-precision calculations natively.
Avoiding Accidental Data Loss:
Defaulting to the higher-precision type prevents silent truncation of data, reserving float strictly for specialized scenarios where memory constraints are absolute (e.g., 3D graphics buffers, massive arrays of audio data, or machine learning model weights).
Because of this hidden binary rounding, never compare floats directly using ==. For example:
(0.1f + 0.2f) == 0.3f can actually evaluate to false depending on the JVM and compiler optimizations, because the sum doesn’t precisely bit-match 0.3f. Always use an epsilon (threshold) for comparisons:
boolean isEqual = Math.abs((a + b) - 0.3f) < 0.00001f;
Best Practice for Financial Data
Never use float or double for currency or exact financial calculations due to rounding artifacts. Always use java.math.BigDecimal instead.
BigDecimal price = new BigDecimal("0.1");BigDecimal tax = new BigDecimal("0.2");BigDecimal total = price.add(tax); // Exactly 0.3