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
猜你喜欢
- 前言:什么是JDBCJava 数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户
- Handler每个初学Android开发的都绕不开Handler这个“坎”,为什么说是个坎呢,首先这是Android架构的精髓之一,其次大部
- 前言注解(Annotation)不是程序,但可以对程序作出解释,也可以被其它程序(如编译器)读取。注解的格式:以@注释名在代码中存在,还可以
- 实现“摇一摇”功能,其实很简单,就是检测手机的重力感应,具体实现代码如下:1、在 AndroidManifest.xml 中添加操作权限2、
- 可以静态绑定数据源,这样就自动为DataGridView控件添加 相应的行。假如需要动态为DataGridView控件添加新行,方法有很多种
- yml 文件规则yml文件的好处,天然的树状结构,一目了然,实质上跟properties是差不多的。不支持tab缩进可以使用 "-
- 1、输出矩形以此矩形案例(4行,9列的矩形)为例public static void main(String[] args) {  
- 第一种方法:string s=abcdeabcdeabcde;string[] sArray=s.Split(c) ;foreach(str
- SharedPreferences是Android中最容易理解的数据存储技术,实际上SharedPreferences处理的就是一个key-
- 自从SEOTcs系统11月份24日更新了一下SEO得分算法以来,一直困扰我的一个问题出现了,java的数据job任务,在执行过程中会经常报以
- 我object != null要避免很多NullPointerException。有什么替代方法:if (someobject != nul
- 在基于UI元素的自动化测试中, 无论是桌面的UI自动化测试,还是Web的UI自动化测试. 首先我们需要查找和识别UI
- 注意是maven的webapp:选择maven下一步下一步。maven下载过慢在setting中加入镜像。 我也有疑问这是什么鬼格式,但是证
- 前言java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也不能做出来非常好用,
- 大致思路:注解实现方式:就是用 反射机制. 获取指定的包下使用了注解的类,存储在一个map容器, 然后获取map容器下类的属性, 利用反射给
- 在Android中,线程内部或者线程之间进行信息交互时经常会使用消息,这些基础的东西如果我们熟悉其内部的原理,将会使我们容易、更好地架构系统
- throw抛出异常的方式比较直接:if(age < 0){throw new MyException("年龄不能为负数!&q
- 1. 什么是λ表达式λ表达式本质上是一个匿名方法。让我们来看下面这个例子: public int add(int x, int
- 一、何为栈?栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称
- 前言多数据源的事务处理是个老生常谈的话题,跨两个数据源的事务管理也算是分布式事务的范畴,在同一个JVM里处理多数据源的事务,比较经典的处理方