Double Class
double is Java's default floating-point type: a 64-bit IEEE 754 double-precision number with about 15–16 significant decimal digits and an enormous range. java.lang.Double is its wrapper class, providing constants, parsing, comparison, and tests for special values. double is the right choice for measurements, statistics, scientific values, geometry, and percentages. It is the wrong choice for money and anything else that must match decimal arithmetic exactly.
Declaring double Values
Any literal with a decimal point or exponent is a double:
double price = 499.99;
double rate = 0.075;
double avogadro = 6.022e23;
double tiny = 1.6e-19;
double fromInt = 42; // int widens to double automatically
double explicit = 3d; // optional d/D suffix
How double Stores Numbers
1 bit 11 bits 52 bits
[sign] [ exponent ] [ fraction (mantissa) ]
With 52 stored fraction bits plus an implicit leading 1, a double has 53 bits of precision. That allows exact representation of every integer up to 2⁵³ (9,007,199,254,740,992), and about 15–16 significant decimal digits for other values.
double big = (double) (1L << 53);
System.out.println(big + 1 == big); // true: above 2^53, adding 1 can be lost
Math.ulp(1.0); // 2.220446049250313E-16: gap between 1.0 and the next double
Decimal fractions are approximations
System.out.println(0.1 + 0.2); // 0.30000000000000004
System.out.println(new BigDecimal(2.675));
// 2.67499999999999982236431605997495353221893310546875
The double written as 2.675 is actually slightly less than 2.675, because 2.675 has no exact binary form. new BigDecimal(double) reveals the exact stored value.
How doubles Are Printed
Double.toString (used by println and concatenation) prints the shortest decimal that uniquely identifies the value, switching to scientific notation for very large or very small magnitudes:
System.out.println(100.0); // 100.0
System.out.println(12345678.9); // 1.23456789E7 (≥ 10^7: scientific)
System.out.println(0.001); // 0.001
System.out.println(0.0001); // 1.0E-4 (< 10^-3: scientific)
For user-facing output, always format explicitly:
String.format("%.2f", 12345678.9); // "12345678.90"
String.format("%,.2f", 12345678.9); // "12,345,678.90"
Constants and Special Values
| Constant | Value |
|---|---|
Double.MAX_VALUE | 1.7976931348623157E308 |
Double.MIN_VALUE | 4.9E-324 (smallest positive value) |
Double.MIN_NORMAL | 2.2250738585072014E-308 |
Double.POSITIVE_INFINITY, NEGATIVE_INFINITY | Results of overflow or x / 0.0 |
Double.NaN | Result of undefined operations such as 0.0 / 0.0 or Math.sqrt(-1) |
Double.isNaN(0.0 / 0.0); // true
Double.isInfinite(1.0 / 0); // true
Double.isFinite(42.0); // true
Division by zero with doubles does not throw: it produces Infinity or NaN, which then silently spread through later calculations. Check results with Double.isFinite at boundaries where invalid values must not pass, such as before storing a computed average or sending it to a client.
NaN != NaN is always true, so test with Double.isNaN, never ==. As with Float, Double.compare and Double.equals treat NaN as equal to itself and order -0.0 before 0.0, which is what sorting and collections need.
Rounding
| Method | Rule | 2.5 | -2.5 | -2.7 |
|---|---|---|---|---|
Math.round(x) | Nearest; halves round up (toward +∞); returns long | 3 | -2 | -3 |
Math.rint(x) | Nearest; halves round to the even neighbor; returns double | 2.0 | -2.0 | -3.0 |
Math.floor(x) | Down, toward −∞ | 2.0 | -3.0 | -3.0 |
Math.ceil(x) | Up, toward +∞ | 3.0 | -2.0 | -2.0 |
(long) x | Truncate toward zero | 2 | -2 | -2 |
To round to a number of decimal places for display, use formatting (%.2f) or BigDecimal. Avoid tricks like Math.round(x * 100) / 100.0 for anything important: the intermediate multiplication introduces its own binary error.
Note that String.format("%.2f", 2.675) produces "2.68": Java's formatter rounds based on the shortest decimal form of the double (2.675), not its exact binary value. BigDecimal.valueOf(2.675).setScale(2, RoundingMode.HALF_UP) gives the same result explicitly. When rounding rules are part of a business requirement, use BigDecimal from the start.
Parsing and Converting
double a = Double.parseDouble("1499.99");
Double b = Double.valueOf(" 3.5 "); // leading/trailing whitespace is allowed
double c = Double.parseDouble("1e-3"); // 0.001
double nan = Double.parseDouble("NaN"); // NaN
double d = Double.parseDouble("1,234.5".replace(",", "")); // grouping separators must be removed
Unlike Integer.parseInt, parseDouble ignores surrounding whitespace. It does not accept locale-specific formats such as 1.234,5; use NumberFormat for those. Invalid text throws NumberFormatException.
Comparing doubles
Exact comparison with == is fragile after arithmetic. A robust check combines a relative tolerance (for large values) with an absolute tolerance (for values near zero):
static boolean approxEqual(double a, double b, double relTol, double absTol) {
double diff = Math.abs(a - b);
return diff <= absTol || diff <= relTol * Math.max(Math.abs(a), Math.abs(b));
}
approxEqual(0.1 + 0.2, 0.3, 1e-9, 1e-12); // true
approxEqual(1e20 + 1e5, 1e20, 1e-9, 1e-12); // true: a difference of 1e5 is tiny relative to 1e20
approxEqual(0.001, 0.002, 1e-9, 1e-12); // false
A fixed absolute tolerance such as 1e-9 alone fails for large numbers (where neighboring doubles are far more than 1e-9 apart) and can be too loose for very small ones. Testing frameworks provide similar helpers, such as JUnit's assertEquals(expected, actual, delta).
double Versus BigDecimal
double | BigDecimal | |
|---|---|---|
| Representation | Binary approximation | Exact decimal digits |
| Speed | Hardware arithmetic, very fast | Software arithmetic, much slower |
| Memory | 8 bytes | An object with an internal integer and scale |
| Rounding | Implicit after every operation | Explicit, with a chosen RoundingMode |
| Good for | Science, statistics, graphics, measurements | Money, tax, invoices, accounting |
Practical Example: Response-Time Statistics
A monitoring tool computes summary statistics for API response times and flags outliers.
public class Db {
record Stats(double mean, double stdDev, double min, double max) { }
static Stats stats(double[] xs) {
double sum = 0, min = Double.POSITIVE_INFINITY, max = Double.NEGATIVE_INFINITY;
for (double x : xs) {
sum += x;
min = Math.min(min, x);
max = Math.max(max, x);
}
double mean = sum / xs.length;
double sq = 0;
for (double x : xs) sq += (x - mean) * (x - mean);
return new Stats(mean, Math.sqrt(sq / (xs.length - 1)), min, max);
}
public static void main(String[] args) {
double[] responseMs = {120.5, 98.2, 143.0, 110.7, 101.3, 512.9, 118.4};
Stats s = stats(responseMs);
System.out.printf("mean=%.2f ms sd=%.2f ms min=%.1f max=%.1f%n", s.mean(), s.stdDev(), s.min(), s.max());
for (double x : responseMs) {
double z = (x - s.mean()) / s.stdDev();
if (Math.abs(z) > 2) System.out.printf("Outlier: %.1f ms (z = %.2f)%n", x, z);
}
System.out.println(Double.isNaN(stats(new double[0]).mean()));
}
}
Output:
mean=172.14 ms sd=150.99 ms min=98.2 max=512.9
Outlier: 512.9 ms (z = 2.26)
true
This is exactly the kind of work double is designed for: averages, standard deviations, and ratios where tiny binary rounding errors are irrelevant to the result. Several details are worth noting:
- Starting values for min and max.
minandmaxstart atPOSITIVE_INFINITYandNEGATIVE_INFINITY, so the first real value always replaces them. - Sample standard deviation. It divides by
n - 1, and the z-score expresses how many standard deviations each reading lies from the mean. The single slow request at 512.9 ms stands out. - Empty input. The last line shows a pitfall. Passing an empty array divides
0.0by0and returnsNaNinstead of throwing, so production code should check the length first or validate the result withDouble.isFinite.