Understanding the use of BigDecimal

Posted by manmanman on Thu, 12 Dec 2019 03:04:33 +0100

What is BigDecimal

BigDecimal can represent a floating point number of any size with complete accuracy.

Why use BigDecimal instead of double

Talk is cheap, Show me the Code.

Example 1:

double d1 = 0.3;
double d2 = 0.2;
System.out.println("Double:\t 0,3 - 0,2 = " + (d1 - d2));

float f1 = 0.3f;
float f2 = 0.2f;
System.out.println("Float:\t 0,3 - 0,2 = " + (f1 - f2));

BigDecimal bd1 = new BigDecimal("0.3");
BigDecimal bd2 = new BigDecimal("0.2");
System.out.println("BigDec:\t 0,3 - 0,2 = " + (bd1.subtract(bd2)));

Run Results

Double:     0,3 - 0,2 = 0.09999999999999998
Float:     0,3 - 0,2 = 0.10000001
BigDec:     0,3 - 0,2 = 0.1

From the results, BigDecimal is required when we want to perform precise decimal operations.Now let's divide and see what happens:

Example 2:

double d1 = 10;
double d2 = 3;
System.out.println("Double:\t 10 / 3 = " + (d1 / d2));

float f1 = 10f;
float f2 = 3f;
System.out.println("Float:\t 10 / 3 = " + (f1 / f2));

// Exception!
BigDecimal bd3 = new BigDecimal("10");
BigDecimal bd4 = new BigDecimal("3");
System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4)));

Run Results

Double:     10 / 3 = 3.3333333333333335
Float:     10 / 3 = 3.3333333
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

An exception is thrown when the result is excluded and the carry status value is not set.The correct operation is as follows:

System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4,4,BigDecimal.ROUND_HALF_UP)));

Run Results

Double:     10 / 3 = 3.3333333333333335
Float:     10 / 3 = 3.3333333
BigDec:     10 / 3 = 3.3333

Summary: When we have a very high accuracy requirement, we need to make precise calculations, such as currency, so we need to use the java.math.BigDecimal class for precise calculations.

Add, subtract, multiply and divide

Method Narration
add(BigDecimal) Add the values in the BigDecimal object and return it
subtract(BigDecimal) Subtract the values in the BigDecimal object and return it
multiply(BigDecimal) Multiply the values in the BigDecimal object and return the object
divide(BigDecimal) Divide the values in the BigDecimal object and return it


public class BigDecimalCalculation {
    static BigDecimal a = new BigDecimal("0.02");
    static BigDecimal b = new BigDecimal("0.03");

    public static void main(String[] args) {
        System.out.println("a + b = " + a.add(b));
        System.out.println("a - b = " + a.subtract(b));
        System.out.println("a * b = " + a.multiply(b));
        System.out.println("a ÷ b = " + a.divide(b,2,BigDecimal.ROUND_HALF_UP));
    }

}

Run result:

a + b = 0.05
a - b = -0.01
a * b = 0.0006
a ÷ b = 0.67

common method

Keep two decimal places

public class keepTwoDecimal {
    public static void main(String[] args) {
        BigDecimal num= new BigDecimal(13.154215);

        //Mode 1
        DecimalFormat df1 = new DecimalFormat("0.00");
        String str = df1.format(num);
        System.out.println(str);  //13.15

        //Mode 2
        // #.00 means two decimal digits#.0000 four decimal digits
        DecimalFormat df2 =new DecimalFormat("#.00");
        String str2 =df2.format(num);
        System.out.println(str2);  //13.15

        //Mode 3
        //%.2f%.indicates that any number of digits before the decimal point 2 indicates two decimal places formatted as f indicates floating point type
        String result = String.format("%.2f", num);
        System.out.println(result);  //13.15
    }
}

Rounding

  • ROUND_UP

Rounding mode away from zero.

Always increase the number before discarding non-zero parts (always add 1 to the number before non-zero discarded parts)

For example: 2.36 -> 2.4

  • ROUND_DOWN

Rounding mode near zero.

Never add a number until a part is discarded (never add 1, or truncate, to the number before the discarded part).

For example: 2.36 -> 2.3

  • ROUND_CEILING

Rounding pattern approaching positive infinity.

If BigDecimal is positive, the rounding behavior is the same as ROUND_UP;

If negative, the rounding behavior is the same as ROUND_DOWN.

Equivalent to a combination of ROUND_UP and ROUND_DOWN

  • ROUND_FLOOR

Rounding pattern near negative infinity.

If BigDecimal is positive, the rounding behavior is the same as ROUND_DOWN;

If negative, the rounding behavior is the same as ROUND_UP.

Contrary to ROUND_CEILING

  • ROUND_HALF_UP

Rounding

For example: 2.35 -> 2.4

  • ROUND_HALF_DOWN

Rounding

For example: 2.35 -> 2.3

  • ROUND_HALF_EVEN

If the number to the left of the discarded part is odd, the rounding behavior is the same as ROUND_HALF_UP (rounding);

If it is even, the rounding behavior is the same as ROUND_HALF_DOWN (rounding).

For example: 1.15 -> 1.1, 1.25 -> 1.2

  • ROUND_UNNECESSARY

Asserting that the requested operation has precise results does not require rounding.

ArithmeticException is thrown if this rounding mode is specified for operations that produce accurate results.

I think it's OK to know what's left, and I feel that what's left is wrong, like ROUND_HALF_DOWN and ROUND_HALF_EVEN. Look at the results below and you will know why I'm saying it.

