Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to limit BigDecimal to a fixed fractions amount?

I'm trying to create a BigDecimal class that has always a fixed maximum fractions count. But when printing that number, it is not cut to the fractions I defined in scale. Why?

class MyDecimal extends BigDecimal {
    public MyDecimal(double val) {
        super(val); 
        setScale(4, RoundingMode.HALF_UP);
    }
}


Sysout(new MyDecimal(0.0001));
//0.000100000000000000008180305391403130954586231382563710212707519531254
like image 708
membersound Avatar asked Mar 21 '23 05:03

membersound


2 Answers

BigDecimal is immutable, and should not be extended. setScale() does not modify the BigDecimal instance. It returns a copy of the BigDecimal instance with the scale modified (as every other "mutating" method of BigDecimal, since it's immutable). Calling it and ignoring the returned value is thus useless.

Instead of extending BigDecimal, create a factory method:

public static BigDecimal createWithScale4(double d) {
    BigDecimal temp = new BigDecimal(d);
    return temp.setScale(4);
}
like image 85
JB Nizet Avatar answered Apr 01 '23 08:04

JB Nizet


The problem is that you are putting in your input as a double. This is from the javadoc:

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

Since you are using a double, it is being stored as a floating-point number, and not as a precision number. It then converts that non-precision floating-point number to a precision BigDecimal, which then puts it out as a precise number. It works if the input is a String, however, since a String is just text, and converts the string directly to a BigDecimal. See this from here:

Creating a big decimal from a (scalar) double is simple:

bd = new BigDecimal(1.0);

To get a BigDecimal from a Double, get its doubleValue() first.

However it is a good idea to use the string constructor:

bd = new BigDecimal("1.5");

If you don't, then you'll get the following,

bd = new BigDecimal(1.5);

bd.toString(); // => 0.1499999999999999944488848768742172978818416595458984375

So do it like this:

Sysout(new MyDecimal("0.0001"));

Also, as @JBNizet points out, you're extending an immutable object, a BigDecimal. You're ignoring the return value of setting the scale. This could be changed by using a method such as the one below:

public static BigDecimal createBigDecimal(String s) {
    BigDecimal bigdeci = new BigDecimal(s);
    return bigdeci.setScale(4);
}
like image 32
hichris123 Avatar answered Apr 01 '23 07:04

hichris123