Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

BigDecimal is designed to work with large floating point numbers. It solves the problem with floating point arithmetic and provides a way to control the precision. Additionally, it has plenty of conventional methods for operations over the numbers.

We can leverage the features of BigDecimal by converting an Integer. In this tutorial, we’ll learn several ways to do this and discuss their pros and cons.

2. Constructor Conversion

One of the most straightforward ways to use constructor conversion. BigDecimal exposes constructors that can convert from a wide range of inputs. Thus, we can pass a given Integer to the BigDecimal constructor:

@ParameterizedTest
@ArgumentsSource(BigDecimalConversionArgumentsProvider.class)
void giveIntegerWhenConvertWithConstructorToBigDecimalThenConversionCorrect(Integer given, BigDecimal expected) {
    BigDecimal actual = new BigDecimal(given);
    assertThat(actual).isEqualTo(expected);
}

However, with this approach, we’ll force BigDecimal to create a new object every time:

@ParameterizedTest
@ValueSource(ints = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
void giveIntegerWhenConvertWithConstructorToBigDecimalThenConversionWithoutCaching(Integer given) {
    BigDecimal firstBigDecimal = new BigDecimal(given);
    BigDecimal secondBigDecimal = new BigDecimal(given);
    assertThat(firstBigDecimal)
      .isEqualTo(secondBigDecimal)
      .isNotSameAs(secondBigDecimal);
}

3. Static Factory Conversion

Another technique involves a static factory, and it’s similar to the previous example:

@ParameterizedTest
@ArgumentsSource(BigDecimalConversionArgumentsProvider.class)
void giveIntegerWhenConvertWithValueOfToBigDecimalThenConversionCorrect(Integer given, BigDecimal expected) {
    BigDecimal actual = BigDecimal.valueOf(given);
    assertThat(actual).isEqualTo(expected);
}

It provides one benefit: it can cache values, unlike constructor conversion. Thus, we can reuse the same object in different contexts. Because BigDecimal is immutable, it doesn’t create any problems.

4. Caching

The BigIntegers.valueOf() factory caches the values from zero to ten. All the values are defined in the static ZERO_THROUGH_TEN array inside the BigDecimal class:

private static final BigDecimal[] ZERO_THROUGH_TEN = {
  new BigDecimal(BigInteger.ZERO,       0,  0, 1),
  new BigDecimal(BigInteger.ONE,        1,  0, 1),
  new BigDecimal(BigInteger.TWO,        2,  0, 1),
  new BigDecimal(BigInteger.valueOf(3), 3,  0, 1),
  new BigDecimal(BigInteger.valueOf(4), 4,  0, 1),
  new BigDecimal(BigInteger.valueOf(5), 5,  0, 1),
  new BigDecimal(BigInteger.valueOf(6), 6,  0, 1),
  new BigDecimal(BigInteger.valueOf(7), 7,  0, 1),
  new BigDecimal(BigInteger.valueOf(8), 8,  0, 1),
  new BigDecimal(BigInteger.valueOf(9), 9,  0, 1),
  new BigDecimal(BigInteger.TEN,        10, 0, 2),
};

The valueOf(long) factory uses this array inside:

public static BigDecimal valueOf(long val) {
    if (val >= 0 && val < ZERO_THROUGH_TEN.length)
        return ZERO_THROUGH_TEN[(int)val];
    else if (val != INFLATED)
        return new BigDecimal(null, val, 0, 0);
    return new BigDecimal(INFLATED_BIGINT, val, 0, 0);
}

We can see that for certain values, the BigDecimal objects are the same:

@ParameterizedTest
@ValueSource(ints = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
void giveIntegerWhenConvertWithValueOfToBigDecimalThenConversionCachesTheResults(Integer given) {
    BigDecimal firstBigDecimal = BigDecimal.valueOf(given);
    BigDecimal secondBigDecimal = BigDecimal.valueOf(given);
    assertThat(firstBigDecimal).isSameAs(secondBigDecimal);
}

This might benefit performance if we use many BigDecimal values from zero to ten. Also, as BigDecimal objects are immutable, we can implement our cache for the numbers we use repeatedly in our application.

At the same time, the numbers outside of this range won’t use caching:

@ParameterizedTest
@ValueSource(ints = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21})
void giveIntegerWhenConvertWithValueOfToBigDecimalThenConversionWontCacheTheResults(Integer given) {
    BigDecimal firstBigDecimal = BigDecimal.valueOf(given);
    BigDecimal secondBigDecimal = BigDecimal.valueOf(given);
    assertThat(firstBigDecimal)
      .isEqualTo(secondBigDecimal)
      .isNotSameAs(secondBigDecimal);
}

Thus, relying on identity equality in the production code isn’t advisable.

5. Conclusion

BigDecimal is a good choice when we need to make operations on floating point numbers, avoiding rounding errors. Also, it allows us to use massive numbers that cannot be represented otherwise. BigDecimal provides various methods for conversion from other types, for example, from an Integer.

As usual, all the code from this tutorial is available over on GitHub.

Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE
res – REST with Spring (eBook) (everywhere)
Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.