What is the difference between BigDecimal.equals() and BigDecimal.compareTo() when comparing two values, and which should you use to test numeric equality?
answer
- equals() = value AND scale
- compareTo() = value only, ignores scale
- 2.0 vs 2.00: equals false, compareTo 0
- Use compareTo() == 0 for numeric equality
- Internal = unscaled BigInteger + scale
basics
~20 sequals() also checks scale, so 2.0 is not equal to 2.00. compareTo() ignores scale and compares the actual number, so 2.0 compareTo 2.00 returns 0. To check if two numbers are numerically equal, use compareTo() == 0.
solid answer
~40 sBigDecimal stores both an unscaled integer and a scale (number of digits after the decimal point). equals() requires both the numeric value AND the scale to match, so new BigDecimal("2.0").equals(new BigDecimal("2.00")) is false even though they represent the same number. compareTo() compares only the mathematical value and ignores scale, so the same pair returns 0 (equal). The rule: use compareTo(other) == 0 whenever you mean 'is this the same number'. Reserve equals() for when you specifically care that the representation, including trailing zeros / precision, is identical. This mismatch is the classic gotcha because it means BigDecimal's equals and compareTo are inconsistent, which also breaks usage in hash-based collections.
code
java · 10 linesBigDecimal a = new BigDecimal("2.0");
BigDecimal b = new BigDecimal("2.00");
System.out.println(a.equals(b)); // false (scale 1 != scale 2)
System.out.println(a.compareTo(b)); // 0 (same numeric value)
System.out.println(a.compareTo(b) == 0); // true -> numeric equality
// Make equals agree by normalizing scale:
System.out.println(
a.stripTrailingZeros().equals(b.stripTrailingZeros())); // truego deeper
Knows equals() returns false for 2.0 vs 2.00 and that compareTo() == 0 is the right way to test numeric equality.
Can explain the internal unscaled-value + scale representation and why equals() factors in scale.
Articulates that BigDecimal deliberately breaks the equals/compareTo consistency recommendation and knows the downstream HashSet/HashMap and normalization implications.
Can reason about when precision-sensitive equality (equals) is actually desired vs numeric equality, and set team conventions (e.g. always normalize scale or always use compareTo) plus enforcement via static analysis.
## What BigDecimal is `BigDecimal` is a Java class for **arbitrary-precision decimal numbers**. Unlike `double` (which is binary floating-point and can't represent 0.1 exactly), `BigDecimal` represents numbers exactly as a decimal. Internally it stores two things: - an **unscaled value**: a `BigInteger` (an integer of any size), and - a **scale**: an `int` saying how many digits sit to the right of the decimal point. The actual number it represents is `unscaledValue × 10^(-scale)`. Example: - `new BigDecimal("2.0")` → unscaled value `20`, scale `1` → 20 × 10⁻¹ = 2.0 - `new BigDecimal("2.00")` → unscaled value `200`, scale `2` → 200 × 10⁻² = 2.00 Both represent the **same mathematical number** (two), but they have **different internal representations** (different unscaled value and different scale). ## equals(): representation equality `BigDecimal.equals(Object)` returns `true` only when **both the unscaled value and the scale are equal**. So: ``` new BigDecimal("2.0").equals(new BigDecimal("2.00")) // false ``` This is intentional. The Javadoc explicitly says two BigDecimals are equal only if they are equal in value **and** scale. equals() treats `2.0` and `2.00` as different objects because they carry different precision information (the number of significant trailing zeros). ## compareTo(): numeric equality `BigDecimal.compareTo(BigDecimal)` ignores scale and compares only the **mathematical value**. It returns a negative int, zero, or a positive int (the standard `Comparable` contract). So: ``` new BigDecimal("2.0").compareTo(new BigDecimal("2.00")) // 0 (equal value) ``` A return of `0` means "these are the same number". ## The rule - **To ask "are these the same number?"** → use `a.compareTo(b) == 0`. - **To ask "are these the exact same value AND precision?"** → use `a.equals(b)`. In the vast majority of business code (money, totals, comparisons) you want numeric equality, so you should use `compareTo() == 0`, not `equals()`. ## Why this matters: the inconsistency The general `Object.equals`/`Comparable.compareTo` convention recommends that `a.compareTo(b) == 0` should be *consistent with* `a.equals(b)`. `BigDecimal` deliberately **violates** this recommendation (the Javadoc warns about it). That inconsistency is the root of two common bugs: 1. Direct equality checks giving "wrong" answers when scales differ. 2. Using BigDecimal as a key in `HashSet`/`HashMap` (see the related question) — because hashing is built on `equals()`, `2.0` and `2.00` land in different buckets. ## Normalizing scale If you need consistent behavior, you can normalize. `stripTrailingZeros()` removes trailing zeros, and `setScale(n, RoundingMode)` forces a fixed scale. After normalizing both operands to the same scale, `equals()` and `compareTo()` agree. (Note: `stripTrailingZeros()` on `0.00` historically produced `0E-8` rather than `0` in some JDKs — be careful with zero.) ## Summary table | | `2.0` vs `2.00` | |---|---| | `equals()` | false (scales differ) | | `compareTo() == 0` | true (same value) |
- How can you make equals() and compareTo() agree on two BigDecimals?Normalize their scales first — e.g. call stripTrailingZeros() on both, or setScale(n, RoundingMode) to force the same scale. Once unscaled value and scale both match, equals() returns true and compareTo() returns 0.
- Does compareTo() ever throw or behave oddly with NaN-like values?No. BigDecimal has no NaN or infinity — it's exact arbitrary-precision decimal. compareTo() always returns a clean -1/0/1 ordering for any two non-null BigDecimals.
Think of equals() as comparing two price tags character-by-character: '$2.0' and '$2.00' are different strings. compareTo() is like asking the cashier 'is it the same price?' — both ring up as two dollars.
saying these in an interview costs you the question
- Claiming equals() and compareTo() always agree for BigDecimal (they intentionally don't).
- Saying 2.0 equals 2.00 via equals() — it returns false.
- Believing BigDecimal stores numbers as a single double internally.
- Using == to compare BigDecimal objects (compares references, not value).