Java DecimalFormat introduction to the trap of preserving decimal places and rounding

  • 2021-11-29 06:48:55
  • OfStack

Directory requirements code implementation found problem RoundingMode.HALF_EVEN wrong code test RoundingMode.HALF_EVEN correct code test RoundingMode.HALF_EVEN conclusion

Demand

The service needs to export the digital content of Excel to keep two decimal places, and 4 round 5

Code implementation

Code plagiarized by Baidu 1 Circle


DecimalFormat dfScale2 = new DecimalFormat("###.##");
dfScale2.format(1.125D);

Find a problem

Exporting data is very strange. Not all data is as 4-rounded and 5-inputted as expected.

After investigation, it is finally found that the problem is RoundingMode, so HALF_UP should be used.

HALF_EVEN is used by default for DecimalFormat


DecimalFormat dfScale2 = new DecimalFormat("###.##");
System.out.println("dfScale2.getRoundingMode()=" + dfScale2.getRoundingMode());
// Output result 
dfScale2.getRoundingMode()=HALF_EVEN
//

RoundingMode.HALF_EVEN

To find out about HALF_EVEN, go to official website API and have a look

HALF_EVEN is rounded to 5 (for example, 2.115 with two decimal places reserved), followed by a non-0 value to 1 (for example, 2.11500001 with two decimal places reserved is formatted as 2.12). When there are no numbers or all zeros after 5, it is rounded if it is preceded by even numbers, and it is rounded if it is odd numbers. The goal is to make the first rounded digit even.

CEILING is approaching a larger value Rounding mode to round towards positive infinity. DOWN Rounding Down Rounding mode to round towards zero. FLOOR nears smaller values Rounding mode to round towards negative infinity. HALF_DOWN 5 rounding 6 inputs Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round down. HALF_EVEN Rounding mode to round towards the "nearest neighbor" unless both neighbors are equidistant, in which case, round towards the even neighbor. HALF_UP 4 rounding 5 inputs Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up. UNNECESSARY sets this mode and throws an exception for exact value formatting Rounding mode to assert that the requested operation has an exact result, hence no rounding is necessary. UP carries away from the digit 0. Rounding mode to round away from zero.

Bad code test RoundingMode.HALF_EVEN

In order to better understand HALF_EVEN, I wrote some test code but found myself more confused … I don't know what mechanism HALF_EVEN is … The output mantissa is very irregular.


import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.*;
public class LocalTest {
// Definition 1 Object that preserves the two-place decimal format  DecimalFormat  Variables of  dfScale2
 @Test
  public void testDecimalFormat() {
    DecimalFormat dfScale2 = new DecimalFormat("###.##");
    System.out.println("dfScale2.getRoundingMode()=" + dfScale2.getRoundingMode());
    System.out.println("dfScale2.format(1.125D)=" + dfScale2.format(1.125D));
    System.out.println("dfScale2.format(1.135D)=" + dfScale2.format(1.135D));
    System.out.println("dfScale2.format(1.145D)=" + dfScale2.format(1.145D));
    System.out.println("dfScale2.format(1.225D)=" + dfScale2.format(1.225D));
    System.out.println("dfScale2.format(1.235D)=" + dfScale2.format(1.235D));
    System.out.println("dfScale2.format(1.245D)=" + dfScale2.format(1.245D));
    System.out.println();
    System.out.println("dfScale2.format(2.125D)=" + dfScale2.format(2.125D));
    System.out.println("dfScale2.format(2.135D)=" + dfScale2.format(2.135D));
    System.out.println("dfScale2.format(2.145D)=" + dfScale2.format(2.145D));
    System.out.println("dfScale2.format(2.225D)=" + dfScale2.format(2.225D));
    System.out.println("dfScale2.format(2.235D)=" + dfScale2.format(2.235D));
    System.out.println("dfScale2.format(2.245D)=" + dfScale2.format(2.245D));
    System.out.println();
    System.out.println("dfScale2.format(3.125D)=" + dfScale2.format(3.125D));
    System.out.println("dfScale2.format(3.135D)=" + dfScale2.format(3.135D));
    System.out.println("dfScale2.format(3.145D)=" + dfScale2.format(3.145D));
    System.out.println("dfScale2.format(3.225D)=" + dfScale2.format(3.225D));
    System.out.println("dfScale2.format(3.235D)=" + dfScale2.format(3.235D));
    System.out.println("dfScale2.format(3.245D)=" + dfScale2.format(3.245D));
    System.out.println();
    System.out.println("dfScale2.format(4.125D)=" + dfScale2.format(4.125D));
    System.out.println("dfScale2.format(4.135D)=" + dfScale2.format(4.135D));
    System.out.println("dfScale2.format(4.145D)=" + dfScale2.format(4.145D));
    System.out.println("dfScale2.format(4.225D)=" + dfScale2.format(4.225D));
    System.out.println("dfScale2.format(4.235D)=" + dfScale2.format(4.235D));
    System.out.println("dfScale2.format(4.245D)=" + dfScale2.format(4.245D));
  }
 }

