Warning: Use BigDecimal with caution online, the pit is almost opened

Past popular articles:

1. How can there be so many projects from scratch for you to develop
2. From microservices to monolithic architecture, the cost is reduced by 90%! Yes, you read that right!
3. The rollover accident caused by Lombok is too pitiful!
4. How to design a universal payment system?
5. Java 17 user adoption grew 430% in a year

Author: Andy Coding

Link: https://www.cnblogs.com/zhangyinhua/p/11545305.html

The API class BigDecimal provided by Java in the java.math package is used to perform precise operations on numbers with more than 16 effective digits. The double-precision floating-point variable double can handle 16 significant digits, but in practical applications, it may be necessary to perform operations and processing on larger or smaller numbers.

In general, we can directly use Float and Double for those numbers that do not need accurate calculation precision, but Double.valueOf(String) and Float.valueOf(String) will lose precision. So in development, if we need accurate calculation results, we must use the BigDecimal class to operate.

What BigDecimal creates is an object, so we cannot use traditional arithmetic operators such as +, -, *, / to directly perform mathematical operations on its object, but must call its corresponding method. The parameters in the method must also be BigDecimal objects. A constructor is a special method of a class designed to create objects, especially objects with parameters.

BigDecimal common constructor

1. Commonly used constructors

BigDecimal(int)

Creates an object with the integer value specified by the parameter

  • BigDecimal(double)

Creates an object with the double value specified by the argument

  • BigDecimal(long)

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

  • BigDecimal(String)

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

2. Use problem analysis

Example usage:

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);

Example result:

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

Cause Analysis:

1) The result of the construction method whose parameter type is double is somewhat unpredictable. One might think that writing newBigDecimal(0.1) in Java creates a BigDecimal that is exactly equal to 0.1 (an unscaled value of 1 whose scale is 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 any finite-length binary fraction). Thus, the value passed into the constructor will not be exactly equal to 0.1 (although apparently equal to that value).

2) The String constructor is completely predictable: writing newBigDecimal(“0.1”) will create a BigDecimal that is exactly equal to the expected 0.1. Therefore, in comparison, it is generally recommended to use the String construction method first.

3) When double must be used as the source of BigDecimal, note that this constructor provides an exact conversion; it does not provide the same result as using the Double.toString(double) method first, then using BigDecimal(String) Constructor, convert double to String. To get that result, use the static valueOf(double) method.

Detailed explanation of common methods of BigDecimal

1. Common methods

  • add(BigDecimal)

Adds the values in the BigDecimal object and returns the BigDecimal object

  • subtract(BigDecimal)

Subtract the values in the BigDecimal object and return the BigDecimal object

  • multiply(BigDecimal)

Multiply the values in the BigDecimal object and return the BigDecimal object

  • divide(BigDecimal)

Divides the values in the BigDecimal object and returns the BigDecimal object

  • toString()

Converts a value in a BigDecimal object to a string

  • doubleValue()

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

  • floatValue()

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

  • longValue()

Converts a value in a BigDecimal object to a long integer

  • intValue()

Converts a value in a BigDecimal object to an integer

2. BigDecimal size comparison

The compareTo method of bigdemical is generally used to compare the size of BigDecimal in java

int a = bigdemical. compareTo(bigdemical2)

Return result analysis:

a = -1, indicating that bigdemical is smaller than bigdemical2;
a = 0, indicating that bigdemical is equal to bigdemical2;
a = 1, indicating that bigdemical is greater than bigdemical2;

Example: a is greater than or equal to b

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

BigDecimal formatting

Since the format() method of the NumberFormat class can use BigDecimal objects as its parameters, BigDecimal can be used to control the formatting of monetary values, percentage values, and general numerical values beyond 16 significant figures.

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

NumberFormat currency = NumberFormat.getCurrencyInstance(); //Create currency format reference
NumberFormat percent = NumberFormat.getPercentInstance(); //Create a percentage format reference
percent.setMaximumFractionDigits(3); //Maximum 3 decimal places for percentages

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 formatting retains 2 as a decimal, and fills in 0 if it is insufficient:

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 BigDecimal decimals between 1.0 and 1, if the preceding 0 is lost after formatting, then 0 is directly added to the front.
     * 2. If the parameter passed in is equal to 0, the string "0.00" will be returned directly
     * 3. For decimals greater than 1, directly format the return string
     * @param obj incoming decimal
     * @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 & amp; & amp;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

BigDecimal common exception

An exception occurred during division

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

Cause Analysis:

When dividing by the divide method of BigDecimal, when there is no integer division and an infinite cycle of decimals occurs, an exception will be thrown: java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

