Home About

BigDecimal - why do we need it?

Why do we need it?

This question is the pretty standard one in algorithmic interview questions. So, why do we need it? I think it’s fair to ask. The main reason behind this is knowing how floating point numbers are being stored. Usually, you don’t need it to use this knowledge otherwise to know “solution” but it’s good to know.

The main reason is the precision or rather, the lack of it. In environments where we need to have big numbers very precise like face values and when it’s multiplied by exchange ratios, the potential difference can be very signifcant. Therefore potential financial risk can be signifcant, too.

The standard double (or even more float) is stored with 8 bytes, the standard float consumes 4 bytes. However, it doesn’t explain yet the reason why. Let’s take look it.

Root causes

Floating point precision

Let’s take a look how typical double looks like.

So, we see that the formula for our double number is as it follows:

sign * mantissa * exponent
{1,1}  * <1,2) * (2**exp)

where both mantissa and exponent are stored binary wise.

Knowing this, we see the formula for granularity is (2 ** (-11)) * (2 ** exponent) where the first part comes from mantissa precision (smallest difference in mantissa). When it’s applied to exponent (in binary representation), we see the granularity will be decreasing for the smaller numbers and increasing for the bigger numbers.

For the big numbers like millions, especially when it’s a result of more complex formulas containing more factors and especially division (it’s advised to divide numbers as late as it’s possible), the potential error caused by granularity and representation errors can be really signifcant.

How BigDecimal are different?

BigDecimals don’t have the same limits as stndard floating point types. They are being stored ‘digit-wise’, digit by digit and they potentially have unlimited number of digits allowed to be stored in BigDecimal (mantissa part) stored as BigIntiger under the hood (decimal wise… in the simplest implementation. Real implementation dealing with “compact” and “non-compact” case you can check in the github source code of openjdk).

However, they still cannot hold rational numbers like ( 2 / 3 ) and thus they cannot have “infinite” number representation of rational number. Therefore, you need to be very careful when you divide and preferably perform it at the very end of computations.

Be careful with representing dividing like 1 / 3 * 3 != 1 in computer world… (1 / 3 cannot be stored precisely in binary stored mantissa)

Conclusion

The point is - just use BigDecimal when you suspect that you may need big numbers with good precision.