杰瑞科技汇

Java中Decimal与Double,到底该用哪个?

Of course! This is a fundamental and crucial concept in Java. Let's break it down.

Java中Decimal与Double,到底该用哪个?-图1
(图片来源网络,侵删)

The core idea is this:

  • double is for speed and scientific calculations. It's a binary floating-point type. It's fast but not precise for money.
  • BigDecimal is for precision and financial calculations. It's a decimal-based type. It's slower but can represent numbers exactly, as you'd expect on paper.

The double Type (The Problem)

double is a primitive data type in Java that represents a 64-bit double-precision floating-point number. It follows the IEEE 754 standard, which stores numbers in binary (base-2).

Why is double problematic for decimal numbers?

Humans think in base-10 (decimal). Computers think in base-2 (binary). Many simple decimal numbers cannot be represented perfectly in binary.

Example: The Classic 1 + 0.2 Problem

Java中Decimal与Double,到底该用哪个?-图2
(图片来源网络,侵删)
public class DoubleProblem {
    public static void main(String[] args) {
        double a = 0.1;
        double b = 0.2;
        // We expect this to be 0.3, right?
        double sum = a + b;
        System.out.println("a + b = " + sum); // Output: a + b = 0.30000000000000004
        System.out.println("Is sum equal to 0.3? " + (sum == 0.3)); // Output: Is sum equal to 0.3? false
    }
}

Why does this happen?

The number 1 in decimal is a repeating fraction in binary: 00011001100110011.... The computer has to store an approximation of this infinite sequence. The same is true for 2. When you add these two approximations together, the tiny rounding errors accumulate, resulting in 30000000000000004.

Other double characteristics:

  • Speed: Very fast. It's a hardware-supported type used by the CPU for mathematical operations. This makes it ideal for scientific simulations, graphics, and other non-financial applications.
  • Range: Huge range (from ~-1.8e308 to ~1.8e308).
  • Precision: About 15-17 significant decimal digits of precision.

The BigDecimal Class (The Solution)

java.math.BigDecimal is a class, not a primitive. It solves the precision problem by representing numbers in base-10 (decimal).

Java中Decimal与Double,到底该用哪个?-图3
(图片来源网络,侵删)

How does BigDecimal work?

It stores a number as two parts:

  1. An unscaled integer value (the digits).
  2. A scale (the number of digits to the right of the decimal point).

Because it operates on decimal digits, it can represent 1, 2, and 3 exactly.

Example: Fixing the 1 + 0.2 Problem

import java.math.BigDecimal;
public class BigDecimalSolution {
    public static void main(String[] args) {
        // Use the String constructor for best precision!
        BigDecimal a = new BigDecimal("0.1");
        BigDecimal b = new BigDecimal("0.2");
        // Use the add() method for arithmetic
        BigDecimal sum = a.add(b);
        System.out.println("a + b = " + sum); // Output: a + b = 0.3
        System.out.println("Is sum equal to 0.3? " + (sum.compareTo(new BigDecimal("0.3")) == 0)); // Output: Is sum equal to 0.3? true
    }
}

Crucial BigDecimal Rules:

  1. Always use the String constructor. Never pass a double to the BigDecimal constructor if you care about precision.

    // BAD: This inherits the imprecision from the double!
    BigDecimal bad = new BigDecimal(0.1); // Creates BigDecimal("0.1000000000000000055511151231257827021181583404541015625")
    // GOOD: This creates the exact decimal you intended.
    BigDecimal good = new BigDecimal("0.1"); // Creates BigDecimal("0.1")
  2. Use valueOf() as a safe alternative. The BigDecimal.valueOf(double val) method is a convenient and safe way to convert a double. It behaves as if it were new BigDecimal(Double.toString(val)), which is generally more accurate than the double constructor.

    BigDecimal safe = BigDecimal.valueOf(0.1); // Creates BigDecimal("0.1")
  3. Use compareTo() for equality checks. BigDecimal is an object, and you should never use equals() to check for numerical equality. equals() also checks for scale (e.g., 0 is not equal to 10), which is usually not what you want in financial math.

    • a.equals(b) -> true only if value and scale are the same.
    • a.compareTo(b) -> 0 if their numerical values are the same. This is the correct method.
  4. Use setScale() for rounding. Financial calculations almost always require rounding. BigDecimal gives you full control over this.

    BigDecimal price = new BigDecimal("99.995");
    // Round to 2 decimal places using the standard "HALF_UP" rounding mode (like you learned in school)
    BigDecimal roundedPrice = price.setScale(2, RoundingMode.HALF_UP);
    System.out.println(roundedPrice); // Output: 100.00

Comparison Table

Feature double BigDecimal
Type Primitive Object (java.math.BigDecimal)
Precision Binary (Base-2). Prone to rounding errors with decimals. Decimal (Base-10). Can represent decimal numbers exactly.
Performance Very Fast. Native CPU support. Slower. Operations are performed in software.
Use Case Scientific computing, graphics, general-purpose math where performance is key and minor precision loss is acceptable. Financial calculations, currency, any situation where exact decimal precision is required.
Arithmetic Operators (, , , ) Methods (add(), subtract(), multiply(), divide())
Equality Check compareTo() == 0 (not equals())
Construction double d = 1.23; new BigDecimal("1.23") or BigDecimal.valueOf(1.23)

When to Use Which?

✅ Use double when:

  • You are doing scientific or engineering calculations (e.g., physics simulations, 3D graphics).
  • Performance is a critical factor.
  • You are working with values that are not "decimal money" (e.g., sensor readings, percentages).
  • The small, inherent imprecision of floating-point numbers is not a problem for your application.

✅ Use BigDecimal when:

  • You are working with money, currency, or financial data.
  • You need to guarantee that 0 / 2.0 is exactly 0, not 999999999999999.
  • You need precise control over rounding and decimal places.
  • You are reading numbers from a source that guarantees decimal precision (like a CSV file or database column of type DECIMAL or NUMERIC).

Final Example: The Cost of a Shopping Cart

import java.math.BigDecimal;
import java.math.RoundingMode;
public class ShoppingCart {
    public static void main(String[] args) {
        // Using double (BAD for money)
        double applePriceDouble = 0.1;
        double orangePriceDouble = 0.2;
        double totalDouble = applePriceDouble + orangePriceDouble;
        System.out.println("--- Using double ---");
        System.out.printf("Apple: $%.2f, Orange: $%.2f, Total: $%.2f%n", applePriceDouble, orangePriceDouble, totalDouble);
        System.out.println("Total is correct? " + (totalDouble == 0.3)); // false
        System.out.println();
        // Using BigDecimal (GOOD for money)
        BigDecimal applePrice = new BigDecimal("0.10");
        BigDecimal orangePrice = new BigDecimal("0.20");
        // Perform calculations
        BigDecimal subTotal = applePrice.add(orangePrice);
        // Add 8% sales tax
        BigDecimal taxRate = new BigDecimal("0.08");
        BigDecimal tax = subTotal.multiply(taxRate).setScale(2, RoundingMode.HALF_UP);
        // Calculate final total
        BigDecimal total = subTotal.add(tax);
        System.out.println("--- Using BigDecimal ---");
        System.out.printf("Apple: $%.2f, Orange: $%.2f, Subtotal: $%.2f%n", applePrice, orangePrice, sub
分享:
扫描分享到社交APP
上一篇
下一篇