In Java, 80% of people use BigDecimal incorrectly...

Source: cnblogs com/zhangyinhua/p/11545305. html

1, BigDecimal overview

Java in Java The API class BigDecimal provided in math package is used to accurately calculate the number of more than 16 significant bits. Double, a double precision floating-point variable, can handle 16 bit significant numbers, but in practical applications, it may be necessary to operate and process larger or smaller numbers.

Generally, for those numbers that do not need accurate calculation accuracy, we can use Float and Double directly, but Double Valueof (string) and Float Valueof (string) will lose precision. Therefore, in development, if we need accurate calculation results, we must use BigDecimal class to operate.

BigDecimal creates objects, so we can't use traditional arithmetic operators such as +, -, *, / to directly perform mathematical operations on its objects, but must call its corresponding methods. The parameter in the method must also be an object of BigDecimal. Constructors are special methods of classes that are specifically used to create objects, especially objects with parameters.

2, BigDecimal common constructor

2.1. Common constructors

  • BigDecimal(int)

Creates an object with an integer value specified by the parameter

  • BigDecimal(double)

Creates an object with the double value specified by the parameter

  • BigDecimal(long)

Creates an object with the long integer value specified by the parameter

  • BigDecimal(String)

Creates an object with the value specified by the parameter as a string

2.2 analysis of use problems

Use example:

BigDecimal a =new BigDecimal(0.1);
System.out.println("a values is:"+a);
System.out.println("=====================");
BigDecimal b =new BigDecimal("0.1");
System.out.println("b values is:"+b);

Examples of results:

a values is:0.1000000000000000055511151231257827021181583404541015625
=====================
b values is:0.1

Cause analysis:

1) The result of the construction method with the parameter type of double is unpredictable. One might think that the BigDecimal created by writing newBigDecimal(0.1) in Java is exactly equal to 0.1 (non scale value 1, its scale is 1), but it is actually equal to 0.100000000000055512312578217021181583404541015625. This is because 0.1 cannot be accurately expressed as double (or, in this case, as any binary decimal of finite length). In this way, the value passed into the constructor will not be exactly equal to 0.1 (although it is ostensibly equal to that value).

2) The String construction method is completely predictable: writing newBigDecimal("0.1") will create a BigDecimal, which is exactly equal to the expected 0.1. Therefore, in comparison, it is generally recommended to give priority to the String construction method.

3) When BigDecimal is used as the source of the transformation, please note that when BigDecimal is used as an accurate method; It does not provide the same result as the following: use double first ToString (double) method, and then use BigDecimal(String) construction method to convert double into String. To get this result, use the static valueOf(double) method.

3, Detailed explanation of common methods of BigDecimal

3.1 common methods

  • add(BigDecimal)

Adds the values in the BigDecimal object to return the BigDecimal object

  • subtract(BigDecimal)

The BigDecimal object is returned by subtracting the value in the BigDecimal object

  • multiply(BigDecimal)

The BigDecimal object is returned by multiplying the values in the BigDecimal object

  • divide(BigDecimal)

Returns a BigDecimal object by dividing the values in the BigDecimal object

  • toString()

Converts the value in the BigDecimal object to a string

  • doubleValue()

Converts the value in the BigDecimal object to a double

  • floatValue()

Converts the value in the BigDecimal object to a single precision number

  • longValue()

Converts the value in the BigDecimal object to a long integer

  • intValue()

Converts the value in the BigDecimal object to an integer

3.2 BigDecimal size comparison

BigDecimal's compareTo method is generally used to compare the size of BigDecimal in java

int a = bigdemical.compareTo(bigdemical2)

Return result analysis:

a = -1,express bigdemical less than bigdemical2;
a = 0,express bigdemical be equal to bigdemical2;
a = 1,express bigdemical greater than bigdemical2;

Example: a is greater than or equal to b

new bigdemica(a).compareTo(new bigdemical(b)) >= 0

4, BigDecimal formatting

Because the format() method of NumberFormat class can use BigDecimal object as its parameter, BigDecimal can be used to format and control the currency value, percentage value and general value exceeding 16 significant digits.

