Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BigDecimal setScale method working exactly in the opposite way I am expecting

Consider this code:

import java.math.BigDecimal;
import java.math.RoundingMode;

public class RoundingTests {

    public static void main(String[] args) {
        BigDecimal bd1 = new BigDecimal(265.345d);
        BigDecimal bd2 = new BigDecimal(265.335d);

        System.out.println("Setting scale 265.345: " + bd1.setScale(2, RoundingMode.HALF_EVEN));
        System.out.println("Setting scale 265.335: " + bd2.setScale(2, RoundingMode.HALF_EVEN));
    }
}

The output is:

Setting scale 265.345: 265.35
Setting scale 265.335: 265.33

Now this is exactly the opposite of what I was expecting. With RoundingMode.HALF_EVEN (also called Bankers rounding) I was expecting both these values to become the value 265.34

Note that I am not using the BigDecimal.round method on purpose because it also does not what I need. If I add this code (and import java.math.MathContext) :

System.out.println("Rounding 265.345: " + bd1.round(new MathContext(2, RoundingMode.HALF_EVEN)));
System.out.println("Rounding 265.335: " + bd2.round(new MathContext(2, RoundingMode.HALF_EVEN)));

Then the output is:

Rounding 265.345: 2.7E+2
Rounding 265.335: 2.7E+2

This is expected and also explained in BigDecimal setScale and round, but means it's basically useless for my purpose.

Can someone explain the issue with setScale here?

Update: So it was just another floating point problem, with no easy way to fix it instead of using BigDecimals from the start.

like image 737
Sebastiaan van den Broek Avatar asked Nov 12 '22 18:11

Sebastiaan van den Broek


1 Answers

You might want to change your code to use BigDecimal.valueOf() instead of new BigDecimal().

The code

  Double dValue = 265.345d;
  Double dValue2 = 265.335d;
  BigDecimal value = BigDecimal.valueOf(dValue);
  BigDecimal bd1 = new BigDecimal(265.345d);
  BigDecimal bd2 = new BigDecimal(265.335d);
  BigDecimal value2 = BigDecimal.valueOf(dValue2);
  System.out.println("BigDecimal.valueOf(dValue);");
  System.out.println(value.toPlainString());
  System.out.println(String.valueOf(dValue));
  value = value.setScale(2, BigDecimal.ROUND_HALF_EVEN);
  System.out.println(value);
  System.out.println("BigDecimal.valueOf(dValue2);");
  System.out.println(value2.toPlainString());
  System.out.println(String.valueOf(dValue2));
  value2 = value2.setScale(2, BigDecimal.ROUND_HALF_EVEN);
  System.out.println(value2);
  System.out.println("BigDecimal bd1 = new BigDecimal(265.345d);");
  System.out.println(bd1.setScale(2, BigDecimal.ROUND_HALF_EVEN));
  System.out.println("BigDecimal bd2 = new BigDecimal(265.335d);");
  System.out.println(bd2.setScale(2, BigDecimal.ROUND_HALF_EVEN));

outputs :

BigDecimal.valueOf(dValue);
265.345
265.345
265.34
BigDecimal.valueOf(dValue2);
265.335
265.335
265.34
BigDecimal bd1 = new BigDecimal(265.345d);
265.35
BigDecimal value2 = BigDecimal.valueOf(dValue2);
265.33
like image 103
Jonathan Drapeau Avatar answered Nov 15 '22 04:11

Jonathan Drapeau