Java DecimalFormat 保留小数位及四舍五入的陷阱介绍
作者:智鼎在线IT团队 时间:2023-11-09 04:49:33
需求
业务需要导出的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
//
RoundingMode.HALF_EVEN
想了解HALF_EVEN,去官网API看了下
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模式是否是自己需要的.如果不是记得手动设置下.
来源:https://blog.csdn.net/baixf/article/details/88792219
![](/images/zang.png)
![](/images/jiucuo.png)
猜你喜欢
基于android实现五子棋开发
![](https://img.aspxhome.com/file/2023/3/126683_0s.jpg)
spring-boot-maven-plugin:打包时排除provided依赖问题
![](https://img.aspxhome.com/file/2023/5/72545_0s.png)
c#中单例类与静态类的区别以及使用场景
![](https://img.aspxhome.com/file/2023/0/102210_0s.jpg)
C++ 中const修饰虚函数实例详解
Android App中使用Gallery制作幻灯片播放效果
![](https://img.aspxhome.com/file/2023/1/139461_0s.jpg)
使用Hibernate根据实体类自动生成表的方法
Java实现限定时间CountDownLatch并行场景
selenium高效应对Web页面元素刷新的实例讲解
Java程序图形用户界面设计之按钮与布局
![](https://img.aspxhome.com/file/2023/2/61572_0s.png)
DevExpress实现为TextEdit设置水印文字的方法
![](https://img.aspxhome.com/file/2023/1/119451_0s.png)
C#微信公众平台开发之access_token的获取存储与更新
![](https://img.aspxhome.com/file/2023/8/111228_0s.jpg)
Java实战之酒店人事管理系统的实现
![](https://img.aspxhome.com/file/2023/0/71200_0s.jpg)
Java多线程案例之单例模式懒汉+饿汉+枚举
![](https://img.aspxhome.com/file/2023/0/77130_0s.png)
Maven默认中央仓库(settings.xml 配置详解)
Android studio 运行main 函数的方法
![](https://img.aspxhome.com/file/2023/5/89535_0s.png)
TC 集群Seata1.6高可用架构源码解析
![](https://img.aspxhome.com/file/2023/1/75801_0s.png)
Java8如何使用Lambda表达式简化代码详解
android开发之蜂鸣提示音和震动提示的实现原理与参考代码
java.net.SocketException: Connection reset 解决方法
![](https://img.aspxhome.com/file/2023/6/57416_0s.png)