JAVA DecimalFormat 保留小数位以及四舍五入的陷阱

需求

业务需要导出的Excel的数字内容保留两位小数,并且四舍五入

代码实现

百度一圈所抄袭的代码

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

发现问题

导出数据很诡异.不是所有数据都是如所想的四舍五入.
经过排查最终发现是RoundingMode的问题,应该使用HALF_UP,
DecimalFormat 默认使用的是HALF_EVEN

DecimalFormat dfScale2 = new DecimalFormat("###.##");
System.out.println("dfScale2.getRoundingMode()=" + dfScale2.getRoundingMode());
//输出结果
dfScale2.getRoundingMode()=HALF_EVEN

@Test
    public void decimalFormatTest() {
        DecimalFormat decimalFormat_HAFL_EVEN = new DecimalFormat("#.##");
        decimalFormat_HAFL_EVEN.setRoundingMode(RoundingMode.HALF_EVEN);
        System.out.println("decimalFormat_HAFL_EVEN.format(new BigDecimal(12.245))=" + decimalFormat_HAFL_EVEN.format(new BigDecimal(12.245D)));
        System.out.println("decimalFormat_HAFL_EVEN.format(new BigDecimal(\"12.245\"))=" + decimalFormat_HAFL_EVEN.format(new BigDecimal("12.245")));
//        decimalFormat_HAFL_EVEN.format(new BigDecimal(12.245))=12.24
//        decimalFormat_HAFL_EVEN.format(new BigDecimal("12.245"))=12.24

        DecimalFormat decimalFormat_HALF_UP = new DecimalFormat("#.##");
        decimalFormat_HALF_UP.setRoundingMode(RoundingMode.HALF_UP);
        System.out.println("decimalFormat_HALF_UP.format(new BigDecimal(12.245))=" + decimalFormat_HALF_UP.format(new BigDecimal(12.245D)));
        System.out.println("decimalFormat_HALF_UP.format(new BigDecimal(\"12.245\"))=" + decimalFormat_HALF_UP.format(new BigDecimal("12.245")));
//        decimalFormat_HALF_UP.format(new BigDecimal(12.245))=12.24
//        decimalFormat_HALF_UP.format(new BigDecimal("12.245"))=12.25
    }

RoundingMode.HALF_EVEN

想了解HALF_EVEN,去官网API看了下
https://docs.oracle.com/javase/8/docs/api/java/math/RoundingMode.html
HALF_EVEN 被舍位是5(如保留两位小数的2.115),后面还有非0值进1(如保留两位小数的2.11500001 格式化为2.12),5后面没有数字或者都是0时,前面是偶数则舍,是奇数则进1,目标是让被舍前一位变为偶数.

CEILING 向更大的值靠近
Rounding mode to round towards positive infinity.
DOWN向下取整
Rounding mode to round towards zero.
FLOOR 向更小的值靠近
Rounding mode to round towards negative infinity.
HALF_DOWN 五舍六入
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 四舍五入
Rounding mode to round towards “nearest neighbor” unless both neighbors are equidistant, in which case round up.
UNNECESSARY 设置这个模式,对于精确值格式化会抛出异常
Rounding mode to assert that the requested operation has an exact result, hence no rounding is necessary.
UP 向远离数字0进行进位.
Rounding mode to round away from zero.
在这里插入图片描述

错误的代码测试RoundingMode.HALF_EVEN

为了更好的理解HALF_EVEN,写了些测试代码但是发现自己更迷惘了…搞不清楚到底HALF_EVEN是什么机制进舍…输出结果的尾数很不规律.

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.*;
public class LocalTest {
//定义一个保留两位小数格式的 DecimalFormat 的变量 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

正确的代码测试RoundingMode.HALF_EVEN

突然发现自己忽略了一个事情,测试的参数都是用的double类型.想起来double类型不精准.但是侥幸心理以及知识不牢靠以为 3位小数应该影响不大吧.改了下代码,把参数改为BigDecimal类型
使用BigDecimal时,参数尽量传入字符串,要比传入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

结论

1 警觉doulbe的不精确所引起RoundingMode结果不稳定的问题,即使是四舍五入的模式,对double类型参数使用也会有不满足预期的情况.
2 使用数字格式化时,要注意默认RoundingMode模式是否是自己需要的.如果不是记得手动设置下.

### 回答1: 可以使用Java中的DecimalFormat类来实现float保留两位小数并且四舍五入的操作。示例代码如下: ``` float num = 3.1415926f; DecimalFormat df = new DecimalFormat("#.00"); String result = df.format(num); System.out.println(result); // 输出:3.14 ``` 在这个示例中,我们首先定义了一个float类型的变量num,其值为3.1415926。然后创建了一个DecimalFormat对象df,指定格式为保留两位小数的"#.00"。最后调用df.format(num)方法,将num格式化为保留两位小数的字符串,并将其赋值给result变量。最终输出result,结果为3.14,符合我们的要求。 ### 回答2: 在Java中,我们可以使用DecimalFormat类来控制浮点数的小数位数和四舍五入。具体步骤如下: 1. 首先,我们需要导入java.text.DecimalFormat类:import java.text.DecimalFormat; 2. 创建一个DecimalFormat对象,指定数字格式。这里我们可以使用"0.00"来表示保留两位小数的格式,即:DecimalFormat df = new DecimalFormat("0.00"); 3. 使用DecimalFormat的format方法,将浮点数作为参数传入,得到保留两位小数并四舍五入的结果。例如:float num = 3.14159f; String result = df.format(num); 4. 最后,通过打印或使用结果进行后续操作。例如:System.out.println(result); 综上所述,通过导入DecimalFormat类,使用指定的数字格式,我们可以实现对浮点数进行保留两位小数并四舍五入的操作。 ### 回答3: 在Java中,我们可以使用DecimalFormat类来实现保留两位小数并进行四舍五入的操作。首先,我们需要导入java.text.DecimalFormat类。然后,我们可以创建一个DecimalFormat对象,并通过调用setRoundingMode方法将其设置为四舍五入模式。接下来,我们可以调用format方法将浮点数转换为保留两位小数并进行四舍五入的字符串表示。最后,我们可以将结果打印出来或者进行其他操作。 下面是一个示例代码: ```java import java.text.DecimalFormat; public class Main { public static void main(String[] args) { float number = 3.14159f; DecimalFormat decimalFormat = new DecimalFormat("#.00"); decimalFormat.setRoundingMode(java.math.RoundingMode.HALF_UP); String result = decimalFormat.format(number); System.out.println(result); } } ``` 在上面的示例中,我们将浮点数3.14159保留两位小数并进行四舍五入,得到的结果是3.14。 注意,由于浮点数的精度问题,可能会出现一些精度丢失的情况。因此,在进行浮点数运算之前,最好先转换为BigDecimal类型进行精确计算,然后再进行保留小数位数并四舍五入的操作。
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值