BigDecimal floor rounding goes wrong

11,687

Solution 1

From my answer to that question:

When the code is compiled or interpreted, your “0.1” is already rounded to the nearest number in that format, which results in a small rounding error even before the calculation happens.

The problem is that new BigDecimal(-0.07); uses a double literal to initialize the BigDecimal - so the error still happens. Use the BigDecimal constructor that takes a String instead.

Solution 2

You should use the string constructor to avoid rounding errors due to the use of doubles:

BigDecimal velocity = new BigDecimal("-0.07");
BigDecimal afterMultiplyingBy200 = velocity.multiply(new BigDecimal("200"));

Javadoc extract for the constructor using doubles - emphasis mine:

  1. The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a BigDecimal which is exactly equal to 0.1 (an unscaled value of 1, with a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the value that is being passed in to the constructor is not exactly equal to 0.1, appearances notwithstanding.
  2. The String constructor, on the other hand, is perfectly predictable: writing new BigDecimal("0.1") creates a BigDecimal which is exactly equal to 0.1, as one would expect. Therefore, it is generally recommended that the String constructor be used in preference to this one.

Solution 3

The problem is right here:

BigDecimal velocity = new BigDecimal(-0.07);

-0.07 cannot be represented exactly as a double, so the literal value passed to the BigDecimal constructor ends up being slightly different than -0.07. BigDecimal just takes that approximate value and runs with it, producing the results you're seeing.

Try:

BigDecimal velocity = new BigDecimal(-7, 2);
BigDecimal afterMultiplyingBy200 = velocity.multiply( new BigDecimal(2, -2) );

Solution 4

According to the documentation, ROUND_FLOOR rounds towards negative infinity, thus rounding -14.000... to -15.

Share:
11,687
Rustam Issabekov
Author by

Rustam Issabekov

Updated on June 04, 2022

Comments

  • Rustam Issabekov
    Rustam Issabekov almost 2 years

    I recently ask a question about weird java double floor rounding, and got answers to use BigDecimals instead, so tried the following code:

    BigDecimal velocity = new BigDecimal(-0.07);
    BigDecimal afterMultiplyingBy200 = velocity.multiply( new BigDecimal(200.0) );
    BigDecimal floored = afterMultiplyingBy200.setScale(0, RoundingMode.FLOOR);
    System.out.println("After multiplication " + afterMultiplyingBy200);
    System.out.println("floored value is " + floored);
    

    And I'm getting following results

    After multiplication -14.000000000000001332267629550187848508358001708984375000
    floored value is -15
    

    It seems that even using BigDecimal I can't get correct value for multiplying -0.07 by 200, is there anything that I can do to get exactly -14.0?