
preface
Today, a junior developer in the group asked why the tester tested that the price calculation module he wrote had calculation deviations. He checked for a long time but found no problem. Brother Xiaopang wants to remind you here that please use commercial calculationsBigDecimal
, floating point commercial calculations are inaccurate. Because computers cannot use binary decimal numbers to accurately describe decimal decimal numbers in our programs. Effective Java also recommends "using BigDecimal to do precise calculations" in Article 48. Today we will summarize and summarize the relevant knowledge points.
1.& nbsp;BigDecimal
BigDecimal represents an immutable arbitrary precision signed decimal number. It consists of two parts:
- intVal Integer with uncorrected precision, type is
BigInteger
- Scale –A 32-bit integer that represents the number of digits to the right of the decimal point
For example, BigDecimal 3.14 has an uncorrected value of 314, scaled to 2. We use BigDecimal for high-precision arithmetic operations. We also use it for calculations that need to control scaling and rounding behavior. If your calculations are commercial calculations, be sure to use accurate calculationsBigDecimal
。
2. Constructing BigDecimal
we canString
,character
Array,int
,long
andBigInteger
create aBigDecimal
Target:
@Test
public void theValueMatches() {
BigDecimal bdFromString = new BigDecimal("0.12");
BigDecimal bdFromCharArray = new BigDecimal(new char[]{'3', '.', '1', '4', '1', '5'});
BigDecimal bdlFromInt = new BigDecimal(42);
BigDecimal bdFromLong = new BigDecimal(123412345678901L);
BigInteger bigInteger = BigInteger.probablePrime(100, new Random());
BigDecimal bdFromBigInteger = new BigDecimal(bigInteger);
assertEquals("0.12", bdFromString.toString());
assertEquals("3.1415", bdFromCharArray.toString());
assertEquals("42", bdlFromInt.toString());
assertEquals("123412345678901", bdFromLong.toString());
assertEquals(bigInteger.toString(), bdFromBigInteger.toString());
}
We can also learn fromdouble
createBigDecimal
:
@Test
public void whenBigDecimalCreatedFromDouble_thenValueMayNotMatch() {
BigDecimal bdFromDouble = new BigDecimal(0.1d);
assertNotEquals("0.1", bdFromDouble.toString());
}
We found that in this case, the results were different than expected (i.e. 0.1). This is because: the result of this conversion isdouble
is an exact decimal representation of binary floating point values, whose worthy results are not something we can predict. we should useString
Constructors instead ofdouble
Constructor. In addition, we can usevalueOf
Static methods willdouble
converted toBigDecimal
Or directly use its uncorrected number plus decimal places:
@Test
public void whenBigDecimalCreatedUsingValueOf_thenValueMatches() {
BigDecimal bdFromDouble = BigDecimal.valueOf(0.1d);
BigDecimal bigFromLong=BigDecimal.valueOf(1,1);
assertEquals("0.1", bdFromDouble.toString());
assertEquals("0.1", bigFromLong.toString());
}
This method converts double to its String representation before converting it to BigDecimal. In addition, it can reuse object instances. Therefore, we should use the valueOf method first to construct functions.
3. Common APIs
method name | Explanation of usage of corresponding method |
---|---|
abs() | Absolute value, scale unchanged |
add(BigDecimal augend) | Plus, scale is the greater of the augend and the original scale |
subtract(BigDecimal augend) | Subtract, scale is the greater of the augunt and original scale |
multiply(BigDecimal multiplicand) | Multiply, scale is the sum of augend and original value scale |
divide(BigDecimal divisor) | Divide, original value/divisor. If you cannot divide all, an exception will be thrown. Scale is consistent with the original value. |
divide(BigDecimal divisor, int roundingMode) | Divide, specify the rounding method, and scale is consistent with the original value |
divide(BigDecimal divisor, int scale, int roundingMode) | Except, specify rounding method and scale |
remainder(BigDecimal divisor) | Withdraw residual value, scale is consistent with original value |
divideAndRemainder(BigDecimal divisor) | After division, an array is returned to store the division and remainder as shown in 23/3 return {7,2} |
divideToIntegralValue(BigDecimal divisor) | Except, only the integer part is retained, but the scale is still consistent with the original value |
max(BigDecimal val) | The larger value returns the original value and the larger value in val, consistent with the scale of the result |
min(BigDecimal val) | Lower value, consistent with the scale of the result |
movePointLeft(int n) | The decimal point is shifted to the left, and scale is the original value scale+n |
movePointRight(int n) | Move the decimal point to the right, and scale is the original value scale+n |
negate() | Reverse, scale does not change |
pow(int n) | Power, original value ^n, the n-th power of original value |
scaleByPowerOfTen(int n) | Equivalent to shifting the decimal point to the right by n digits, original value *10^n |
4. BigDecimal operation
Operations on BigDecimal are like other Number classes (Integer, Long, Double, etc.), BigDecimal provides operations for arithmetic and comparison operations. It also provides scaling operations, rounding and format conversion operations. It does not cause arithmetic operators+, - ,/,*
or logical operators>、< 、|、&
Overload. Instead, we useBigDecimal
Corresponding methods add, subtract, multiply, divide and compare. andBigDecimal
There are methods to extract various attributes.
4.1 extract attribute
Precision, decimal places and symbol:
@Test
public void whenGettingAttributes_thenExpectedResult() {
BigDecimal bd = new BigDecimal("-12345.6789");
assertEquals(9, bd.precision());
assertEquals(4, bd.scale());
assertEquals(-1, bd.signum());
}
4.2 Compare sizes
we usecompareTo
Methods Compare twoBigDecimal
Value of:
@Test
public void whenComparingBigDecimals_thenExpectedResult() {
BigDecimal bd1 = new BigDecimal("1.0");
BigDecimal bd2 = new BigDecimal("1.00");
BigDecimal bd3 = new BigDecimal("2.0");
assertTrue(bd1.compareTo(bd3) < 0);
assertTrue(bd3.compareTo(bd1) > 0);
assertTrue(bd1.compareTo(bd2) == 0);
assertTrue(bd1.compareTo(bd3) <= 0);
assertTrue(bd1.compareTo(bd2) >= 0);
assertTrue(bd1.compareTo(bd3) != 0);
}
The above method ignores decimal places when comparing. If you want to compare both precision and decimal places, please useequals
Method:
@Test
public void whenEqualsCalled_thenSizeAndScaleMatched() {
BigDecimal bd1 = new BigDecimal("1.0");
BigDecimal bd2 = new BigDecimal("1.00");
assertFalse(bd1.equals(bd2));
}
4.3 four operations
BigDecimal provides the following four methods for arithmetic operations:
- add --Add
- subtract -subtract
- divide --Division, there may be no division, and you must explicitly declare that the decimal place is reserved to avoid throwing
ArithmeticException
abnormal - multiply-multiply
@Test
public void whenPerformingArithmetic_thenExpectedResult() {
BigDecimal bd1 = new BigDecimal("4.0");
BigDecimal bd2 = new BigDecimal("2.0");
BigDecimal sum = bd1.add(bd2);
BigDecimal difference = bd1.subtract(bd2);
BigDecimal quotient = bd1.divide(bd2);
BigDecimal product = bd1.multiply(bd2);
assertTrue(sum.compareTo(new BigDecimal("6.0")) == 0);
assertTrue(difference.compareTo(new BigDecimal("2.0")) == 0);
assertTrue(quotient.compareTo(new BigDecimal("2.0")) == 0);
assertTrue(product.compareTo(new BigDecimal("8.0")) == 0);
}
4.4 to rounding
Since it is a mathematical operation, we have to talk about rounding. For example, when calculating the amount, we can easily encounter that the final settlement amount is RMB22.355
situation. Because the currency has no lower-score units, we use precision and rounding mode rules to clip the numbers. Java provides two classes to control rounding behaviorRoundingMode
andMathContext
。MathContext
We implement the IEEE 754R standard. At present, our usage scenarios are not well understood. We use enumeration more often.RoundingMode
。It offers eight models:
- RoundingMode.UP: With decimal places as the origin, positive numbers take the right, negative numbers take the left
- RoundingMode.DOWN: decimal places are the origin, that is, positive numbers are taken to the left, negative numbers are taken to the right
- RoundingMode.FLOOR: Take the nearest positive number to the left
- RoundingMode.CEILING: Take the nearest integer on the right
- RoundingMode.HALF_DOWN: Rounding, negative numbers take absolute value first, then rounding and negative numbers
- RoundingMode.HALF_UP: Rounding, negative number principle is the same as above
- RoundingMode.HALF_EVEN: This comparison is round. If the integer bits are odd, they are rounded, and if the integer bits are even, they are rounded.
- RoundingMode.ROUND_UNNECESSARY: No rounding is needed. If there are decimal places, throw ArithmeticException exception
5. formatted
Numbers can be formatted through the operation classjava.text.NumberFormat
andjava.text.DecimalFormat
Provide APIs for operation. In fact, we just need to usejava.text.DecimalFormat
, because it representsNumberFormat
。Let's take a look at their apis:
5.1 NumberFormat
- getInstance(Locale)、getNumberInstance(Locale)。Returns the common numerical format for the specified locale.
- NumberFormat.getCurrencyInstance(Locale)。Returns the currency format for the specified locale.
- NumberFormat.getPercentInstance(Locale)。Returns the percentage format for the specified locale.
- NumberFormat.getIntegerInstance(Locale)。Returns the integer numeric format for the specified locale.
- NumberFormat.setMinimumIntegerDigits(int)。Sets the minimum number of digits allowed for the integer part of the number.
- NumberFormat.setMaximumIntegerDigits(int)。Sets the maximum number of digits allowed for the integer part of the number.
- NumberFormat.setMinimumFractionDigits(int)。Set the minimum number of decimal places, fill in the number of insufficient digits by 0, and output it as the actual number of digits if exceeded.
- NumberFormat.setMaximumFractionDigits(int)。Set the maximum number of decimal places reserved, and no zeros will be added if insufficient.
5.2 DecimalFormat
DecimalFormat
In addition to being able to represent people aboveNumberFormat
In addition, it also provides information based onpattern
The formatting style of strings is a bit similar to formatting time. 's take a look atpattern
Rules:
-
"0"-Represents a single digit value. If there is no one, 0 is displayed. For example,"0000.0000", the integer digits or decimal digits are 4. According to the actual output, the 4 integer digits are filled with 0 before the decimal digits are filled with 0 to make up 4 digits.
-
"#"--An integer representing any number of digits. If not, it will not be displayed. Used in decimal places, only one decimal place is represented, and the excess is rounded off. For example: "#": No decimal parts, the decimal parts are rounded.“.#”:The integer part remains unchanged, with one decimal place and rounded.“.##”:The integer part remains unchanged, with two decimal places and rounded.
-
“.”-- Represents the decimal point. Note that a pattern can only appear once, and more than once will format an exception.
-
","--Used with pattern "0" to indicate a comma. Be careful not to use it after the decimal point, otherwise the formatting will be abnormal.
summary
todayBigDecimal
After summarizing and summarizing, I suggest you keep this article for later use, or you can transfer it to other students who need it.
Comments0