Take BigDecimal to format currency and percentage as an example. First, create a BigDecimal object. After the arithmetic operation of BigDecimal, establish references to currency and percentage formatting respectively. Finally, use the BigDecimal object as the parameter of format() method to output its formatted currency value and percentage.

NumberFormat currency = NumberFormat.getCurrencyInstance(); //Create currency formatting reference
NumberFormat percent = NumberFormat.getPercentInstance();  //Create percentage formatting reference
percent.setMaximumFractionDigits(3); //The percentage has a maximum of 3 decimal places

BigDecimal loanAmount = new BigDecimal("15000.48"); //Loan amount
BigDecimal interestRate = new BigDecimal("0.008"); //interest rate
BigDecimal interest = loanAmount.multiply(interestRate); //Multiply

System.out.println("Loan amount:\t" + currency.format(loanAmount));
System.out.println("interest rate:\t" + percent.format(interestRate));
System.out.println("interest:\t" + currency.format(interest));

result:

Loan amount: ¥15,000.48 interest rate: 0.8% interest: ¥120.00

BigDecimal format keeps 2 as decimal, and if it is insufficient, supplement 0:

public class NumberFormat {

    public static void main(String[] s){
        System.out.println(formatToNumber(new BigDecimal("3.435")));
        System.out.println(formatToNumber(new BigDecimal(0)));
        System.out.println(formatToNumber(new BigDecimal("0.00")));
        System.out.println(formatToNumber(new BigDecimal("0.001")));
        System.out.println(formatToNumber(new BigDecimal("0.006")));
        System.out.println(formatToNumber(new BigDecimal("0.206")));
    }
    /**
     * @desc 1.0~1 BigDecimal decimals between. If the previous 0 is lost after formatting, the previous 0 will be added directly.
     * 2.If the passed in parameter is equal to 0, the string "0.00" is returned directly
     * 3.For decimals greater than 1, format and return the string directly
     * @param obj Decimal passed in
     * @return
     */
    public static String formatToNumber(BigDecimal obj) {
        DecimalFormat df = new DecimalFormat("#.00");
        if(obj.compareTo(BigDecimal.ZERO)==0) {
            return "0.00";
        }else if(obj.compareTo(BigDecimal.ZERO)>0&&obj.compareTo(new BigDecimal(1))<0){
            return "0"+df.format(obj).toString();
        }else {
            return df.format(obj).toString();
        }
    }
}

The result is:

3.44
0.00
0.00
0.00
0.01
0.21

5, BigDecimal common exception

5.1. Exception during division

java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result

Cause analysis:

When dividing through the divide method of BigDecimal, when there is no integral division and an infinite circular decimal occurs, an exception will be thrown: Java lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

resolvent:

The divide method sets the exact decimal point, such as divide(xxxxx,2)

6, BigDecimal summary

6.1 summary

BigDecimal is used when accurate decimal calculation is required. The performance of BigDecimal is worse than double and float, especially when dealing with large and complex operations. Therefore, BigDecimal is not necessary for the calculation of general accuracy. Try to use a constructor with a parameter type of String.

BigDecimal is immutable. A new object will be generated during each four operations, so remember to save the value after the operation when performing addition, subtraction, multiplication and division.

6.2. Recommended tools

package com.vivo.ars.util;
import java.math.BigDecimal;

/**
 * It is used for high-precision processing of common mathematical operations
 */
public class ArithmeticUtils {
    //Default division precision
    private static final int DEF_DIV_SCALE = 10;

    /**
     * Provide accurate addition operation
     *
     * @param v1 augend
     * @param v2 Addend
     * @return Sum of two parameters
     */

