Back to Blog
Java

Java Double vs BigDecimal: Precision and Performance

java double vs bigdecimal: Compare Java's double and BigDecimal for exact decimal arithmetic, precision, performance, and when each type is the right choice.

BigDecimalfloating-point precisionIEEE 754Java arithmeticrounding modesmonetary calculations
Illustration comparing Java double and BigDecimal numeric types with a precision scale metaphor.

The java double vs bigdecimal decision usually surfaces when a calculation produces a result that looks wrong. In Java, 0.1 + 0.2 evaluated as double gives 0.30000000000000004, while the same operation on BigDecimal values gives exactly 0.3. Neither type is universally better; the correct choice depends on whether the calculation needs exact decimal arithmetic or can tolerate binary floating-point approximation.

How double Represents Decimal Values

double is a 64-bit IEEE 754 binary floating-point primitive. It stores a sign, an 11-bit exponent, and a 52-bit mantissa, all in base 2. Because the mantissa is binary, most decimal fractions cannot be represented exactly. The value 0.1 is stored as the closest binary fraction, which is slightly larger than the decimal 0.1. This approximation is invisible in most output because System.out.println rounds, but it becomes visible when arithmetic compounds the error.

double a = 0.1; double b = 0.2; System.out.println(a + b); // 0.30000000000000004

The error is small, but it is not zero. In a loop that accumulates many such values, the difference can grow large enough to break equality checks or produce incorrect totals.

What BigDecimal Changes About Arithmetic

BigDecimal represents a signed decimal number as an unscaled integer value combined with a scale. new BigDecimal("100.50") stores the unscaled value 10050 with scale 2, meaning the value is 100.50. Arithmetic is performed on the unscaled integers, so decimal fractions stay exact as long as the operation does not require rounding.

BigDecimal x = new BigDecimal("0.1"); BigDecimal y = new BigDecimal("0.2"); System.out.println(x.add(y)); // 0.3

Addition, subtraction, and multiplication are exact when the result fits within the available precision. Division is the operation that requires care, because a non-terminating decimal result cannot be represented without a rounding mode.

Constructing BigDecimal Without Losing Precision

The constructor you choose determines whether the value is exact. new BigDecimal(0.1) converts the double's binary approximation into a decimal expansion, producing a long string of digits. new BigDecimal("0.1") parses the decimal string directly and is exact.

BigDecimal fromDouble = new BigDecimal(0.1); System.out.println(fromDouble); // 0.1000000000000000055511151231257827021181583404541015625 BigDecimal fromString = new BigDecimal("0.1"); System.out.println(fromString); // 0.1

For values that come from user input, configuration files, or database columns, always use the string constructor or BigDecimal.valueOf(double). The valueOf method uses the canonical string representation of the double, which avoids the long expansion while still accepting a primitive.

equals, compareTo, and the Scale Trap

BigDecimal.equals considers scale, so new BigDecimal("1.0") is not equal to new BigDecimal("1.00") even though the numeric values are identical. compareTo ignores scale and compares numeric value only.

BigDecimal one = new BigDecimal("1.0"); BigDecimal oneHundred = new BigDecimal("1.00"); System.out.println(one.equals(oneHundred)); // false System.out.println(one.compareTo(oneHundred)); // 0

When using BigDecimal as a key in a HashMap or storing it in a HashSet, the scale-sensitive equals and hashCode behavior can cause unexpected misses. Use compareTo for ordering and numeric comparison, and normalize scale explicitly when scale-insensitive equality is required.

Performance and Memory Tradeoffs

double is a primitive: 8 bytes of storage, no heap allocation, and arithmetic that maps directly to CPU floating-point instructions. BigDecimal is an object that wraps a BigInteger unscaled value, so every operation allocates objects and performs integer arithmetic on arbitrary-precision values. In a tight loop that performs millions of operations, BigDecimal can be orders of magnitude slower and consume significantly more memory.

This does not mean BigDecimal is unsuitable for production. It means the cost is justified only when exact decimal arithmetic is a requirement. For scientific computation, graphics, simulation, or any performance-sensitive numeric code, double is the appropriate default. For monetary calculations, tax, interest, or any value that must round to a specific decimal scale, the cost of BigDecimal is the price of correctness.

CriteriondoubleBigDecimal
RepresentationIEEE 754 binary64Unscaled integer + scale
Decimal exactnessApproximate for most fractionsExact for decimal fractions
Storage8-byte primitiveObject with BigInteger value
Arithmetic costLowHigher, allocates per operation
DivisionIEEE roundingRequires scale and RoundingMode
Best fitScientific, performance-sensitiveFinancial, exact decimal math

Choosing Between double and BigDecimal by Scenario

The decision criteria are concrete:

  • Use double when the values are measurements, ratios, or results of scientific formulas where a small relative error is acceptable and performance matters.
  • Use BigDecimal when the values represent money, quantities that must round to a fixed decimal scale, or anything that will be compared for exact equality after arithmetic.
  • Use double when the calculation is part of a hot path such as signal processing or physics simulation.
  • Use BigDecimal when the result feeds into an audit trail, invoice, or legal document where the exact decimal value must be reproducible.

A common middle ground is to use double for intermediate scientific computation and convert to BigDecimal only at the boundary where the value is persisted or displayed. This keeps the hot path fast while preserving exactness at the points where it matters.

Division Requires an Explicit Rounding Mode

Dividing two BigDecimal values with a non-terminating result throws ArithmeticException unless you provide a scale and a RoundingMode.

BigDecimal one = new BigDecimal("1"); BigDecimal three = new BigDecimal("3"); // one.divide(three) throws ArithmeticException BigDecimal result = one.divide(three, 4, RoundingMode.HALF_UP); System.out.println(result); // 0.3333

The choice of rounding mode is a business decision, not a technical one. HALF_UP matches common rounding expectations in financial contexts, while HALF_EVEN avoids bias in statistical calculations. Define the rounding mode once in a shared utility or constant so that every division in the codebase behaves consistently. Mixing rounding modes across modules is a common source of subtle discrepancies in totals and reports.

java double vs bigdecimal: Practical Usage and Code Examples | RYUSLOG DEV