Skip to Content
Course content

279: BigInteger and BigDecimal for Precise Arithmetic

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Why can't I just use double or float for money?

This is the first question everyone asks, and it's a fair one. Why bother with clunky classes when you have double? The problem is that floating-point numbers are stored in binary, and some decimal fractions—like 0.1—cannot be represented exactly in binary. It's similar to how 1/3 becomes 0.333... in decimal; it never quite ends.

If you're building a banking app and you add 0.1 to a balance ten thousand times using a double, you'll eventually see a weird result like 1000.00000000015. In a professional setting, that's a bug that gets you a stern talking-to from the auditing team. I've seen it happen in production systems where "ghost pennies" appeared out of nowhere because someone used double for currency. That's where BigDecimal comes in; it stores numbers as an arbitrary-precision integer and a scale, meaning it treats the number exactly as you wrote it.

// The "Double Trap"
double price = 0.1;
double total = price + price + price; 
System.out.println(total); // Might print 0.30000000000000004

// The Precise Way
BigDecimal precisePrice = new BigDecimal("0.1");
BigDecimal preciseTotal = precisePrice.add(precisePrice).add(precisePrice);
System.out.println(preciseTotal); // Prints exactly 0.3

How do I actually do math if I can't use + or *?

It's a bit of a shock the first time you try to use + with a BigDecimal and the compiler screams at you. Because these are objects and not primitives, you have to use method calls. It feels verbose at first, but it's the price we pay for precision.

One thing you absolutely cannot forget: BigDecimal and BigInteger are immutable. I've seen plenty of juniors write balance.add(deposit) and then wonder why the balance didn't change. The add() method doesn't modify the existing object; it returns a brand new one. You have to assign that result back to a variable.

BigDecimal balance = new BigDecimal("100.00");
BigDecimal deposit = new BigDecimal("50.50");

// WRONG: This does nothing to the balance variable
balance.add(deposit); 

// RIGHT: Assign the result to the variable
balance = balance.add(deposit); 

BigDecimal multiplier = new BigDecimal("1.05"); // 5% interest
balance = balance.multiply(multiplier);

What is this ArithmeticException during division?

If you try to divide 1 by 3 using BigDecimal without specifying how to handle the remainder, Java will throw an ArithmeticException. Why? Because 1/3 is a non-terminating decimal. Java refuses to guess how many decimal places you want; it would rather crash than give you an imprecise answer.

To fix this, you have to provide a RoundingMode. I usually recommend HALF_UP for financial applications—that's the standard "round .5 up" logic we all learned in school. You can also use FLOOR or CEILING depending on whether you're calculating something like a tax bracket where you must always round in a specific direction.

BigDecimal one = new BigDecimal("1");
BigDecimal three = new BigDecimal("3");

// This would throw: ArithmeticException: Non-terminating decimal expansion
// BigDecimal result = one.divide(three); 

// This is the professional way: 2 decimal places, rounding half up
BigDecimal result = one.divide(three, 2, RoundingMode.HALF_UP); 
System.out.println(result); // 0.33

When should I use BigInteger instead of BigDecimal?

Simple: use BigInteger when you don't have a decimal point but your numbers are larger than what a long can handle (roughly 9 quintillion). You'll rarely need this in a standard business CRUD app, but it's essential for cryptography, hashing algorithms, or scientific simulations.

For example, if you're implementing an RSA encryption algorithm, you're dealing with prime numbers that are hundreds of digits long. A long would overflow instantly. BigInteger will keep growing as long as your computer has RAM to support it.

// A number way too big for a long
BigInteger massiveNumber = new BigInteger("9223372036854775808123456789");
BigInteger anotherHugeOne = new BigInteger("12345678901234567890");

BigInteger product = massiveNumber.multiply(anotherHugeOne);
System.out.println("The result is far too large for any primitive: " + product);



📋 Practical Task

Exercise: Building a Precise Invoice Calculator

You are tasked with creating a small utility that calculates the final total for a customer invoice. You must ensure that no precision is lost during the calculation of discounts and taxes.

Requirements:

  • Start with a subtotal of 1250.75.
  • Apply a discount of 15% (multiply by 0.85).
  • Apply a sales tax of 8.25% to the discounted amount.
  • The final result must be rounded to exactly 2 decimal places using RoundingMode.HALF_UP.
  • Use BigDecimal for all calculations. Do not use double or float at any point in the logic.

Expected Output: Your program should print the final total formatted to two decimal places.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.