Workaround:

The divide method sets a precise decimal point, such as: divide(xxxxx,2)

Summary of BigDecimal

1. Summary

Use BigDecimal when accurate decimal calculation is required. The performance of BigDecimal is worse than that of double and float, especially when dealing with large and complex operations. Therefore, it is not necessary to use BigDecimal for general precision calculations. Try to use constructors whose parameter type is String.

BigDecimal is immutable, and a new object will be generated every time the four arithmetic operations are performed, so remember to save the value after the operation when doing addition, subtraction, multiplication and division.

2. Tool recommendation

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

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

    /**
     * Provides precise addition operations
     *
     * @param v1 summand
     * @param v2 addend
     * @return the sum of the 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();
    }

    /**
     * Provides precise addition operations
     *
     * @param v1 summand
     * @param v2 addend
     * @return the sum of the two parameters
     */
    public static BigDecimal add(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.add(b2);
    }

    /**
     * Provides precise addition operations
     *
     * @param v1 summand
     * @param v2 addend
     * @param scale retain scale decimal place
     * @return the sum of the 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();
    }

    /**
     * Provides precise subtraction
     *
     * @param v1 minuend
     * @param v2 subtrahend
     * @return The difference between the 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();
    }

    /**
     * Provides precise subtraction operations.
     *
     * @param v1 minuend
     * @param v2 subtrahend
     * @return The difference between the two parameters
     */
    public static BigDecimal sub(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.subtract(b2);
    }

    /**
     * Provides precise subtraction
     *
     * @param v1 minuend
     * @param v2 subtrahend
     * @param scale retain scale decimal place
     * @return The difference between the 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();
    }

    /**
     * Provides precise multiplication
     *
     * @param v1 Multiplicand
     * @param v2 multiplier
     * @return the product of the 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();
    }

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

    /**
     * Provides precise multiplication
     *
     * @param v1 Multiplicand
     * @param v2 multiplier
     * @param scale retain scale decimal place
     * @return the product of the 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);
    }

    /**
     * Provides precise multiplication
     *
     * @param v1 Multiplicand
     * @param v2 multiplier
     * @param scale retain scale decimal place
     * @return the product of the 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();
    }

    /**
     * Provides (relatively) accurate division operations, accurate to
     * 10 digits after the decimal point, the following numbers are rounded up
     *
     * @param v1 dividend
     * @param v2 divisor
     * @return the quotient of the two parameters
     */

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

    /**
     * Provides (relatively) exact division operations. When an indivisible situation occurs, it is indicated by the scale parameter
     * fixed precision, subsequent numbers are rounded
     *
     * @param v1 dividend
     * @param v2 divisor
     * @param scale indicates that it needs to be accurate to several digits after the decimal point.
     * @return the quotient of the 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) exact division operations. When an indivisible situation occurs, it is indicated by the scale parameter
     * fixed precision, subsequent numbers are rounded
     *
     * @param v1 dividend
     * @param v2 divisor
     * @param scale indicates that it needs to be accurate to several digits after the decimal point
     * @return the quotient of the 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();
    }

    /**
     * Provides precise decimal rounding
     *
     * @param v number to be rounded
     * @param scale How many digits are reserved after the decimal point
     * @return rounded result
     */
    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();
    }

    /**
     * Provides precise decimal rounding
     *
     * @param v number to be rounded
     * @param scale How many digits are reserved after the decimal point
     * @return rounded result
     */
    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();
    }

    /**
     * take the remainder
     *
     * @param v1 dividend
     * @param v2 divisor
     * @param scale How many digits are reserved after the decimal point
     * @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();
    }

    /**
     * Take remainder BigDecimal
     *
     * @param v1 dividend
     * @param v2 divisor
     * @param scale How many digits are reserved after the decimal point
     * @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);
    }

    /**
     * Comparison of size
     *
     * @param v1 compared number
     * @param v2 comparison number
     * @return 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;
    }
}
Past popular articles:
1. One Chinese made me work overtime for two days
2. Microsoft Bing suddenly exploded update! There is no need to wait for everyone to be available, the answer is illustrated and textual, netizens: Force ChatGPT to enlarge the trick?
3. Still using Shiro?
4. How to package the SpringBoot project into an exe application?
5. The 10 subtle uses of MyBatis are really wonderful!
6. I found a database tool that is so easy to use, I was amazed!
7. There is a new colleague, the code naming convention is really elegant! Code is poetry! !
8. Spring Batch batch processing, aggressive and powerful!
9. The controller layer code should be written like this, the boss insisted on giving me a salary increase after reading it!
10. A quick look at the new UI of IDEA, which looks like VS Code