dfScale2.getRoundingMode()=HALF_EVEN
dfScale2.format(1.125D)=1.12
dfScale2.format(1.135D)=1.14
dfScale2.format(1.145D)=1.15
dfScale2.format(1.225D)=1.23
dfScale2.format(1.235D)=1.24
dfScale2.format(1.245D)=1.25
dfScale2.format(2.125D)=2.12
dfScale2.format(2.135D)=2.13
dfScale2.format(2.145D)=2.15
dfScale2.format(2.225D)=2.23
dfScale2.format(2.235D)=2.23
dfScale2.format(2.245D)=2.25
dfScale2.format(3.125D)=3.12
dfScale2.format(3.135D)=3.13
dfScale2.format(3.145D)=3.15
dfScale2.format(3.225D)=3.23
dfScale2.format(3.235D)=3.23
dfScale2.format(3.245D)=3.25
dfScale2.format(4.125D)=4.12
dfScale2.format(4.135D)=4.13
dfScale2.format(4.145D)=4.14
dfScale2.format(4.225D)=4.22
dfScale2.format(4.235D)=4.24
dfScale2.format(4.245D)=4.25

Proper code tests RoundingMode.HALF_EVEN

Suddenly found himself ignoring a thing, the test parameters are used in double type. Think of double type is not accurate. But lucky psychology and knowledge is not reliable think 3 decimal should not affect it. Change the next code, the parameters to BigDecimal type

When using BigDecimal, the parameters are passed in strings as much as possible, which is more accurate than passing in double.


new BigDecimal("1.125")

  @Test
  public void testDecimalFormat() {
    DecimalFormat dfScale2 = new DecimalFormat("###.##");
      dfScale2.setRoundingMode(RoundingMode.HALF_EVEN);
    System.out.println("dfScale2.getRoundingMode()=" + dfScale2.getRoundingMode());
        System.out.println("dfScale2.format(new BigDecimal(\"1.1251\"))=" + dfScale2.format(new BigDecimal("1.1251")));
    System.out.println("dfScale2.format(new BigDecimal(\"1.1351\"))=" + dfScale2.format(new BigDecimal("1.1351")));
    System.out.println("dfScale2.format(new BigDecimal(\"1.1451\"))=" + dfScale2.format(new BigDecimal("1.1451")));
    System.out.println("dfScale2.format(new BigDecimal(\"1.2250\"))=" + dfScale2.format(new BigDecimal("1.2250")));
    System.out.println("dfScale2.format(new BigDecimal(\"1.2350\"))=" + dfScale2.format(new BigDecimal("1.2350")));
    System.out.println("dfScale2.format(new BigDecimal(\"1.2450\"))=" + dfScale2.format(new BigDecimal("1.2450")));
    System.out.println("dfScale2.format(new BigDecimal(\"1.22501\"))=" + dfScale2.format(new BigDecimal("1.22501")));
    System.out.println("dfScale2.format(new BigDecimal(\"1.23505\"))=" + dfScale2.format(new BigDecimal("1.23505")));
    System.out.println("dfScale2.format(new BigDecimal(\"1.24508\"))=" + dfScale2.format(new BigDecimal("1.24508")));
   

dfScale2.getRoundingMode()=HALF_EVEN
dfScale2.format(new BigDecimal("1.1251"))=1.13
dfScale2.format(new BigDecimal("1.1351"))=1.14
dfScale2.format(new BigDecimal("1.1451"))=1.15
dfScale2.format(new BigDecimal("1.2250"))=1.22
dfScale2.format(new BigDecimal("1.2350"))=1.24
dfScale2.format(new BigDecimal("1.2450"))=1.24
dfScale2.format(new BigDecimal("1.22501"))=1.23
dfScale2.format(new BigDecimal("1.23505"))=1.24
dfScale2.format(new BigDecimal("1.24508"))=1.25

Conclusion

1. Be alert to the unstable result of RoundingMode caused by the inaccuracy of doulbe. Even in the mode of 4 rounding and 5 entering, the use of double type parameters will not meet the expectations.

2. When using numeric formatting, pay attention to whether the default RoundingMode mode is what you need. If not, remember to set it manually.


Related articles: