Skip to content
Community

EXPLORE TOPICS

Editorial approach

New to CoreTrailKit?

Observe your services. Keep the evidence close.

CoreTrailKit
Back to articles
GUIDE/ Java

Java BigDecimal: define equality and scale

Two monetary-looking values print as 2.0 and 2.00, yet a HashSet retains both. BigDecimal’s equals contract includes scale, while its numerical comparison can consider those values equal.

By CoreTrailKit3 min read

01

Compare the two equality operations

Inspect the original values and scales before changing code. BigDecimal equals and compareTo answer different questions. A HashSet uses equality and hashing, so a numerical comparison performed elsewhere does not change its key behavior.

Our fixture constructed decimal values from strings to keep the inputs exact. equals returned false, compareTo returned zero, and a HashSet contained two elements. Those observations follow the value type’s documented contract.

02

Define the domain rule before normalizing

A scale can carry meaning in a measurement or representation-sensitive system. In another domain, a validated fixed-scale amount may be the intended key. Choose that rule explicitly rather than treating one equality operation as universally wrong.

The example uses a two-decimal policy only to make the choice visible. Real currencies, tax calculations, measurements, and stored schemas may require different precision, scale, or rounding. Changing existing key normalization can also require a data migration.

03

Validate the chosen scale without implicit rounding

Save this example as DecimalPolicy.java. The boundary method constructs a BigDecimal from text and requests scale two with RoundingMode.UNNECESSARY. Input requiring a discarded nonzero digit is rejected rather than silently rounded.

The assertions check the original distinction, the normalized keys, and the extra-precision rejection. They do not authorize changing the amount or currency semantics of an existing system.

java · EXAMPLE
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashSet;
import java.util.List;

public class DecimalPolicy {
    static BigDecimal twoDecimalAmount(String input) {
        return new BigDecimal(input).setScale(2, RoundingMode.UNNECESSARY);
    }

    public static void main(String[] args) {
        var a = new BigDecimal("2.0");
        var b = new BigDecimal("2.00");
        if (a.equals(b) || a.compareTo(b) != 0) throw new AssertionError();
        if (new HashSet<>(List.of(a, b)).size() != 2) throw new AssertionError();
        var normalized = new HashSet<>(List.of(
            twoDecimalAmount("2.0"), twoDecimalAmount("2.00")));
        if (normalized.size() != 1) throw new AssertionError();
        boolean rejected = false;
        try { twoDecimalAmount("2.001"); }
        catch (ArithmeticException expected) { rejected = true; }
        if (!rejected) throw new AssertionError("Unexpected rounding");
        System.out.println("raw keys=2; normalized keys=" + normalized.size());
    }
}

04

Verify key behavior and rejected input

Compile and run the example. It should print raw keys=2; normalized keys=1. The program also checks that 2.001 triggers ArithmeticException under the declared policy.

For the real application, test the values entering maps, sets, database keys, serialization, and comparisons. Inconsistent normalization at different boundaries can leave one layer deduplicating values that another layer still treats as distinct.

sh · EXAMPLE
javac -d out DecimalPolicy.java
java -cp out DecimalPolicy

05

Keep numerical comparison and identity separate

Use compareTo when the business operation asks a numerical ordering question, but do not assume that changes HashSet equality. Conversely, preserving scale-sensitive identity may be correct for a different workflow.

Record the chosen representation, parsing rule, rounding policy, and rejected cases. Avoid converting through binary floating-point just to standardize formatting; that introduces another representation decision and can lose the decimal value the application intended to preserve.

Know the limits

Reproduced with Eclipse Temurin OpenJDK 25.0.4.1+1 in a disposable Linux container with no external network. BigDecimal values 2.0 and 2.00 compared numerically equal but formed two HashSet keys. A deliberate two-decimal normalization reduced them to one key and rejected 2.001 without rounding. This is an illustrative amount policy, not a universal currency or accounting rule.

Sources & further reading

A technical publication by CoreTrailKit.

Browse all articles

CONTINUE EXPLORING