    public static double add(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2).doubleValue();
    }

    /**
     * Provide accurate addition operation
     *
     * @param v1 augend
     * @param v2 Addend
     * @return Sum of two parameters
     */
    public static BigDecimal add(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.add(b2);
    }

    /**
     * Provide accurate addition operation
     *
     * @param v1    augend
     * @param v2    Addend
     * @param scale Keep scale decimal places
     * @return Sum of two parameters
     */
    public static String add(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * Provide accurate subtraction
     *
     * @param v1 minuend
     * @param v2 Subtraction
     * @return Difference between two parameters
     */
    public static double sub(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.subtract(b2).doubleValue();
    }

    /**
     * Provide accurate subtraction.
     *
     * @param v1 minuend
     * @param v2 Subtraction
     * @return Difference between two parameters
     */
    public static BigDecimal sub(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.subtract(b2);
    }

    /**
     * Provide accurate subtraction
     *
     * @param v1    minuend
     * @param v2    Subtraction
     * @param scale Keep scale decimal places
     * @return Difference between two parameters
     */
    public static String sub(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * Provide accurate multiplication
     *
     * @param v1 Multiplicand
     * @param v2 multiplier
     * @return Product of two parameters
     */
    public static double mul(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.multiply(b2).doubleValue();
    }

    /**
     * Provide accurate multiplication
     *
     * @param v1 Multiplicand
     * @param v2 multiplier
     * @return Product of two parameters
     */
    public static BigDecimal mul(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.multiply(b2);
    }

    /**
     * Provide accurate multiplication
     *
     * @param v1    Multiplicand
     * @param v2    multiplier
     * @param scale Keep scale decimal places
     * @return Product of two parameters
     */
    public static double mul(double v1, double v2, int scale) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return round(b1.multiply(b2).doubleValue(), scale);
    }

    /**
     * Provide accurate multiplication
     *
     * @param v1    Multiplicand
     * @param v2    multiplier
     * @param scale Keep scale decimal places
     * @return Product of two parameters
     */
    public static String mul(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.multiply(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * Provide (relatively) accurate division operation. When there is inexhaustible division, it is accurate to
     * 10 digits after the decimal point, and the subsequent figures are rounded
     *
     * @param v1 Divisor
     * @param v2 Divisor
     * @return Quotient of two parameters
     */

    public static double div(double v1, double v2) {
        return div(v1, v2, DEF_DIV_SCALE);
    }

    /**
     * Provides (relatively) accurate division. In case of inexhaustible division, it is specified by the scale parameter
     * To determine the accuracy, the subsequent figures are rounded
     *
     * @param v1    Divisor
     * @param v2    Divisor
     * @param scale Indicates that it needs to be accurate to several decimal places.
     * @return Quotient of two parameters
     */
    public static double div(double v1, double v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * Provides (relatively) accurate division. In case of inexhaustible division, it is specified by the scale parameter
     * To determine the accuracy, the subsequent figures are rounded
     *
     * @param v1    Divisor
     * @param v2    Divisor
     * @param scale Indicates that it needs to be accurate to several decimal places
     * @return Quotient of two parameters
     */
    public static String div(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v1);
        return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * Provide accurate rounding of decimal places
     *
     * @param v     Number to be rounded
     * @param scale How many decimal places are reserved
     * @return Rounded results
     */
    public static double round(double v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(Double.toString(v));
        return b.setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * Provide accurate rounding of decimal places
     *
     * @param v     Number to be rounded
     * @param scale How many decimal places are reserved
     * @return Rounded results
     */
    public static String round(String v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(v);
        return b.setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * Remainder
     *
     * @param v1    Divisor
     * @param v2    Divisor
     * @param scale How many decimal places are reserved
     * @return remainder
     */
    public static String remainder(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.remainder(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * Remainder BigDecimal
     *
     * @param v1    Divisor
     * @param v2    Divisor
     * @param scale How many decimal places are reserved
     * @return remainder
     */
    public static BigDecimal remainder(BigDecimal v1, BigDecimal v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        return v1.remainder(v2).setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * Compare size
     *
     * @param v1 Compared number
     * @param v2 Comparison number
     * @return Returns true if v1 is greater than v2, otherwise false
     */
    public static boolean compare(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        int bj = b1.compareTo(b2);
        boolean res;
        if (bj > 0)
            res = true;
        else
            res = false;
        return res;
    }
}

Recent hot article recommendations:

1.1000 + Java interview questions and answers (2022 latest version)

2.Hot! The Java collaboration is coming...

3.Spring Boot 2.x tutorial, too complete!

4.Don't write the explosive category full of screen, try the decorator mode, this is the elegant way!!

5.Java development manual (Songshan version) is the latest release. Download it quickly!

Feel good, don't forget to like + forward!

Posted by zako234 on Tue, 17 May 2022 04:07:40 +0300