public class BigDecimalScaleTest {
    public static void main(String[] args) {
        double num = 2.35;
        BigDecimal b = new BigDecimal(num);
        // setScale(1) indicates reserving a decimal place
        System.out.println("ROUND_UP,Result:" + b.setScale(1, BigDecimal.ROUND_UP).doubleValue());
        System.out.println("ROUND_DOWN,Result:" + b.setScale(1, BigDecimal.ROUND_DOWN).doubleValue());
        System.out.println("ROUND_CEILING,Result:" + b.setScale(1, BigDecimal.ROUND_CEILING).doubleValue());
        System.out.println("ROUND_FLOOR,Result:" + b.setScale(1, BigDecimal.ROUND_FLOOR).doubleValue());
        System.out.println("ROUND_HALF_UP,Result:" + b.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue());
        System.out.println("ROUND_HALF_DOWN,Result:" + b.setScale(1, BigDecimal.ROUND_HALF_DOWN).doubleValue());
        System.out.println("ROUND_HALF_EVEN,Result:" + b.setScale(1, BigDecimal.ROUND_HALF_EVEN).doubleValue());
        System.out.println("ROUND_UNNECESSARY,Result:" + b.setScale(1, BigDecimal.ROUND_UNNECESSARY).doubleValue());
    }
}

Run Results

ROUND_UP,Results: 2.4
ROUND_DOWN,Results: 2.3
ROUND_CEILING,Results: 2.4
ROUND_FLOOR,Results: 2.3
ROUND_HALF_UP,Results: 2.4
ROUND_HALF_DOWN,Results: 2.4 (Come and explain this to me. What about rounding?)
ROUND_HALF_EVEN,Results: 2.4 (And this one)
Disconnected from the target VM, address: '127.0.0.1:59637', transport: 'socket'
Exception in thread "main" java.lang.ArithmeticException: Rounding necessary

Summary: The usual ones are ROUND_HALF_UP, ROUND_UP and ROUND_DOWN, just the other jokes.

compare

a.compareTo(b)

A > B returns 1; a = b returns 0; a < B returns -1

public class BigDecimalCompare {
    public static void main(String[] args) {
        BigDecimal a = new BigDecimal("0.02");
        BigDecimal b = new BigDecimal("0.01");
        BigDecimal a2 = new BigDecimal("0.02");

        System.out.println(" a > b Return results:" + a.compareTo(b));
        System.out.println(" a = a2 Return results:" + a.compareTo(a2));
        System.out.println(" b < a Return results:" + b.compareTo(a));
    }
}

Run Results

 A > b Returns: 1
 a = a2 Returns: 0
 B < a Returns: -1

Matters needing attention

In the above use, we all use String to assign BigDecimal instead of using the double type. See the following example for specific reasons:

public class BigDecimalTest {
    public static void main(String[] args) {
        BigDecimal num1 = new BigDecimal(0.005);
        BigDecimal num2 = new BigDecimal(1000000);
        BigDecimal num3 = new BigDecimal(-1000000);
        //Initialize as strings as possible
        BigDecimal num12 = new BigDecimal("0.005");
        BigDecimal num22 = new BigDecimal("1000000");
        BigDecimal num32 = new BigDecimal("-1000000");

        //addition
        BigDecimal result1 = num1.add(num2);
        BigDecimal result12 = num12.add(num22);
        //subtraction
        BigDecimal result2 = num1.subtract(num2);
        BigDecimal result22 = num12.subtract(num22);
        //multiplication
        BigDecimal result3 = num1.multiply(num2);
        BigDecimal result32 = num12.multiply(num22);
        //absolute value
        BigDecimal result4 = num3.abs();
        BigDecimal result42 = num32.abs();
        //division
        BigDecimal result5 = num2.divide(num1,20,BigDecimal.ROUND_HALF_UP);
        BigDecimal result52 = num22.divide(num12,20,BigDecimal.ROUND_HALF_UP);

        System.out.println("Addition value Result:"+result1);
        System.out.println("Addition string Result:"+result12);

        System.out.println("subtraction value Result:"+result2);
        System.out.println("Subtraction string Result:"+result22);

        System.out.println("Multiplication value Result:"+result3);
        System.out.println("Multiplication string Result:"+result32);

        System.out.println("Absolute value in value Result:"+result4);
        System.out.println("Absolute value in string Result:"+result42);

        System.out.println("Division value Result:"+result5);
        System.out.println("Division string Result:"+result52);
    }
  }

Run result:

Addition value result: 1000000.0050000000000001040855860842566454688835404296875
 string result for addition: 1000000.005
 Subtraction value result: -999999.99499999999999999999999999998959165914413915743352845311185595703125
 string result for subtraction: -999999.995
 Multiplication value result: 5000.000000000104085586084256645468883514404296875000000
 Multiplication string result: 5000.000
 Absolute value with value result: 1000000
 string result for absolute value: 1000000
 value result for division: 1999999.99999999995836665766
 string result for division: 200000000.00000000000000000
  • Numbers in System.out.println() are of type double by default, and decimal calculations of type double are inaccurate
  • When passed in the double type using the BigDecimal construction method, the result of the calculation is also inaccurate!

So when we assign values using BigDecimal, it's best to use the constructor passed in to the String to confirm accuracy.

Reference resources

https://blog.csdn.net/haiyins...

https://www.jianshu.com/p/294...

https://blog.csdn.net/ochangw...

Topics: Java socket