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


猜你喜欢
- foreach嵌套使用if标签对象取值问题最近做项目过程中,涉及到需要在 Mybatis 中 使用 foreach 进行循环读取传入的查询条
- 效果图: //偶数随机 Random evenRanm
- 1.user实体package com.demo.dto;public class User { private Integer
- 1. 数据构造索引2个文档到 hotel 索引中:PUT /hotel/_doc/1{ "title": &
- Struts2的核心功能是action,对于开发人员来说,使用Struts2主要就是编写action,action类通常都要实现com.op
- 安卓原生的按钮是多么丑,效果是多么单调,大家也是有目共睹的。 要做一个APP少不了使用按钮,一个好看的按钮少不了好看的效果和外表,这次主要跟
- 周末了,觉得我还有很多作业没有写,针对目前大家对OOM的类型不太熟悉,那么我们来总结一下各种OOM出现的情况以及解决方法。我们把各种OOM的
- Android用SharedPreferences实现登录注册注销功能前言本文用SharedPreferences本地缓存账号信息来实现登录
- Volatile关键字的作用主要有如下两个:1.线程的可见性:当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。2. 顺序一致性
- 本文通过是 * 实现的AOP功能的封装与配置的小框架.加深对 * 和AOP编程的理解设计根据配置文件的键xxx对应的值(类全名)创建相应
- Spring-Data-Redis项目(简称SDR)对Redis的Key-Value数据存储操作提供了更高层次的抽象,类似于Spring F
- Android 仿微信的键盘切换(录音,表情,文字,其他),IM通讯,类似朋友圈只要涉及到文字等相关的app都会要涉及到键盘的处理,今天就给
- 1. Dubbo是什么?Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。简单的说,
- 下面笔者说说自己对进制转换的分析:笔者认为,任何进制都可以直接转换到十进制,而十进制也可以相当容易的转换到其他进制,所以笔者在这里将十进制作
- JVM之方法返回地址JVM运行时数据区的虚拟机栈的栈帧中包含了返回地址当一个方法开始执行后,只有两种方式可以退出这个方法。第一种方式是执行引
- 冒泡排序法:关键字较小的记录好比气泡逐趟上浮,关键字较大的记录好比石块下沉,每趟有一块最大的石块沉底。算法本质:(最大值是关键点,肯定放到最
- 本次内容主要介绍基于Ehcache 3.0来快速实现Spring Boot应用程序的数据缓存功能。在Spring Boot应用程序中,我们可
- 对于 * ,学过AOP的应该都不会陌生,因为代理是实现AOP功能的核心和关键技术。那么今天我们将开始 * 的学习:一、引出 * 生活中
- synchronizedsynchronized可以用来同步块,同步方法。同步块可以用来更精确地控制对象锁,控制锁的作用域。(锁
- 本文实例为大家分享了Android Scroller的使用方法,供大家参考,具体内容如下1、scrollTo和ScrollByView类定义