1, The background of BigDecimal
First, let's look at the following code example:
@Test public void countDemo() { logger.info("result:{}", 0.06 + 0.01); logger.info("result:{}", 1.0 - 0.42); logger.info("result:{}", 4.015 * 100); logger.info("result:{}", 303.1 / 1000); }
give the result as follows
result:0.06999999999999999 result:0.5800000000000001 result:401.49999999999994 result:0.30310000000000004
What's the problem? The reason is that our computer is binary, floating-point numbers can not be accurately expressed in binary.
Java float can only be used for scientific computing or engineering computing. In most business computing, it is generally used java.math.BigDecimal Class to perform an exact calculation.
2, Using BigDecimal correctly
2.1 BigDecimal constant
BigDecimal defines several commonly used values, 0, 1, 10, which are static and can be directly referenced by class name, such as: BigDecimal.ZERO .
/** * The value 0, with a scale of 0. * * @since 1.5 */ public static final BigDecimal ZERO = zeroThroughTen[0]; /** * The value 1, with a scale of 0. * * @since 1.5 */ public static final BigDecimal ONE = zeroThroughTen[1]; /** * The value 10, with a scale of 0. * * @since 1.5 */ public static final BigDecimal TEN = zeroThroughTen[10];
2.2 construction method
BigDecimal has four common construction methods
- new BigDecimal(int) creates an object with the integer value specified by the parameter;
- new BigDecimal(double) creates an object with the double precision value specified by the parameter;
- new BigDecimal(long) creates an object with a long integer value specified by the parameter;
- new BigDecimal(String) creates an object with a string value specified by the parameter.
2.3 general steps of BigDecimal operation
When using the BigDecimal class for calculation, there are three main steps:
- Use float or double variable to build BigDecimal object.
- By calling the corresponding methods of BigDecimal, such as addition, subtraction, multiplication, division, etc., the arithmetic operation is carried out.
- Convert BigDecimal object to float, double, int and other types.
3, Detailed explanation of common methods
In the general development process, the data stored in our database are of float and double types. I encapsulate a tool class, which provides add, subtract, multiply, divide, value up, value down and other operations.
3.1 ordinary addition, subtraction, multiplication and division
/** * Default division precision */ private static final int DEF_DIV_SCALE = 10; /** * Precise addition. * * @param v1 augend * @param v2 Addend * @return Sum of two parameters */ public static double add(Double v1, Double v2) { BigDecimal b1 = BigDecimal.valueOf(v1); BigDecimal b2 = BigDecimal.valueOf(v2); return b1.add(b2).doubleValue(); } /** * Accurate subtraction. * * @param v1 minuend * @param v2 Subtraction * @return Difference between two parameters */ public static double sub(Double v1, Double v2) { BigDecimal b1 = BigDecimal.valueOf(v1); BigDecimal b2 = BigDecimal.valueOf(v2); return b1.subtract(b2).doubleValue(); } /** * Precise multiplication. * * @param v1 Multiplier * @param v2 multiplier * @return Product of two parameters */ public static double mul(Double v1, Double v2) { BigDecimal b1 = BigDecimal.valueOf(v1); BigDecimal b2 = BigDecimal.valueOf(v2); return b1.multiply(b2).doubleValue(); } /** * (Relative) accurate division operation. In case of inexhaustible division, the default precision is 10 digits after the decimal point, and the subsequent numbers 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, RoundingMode.HALF_UP); }
3.2 rounding to accuracy or rounding up / down
/** * Round off the calculation results * * @param val * @param scale accuracy * @return For example, keep three decimal places: 0.646464 =] 0.646 */ public static double roundHalfUp(double val, int scale) { BigDecimal dec = BigDecimal.valueOf(val); return dec.setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue(); } /** * Get the calculation result and round it up * * @param val val * @param scale accuracy * @return For example, keep three decimal places: 0.646464 =] 0.647 */ public static double roundUp(double val, int scale) { BigDecimal dec = BigDecimal.valueOf(val); return dec.setScale(scale, RoundingMode.UP).doubleValue(); } /** * Round down after getting the calculation results * * @param val val * @param scale accuracy * @return For example, keep three decimal places: 0.646464 =] 0.646 */ public static double roundDown(double val, int scale) { BigDecimal dec = BigDecimal.valueOf(val); return dec.setScale(scale, RoundingMode.DOWN).doubleValue(); } /** * Division plus rounding up. * * @param v1 Divisor * @param v2 Divisor * @param scale accuracy * @return */ public static double divOfUp(Double v1, Double v2, int scale) { return div(v1, v2, scale, RoundingMode.UP); } /** * Division plus rounding down. * * @param v1 Divisor * @param v2 Divisor * @param scale accuracy * @return */ public static double divOfDown(Double v1, Double v2, int scale) { return div(v1, v2, scale, RoundingMode.DOWN); } /** * Provides (relatively) accurate division operations. In case of inexhaustible division, the scale parameter specifies the precision and the roundingMode specifies the selection method. * * @param v1 Divisor * @param v2 Divisor * @param scale Indicates that the representation needs to be accurate to several decimal places. * @param roundingMode Specify the choice. * @return Quotient of two parameters */ private static double div(double v1, double v2, int scale, RoundingMode roundingMode) { //If the exact range is less than 0, an exception is thrown if (scale < 0) { throw new IllegalArgumentException("The scale must be a positive integer or zero"); } BigDecimal b1 = BigDecimal.valueOf(v1); BigDecimal b2 = BigDecimal.valueOf(v2); return b1.divide(b2, scale, roundingMode).doubleValue(); }
3.3 if it happens to be divided by or multiplied by ten
Sometimes, for example, in the calculation of RMB cents / yuan / 10000, we just divide or multiply by 100 / 10000. In addition to addition and subtraction, BigDecimal provides a method to move the decimal point directly.
/** * Moves the decimal point to the right by a specified number of digits * * @param val Multiplier * @param index Move digits * @return For example, move four decimal places to the right: 1000.01 =] 1000010.0 */ public static double movePointRight(Double val, int index) { BigDecimal value = BigDecimal.valueOf(val); return value.movePointRight(index).doubleValue(); } /** * Moves the decimal point to the left by a specified number of digits * * @param val Divisor * @param index Move digits * @return For example, move four decimal places to the left: 1000.01 = > 1.00001 */ public static double movePointLeft(Double val, int index) { BigDecimal value = BigDecimal.valueOf(val); return value.movePointLeft(index).doubleValue(); }
3.4 BigDecimal size comparison
When comparing whether the values of two BigDecimal are equal, compareTo() method must be used to compare. It returns - 1, 1 and 0 respectively according to the size of the two values, representing less than, greater than and equal to.
@Test public void compareDecimal() { BigDecimal v1 = BigDecimal.valueOf(1.21); BigDecimal v2 = BigDecimal.valueOf(1.22); BigDecimal v3 = BigDecimal.valueOf(1.22); // -1: Less than, 1: greater than, 0: equal to logger.info("result:{}", v1.compareTo(v2)); logger.info("result:{}", v2.compareTo(v1)); logger.info("result:{}", v2.compareTo(v3)); }
3.5BigDecimal accuracy is also lost
Carefully, you must have found that in the tool class, using BigDecimal's valueOf() to create objects, and the internal implementation of valueOf() is
public static BigDecimal valueOf(double val) { return new BigDecimal(Double.toString(val)); }
And the constructor uses new BigDecimal(String), because other problems, such as new BigDecimal(int)/new BigDecimal(long)/new BigDecimal(double), will still result in the loss of precision.
- Example
@Test public void precisionLose() { BigDecimal v1 = new BigDecimal(1.01); BigDecimal v2 = new BigDecimal(1.02); BigDecimal v3 = new BigDecimal("1.01"); BigDecimal v4 = new BigDecimal("1.02"); // 2.0300000000000000266453525910037569701671600341796875 logger.info("result:{}", v1.add(v2)); // 2.03 logger.info("result:{}", v3.add(v4)); }
4, Summary
-
BigDecimal is used to express precise decimal, which is often used in financial calculation;
-
To compare whether the values of BigDecimal are equal, compareTo() must be used instead of equals().
[Github sample code]
More Java notes, see [Java knowledge notebook] , welcome to provide ideas and suggestions.
Daily praise
- Ancestral script Spring Boot sunflower classic Welcome to Tucao, and make complaints about open source.
- nine men's power [Java knowledge notebook] Welcome to Tucao, and make complaints about open source.
The latest article, welcome to the public: the official account - the dust blog; the exchange of views, welcome to add: personal WeChat.

