Fundamentals 12 min read

Mastering Java BigDecimal: Precise Arithmetic, APIs, and Best Practices

This article explains why commercial calculations should use Java's BigDecimal for exact decimal arithmetic, details its structure, shows how to construct instances safely, covers common API methods, rounding modes, number formatting, and provides practical code examples.

Programmer DD
Programmer DD
Programmer DD
Mastering Java BigDecimal: Precise Arithmetic, APIs, and Best Practices

1. Introduction

A junior developer discovered a price‑calculation error caused by floating‑point imprecision; the article stresses that commercial calculations must use BigDecimal because binary floating‑point cannot precisely represent decimal values. Effective Java also recommends BigDecimal for exact arithmetic.

2. BigDecimal Overview

BigDecimal

is an immutable arbitrary‑precision signed decimal number composed of an unscaled integer ( BigInteger) and a scale (the number of digits to the right of the decimal point).

For example, BigDecimal 3.14 has an unscaled value of 314 and a scale of 2.

3. Constructing BigDecimal Instances

Instances can be created from String, char[], int, long, BigInteger, and double. Using the double constructor may yield unexpected results because it captures the exact binary representation of the double.

@Test
public void theValueMatches() {
    BigDecimal bdFromString = new BigDecimal("0.12");
    BigDecimal bdFromCharArray = new BigDecimal(new char[]{'3', '.', '1', '4', '1', '5'});
    BigDecimal bdlFromInt = new BigDecimal(42);
    BigDecimal bdFromLong = new BigDecimal(123412345678901L);
    BigInteger bigInteger = BigInteger.probablePrime(100, new Random());
    BigDecimal bdFromBigInteger = new BigDecimal(bigInteger);
    assertEquals("0.12", bdFromString.toString());
    assertEquals("3.1415", bdFromCharArray.toString());
    assertEquals("42", bdlFromInt.toString());
    assertEquals("123412345678901", bdFromLong.toString());
    assertEquals(bigInteger.toString(), bdFromBigInteger.toString());
}

Creating from double can produce a value that does not match the expected decimal representation:

@Test
public void whenBigDecimalCreatedFromDouble_thenValueMayNotMatch() {
    BigDecimal bdFromDouble = new BigDecimal(0.1d);
    assertNotEquals("0.1", bdFromDouble.toString());
}

To obtain the correct decimal, use the String constructor or the static valueOf method:

@Test
public void whenBigDecimalCreatedUsingValueOf_thenValueMatches() {
    BigDecimal bdFromDouble = BigDecimal.valueOf(0.1d);
    BigDecimal bigFromLong = BigDecimal.valueOf(1, 1);
    assertEquals("0.1", bdFromDouble.toString());
    assertEquals("0.1", bigFromLong.toString());
}

4. Common API Methods

abs() : absolute value, scale unchanged.

add(BigDecimal augend) : addition, result scale = max of operands.

subtract(BigDecimal augend) : subtraction, result scale = max of operands.

multiply(BigDecimal multiplicand) : multiplication, result scale = sum of operand scales.

divide(BigDecimal divisor) : division; throws ArithmeticException if non‑terminating unless a scale and rounding mode are specified.

divide(BigDecimal divisor, int roundingMode) : division with explicit rounding mode.

divide(BigDecimal divisor, int scale, int roundingMode) : division with both scale and rounding mode.

remainder(BigDecimal divisor) : remainder, scale unchanged.

divideAndRemainder(BigDecimal divisor) : returns an array {quotient, remainder}, e.g., 23/3 yields {7,2}.

divideToIntegralValue(BigDecimal divisor) : integer part of division, scale unchanged.

max(BigDecimal val) / min(BigDecimal val) : larger or smaller of two values, result scale = larger operand's scale.

movePointLeft(int n) / movePointRight(int n) : shift decimal point left or right, adjusting scale accordingly.

negate() : sign change, scale unchanged.

pow(int n) : exponentiation.

scaleByPowerOfTen(int n) : equivalent to moving the decimal point right by n places (multiply by 10^n).

5. Attribute Extraction

Methods such as precision(), scale(), and signum() retrieve a BigDecimal 's precision, scale, and sign.

@Test
public void whenGettingAttributes_thenExpectedResult() {
    BigDecimal bd = new BigDecimal("-12345.6789");
    assertEquals(9, bd.precision());
    assertEquals(4, bd.scale());
    assertEquals(-1, bd.signum());
}

5.2 Comparison

compareTo

compares numeric values and ignores scale, while equals requires both value and scale to match.

@Test
public void whenComparingBigDecimals_thenExpectedResult() {
    BigDecimal bd1 = new BigDecimal("1.0");
    BigDecimal bd2 = new BigDecimal("1.00");
    BigDecimal bd3 = new BigDecimal("2.0");
    assertTrue(bd1.compareTo(bd3) < 0);
    assertTrue(bd3.compareTo(bd1) > 0);
    assertTrue(bd1.compareTo(bd2) == 0);
    assertTrue(bd1.compareTo(bd3) <= 0);
    assertTrue(bd1.compareTo(bd2) >= 0);
    assertTrue(bd1.compareTo(bd3) != 0);
}
@Test
public void whenEqualsCalled_thenSizeAndScaleMatched() {
    BigDecimal bd1 = new BigDecimal("1.0");
    BigDecimal bd2 = new BigDecimal("1.00");
    assertFalse(bd1.equals(bd2));
}

5.3 Arithmetic Operations

Four basic operations are provided by add, subtract, multiply, and divide:

@Test
public void whenPerformingArithmetic_thenExpectedResult() {
    BigDecimal bd1 = new BigDecimal("4.0");
    BigDecimal bd2 = new BigDecimal("2.0");
    BigDecimal sum = bd1.add(bd2);
    BigDecimal difference = bd1.subtract(bd2);
    BigDecimal quotient = bd1.divide(bd2);
    BigDecimal product = bd1.multiply(bd2);
    assertTrue(sum.compareTo(new BigDecimal("6.0")) == 0);
    assertTrue(difference.compareTo(new BigDecimal("2.0")) == 0);
    assertTrue(quotient.compareTo(new BigDecimal("2.0")) == 0);
    assertTrue(product.compareTo(new BigDecimal("8.0")) == 0);
}

5.4 Rounding

The RoundingMode enum defines eight strategies: UP , DOWN , FLOOR , CEILING , HALF_DOWN , HALF_UP , HALF_EVEN , and UNNECESSARY (throws an exception if rounding would be required).

6. Number Formatting

Java provides NumberFormat and its subclass DecimalFormat for formatting numbers, currencies, and percentages. NumberFormat offers locale‑specific factories such as getInstance, getCurrencyInstance, and getPercentInstance. DecimalFormat uses pattern strings where:

"0" forces a digit (pads with zeros if absent).

"#" displays an optional digit.

"." denotes the decimal point (only once per pattern).

"," inserts grouping separators (must appear before the decimal point).

7. Conclusion

The article summarizes essential knowledge about BigDecimal, recommending it as a reference for precise commercial calculations and encouraging readers to keep the guide handy.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaAPIprecisionArithmeticBigDecimalRoundingNumber Formatting
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.