背景:

回顾上文,由于double类型转String类型数字过小或者过大时,会自动转为科学计数法。需采用decimal format来格式化double与float的结果,这样转换成字符串不会采用科学计数法。但是根据“#”占位符进行转换时,发现获得的结果并非按照四舍五入的舍入数值方法。

 public static void main(String args[]) {Double a = 3.25,b = 3.251,c = 3.35;DecimalFormat df = new DecimalFormat("#.#");System.out.println(df.getRoundingMode());//decimal format默认舍入方法System.out.println("a:" + df.format(a) + "\n" + "b:" + df.format(b) + "\n" + "c:" + df.format(c));}

DecimalFormat 默认使用的是进位方式是RoundingMode.HALF_EVEN,此舍入模式也称为“银行家算法”,主要在美国使用。

银行家算法:四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一

规律就是看进舍位的前一位是奇数还是偶数
奇数:四舍五入
偶数:五舍六入
若进舍位是5,且后面非0:进一

如3.25 保存一位小数,进舍位前一位是2,2是偶数,五舍六入 结果就是3.2,如果是3.26 结果就是3.3;
如3.251 保存一位小数,5后面非0,所以会进一,即3.3;
再如3.35 保存一位小数 进舍位前一位是3,3是奇数,四舍五入 结果就是3.4。

如果想实现四舍五入,增加下面代码,切换进位方式

df.setRoundingMode(RoundingMode.HALF_UP);public static void main(String[] args) {DecimalFormat df=new DecimalFormat("#.#");df.setRoundingMode(RoundingMode.HALF_UP);//HALF_UP四舍五入double a=3.25,b=3.35;System.out.println(df.format(a)); //3.3System.out.println(df.format(3.251)); //3.3System.out.println(df.format(b)); //3.4
}

Java 中保留小数方法

1. 使用java.text.DecimalFormat

public static String format2(double value) {DecimalFormat df = new DecimalFormat("0.00");df.setRoundingMode(RoundingMode.HALF_UP);return df.format(value);
}

2. 使用java.text.NumberFormat

public static String format3(double value) {NumberFormat nf = NumberFormat.getNumberInstance();nf.setMaximumFractionDigits(2);/** setMinimumFractionDigits设置成2* * 如果不这么做,那么当value的值是100.00的时候返回100* * 而不是100.00*/nf.setMinimumFractionDigits(2);nf.setRoundingMode(RoundingMode.HALF_UP);/** 如果想输出的格式用逗号隔开,可以设置成true*/nf.setGroupingUsed(false);return nf.format(value);
}

3. 使用java.util.Formatter

public static String format4(double value) {/** %.2f % 表示 小数点前任意位数 2 表示两位小数 格式后的结果为 f 表示浮点型*/return new Formatter().format("%.2f", value).toString();
}

4. 使用String.format

public static String format5(double value) {return String.format("%.2f", value).toString();
}

扩展知识
String.format 作为文本处理工具,为我们提供强大而丰富的字符串格式化功能。

占位符格式为: %[index$][标识]*[最小宽度][.精度]转换符

double num = 123.4567899;
System.out.print(String.format("%f %n", num)); // 123.456790
System.out.print(String.format("%a %n", num)); // 0x1.edd3c0bb46929p6
System.out.print(String.format("%g %n", num)); // 123.457
1

可用标识:

  -,在最小宽度内左对齐,不可以与0标识一起使用。0,若内容长度不足最小宽度,则在左边用0来填充。#,对8进制和16进制,8进制前添加一个0,16进制前添加0x。+,结果总包含一个+或-号。空格,正数前加空格,负数前加-号。,,只用与十进制,每3位数字间用,分隔。(,若结果为负数,则用括号括住,且不显示符号。

可用转换符:

  b,布尔类型,只要实参为非false的布尔类型,均格式化为字符串true,否则为字符串false。n,平台独立的换行符, 也可通过System.getProperty("line.separator")获取。f,浮点数型(十进制)。显示9位有效数字,且会进行四舍五入。如99.99。a,浮点数型(十六进制)。e,指数类型。如9.38e+5。g,浮点数型(比%f,%a长度短些,显示6位有效数字,且会进行四舍五入)

精度缺失问题

public static void main(String args[]) {Double a = 3.25,b = 3.251,c = 3.35,d = 3.55;DecimalFormat df = new DecimalFormat("#.#");System.out.println(df.getRoundingMode());//decimal format默认舍入方法System.out.println("a:" + df.format(a) + "\n" + "b:" + df.format(b) + "\n" + "c:" + df.format(c) + "\n" + "d:" + df.format(d));}

不难发现代码中,Double d 经过decimal format 转换后并不满足HALF_EVEN银行家舍入法。format方法直接传float、double类型会有各种意想不到的坑。

很多地方推荐使用 df.format(BigDecimal.valueOf(a)) 或 df.format(new BigDecimal(a)),但是new BigDecimal直接传入float、double类型也会出现精度缺失问题。

BigDecimal.valueOf()传入float类型会出现精度问题。

问题代码如下:

 public static void main(String[] args) {double a = 123456789012345678.12345678,b=3.35d;BigDecimal bigDecimal = new BigDecimal("123456789012345678.12345678");float c = 3.33f;DecimalFormat df = new DecimalFormat("#");System.out.println(df.format(a));//123456789012345680System.out.println(bigDecimal);//123456789012345678.12345678System.out.println(new BigDecimal(b)); //3.350000000000000088817841970012523233890533447265625System.out.println(new BigDecimal(c)); //3.3299999237060546875System.out.println(BigDecimal.valueOf(b)); //3.35System.out.println(BigDecimal.valueOf(c)); //3.3299999237060547System.out.println(0.05+0.01);//0.060000000000000005System.out.println(1.0-0.42);//0.5800000000000001System.out.println(4.015*100);//401.49999999999994System.out.println(123.3/100);//1.2329999999999999}

BigDecimal 的 8 种 RoundingMode (舍入模式)

Java中的简单浮点数类型float和double不能够进行运算。不光是Java,在其它很多编程语言中也有这样的问题。在大多数情况下,计算的结果是准确的,但是多试几次(可以做一个循环)也会出现精度缺失问题。现在终于理解为什么要有BCD码了。BigDecimal 计算和保留小数位是在Java最常用的,也是在财务或者是数据分析中比较重要的方法,要是计算不准确,会少很多钱的哦!

 public static void main(String[] args) {/***  ROUND_UP**  进位制:不管保留数字后面是大是小 (0 除外) 都会进 1。结果会向原点的反方向对齐,正数向正无穷方向对齐,负数向负无穷方向对齐。*  向远离零的方向舍入。舍弃非零部分,并将非零舍弃部分相邻的一位数字加一*/System.out.println("------- ROUND_UP --------");System.out.println(new BigDecimal("0.098").setScale(2, BigDecimal.ROUND_UP)); // 0.10System.out.println(new BigDecimal("0.094").setScale(2, BigDecimal.ROUND_UP)); // 0.10System.out.println(new BigDecimal("-0.098").setScale(2, BigDecimal.ROUND_UP)); // -0.10System.out.println(new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_UP)); // -0.10System.out.println(new BigDecimal("-0.090").setScale(2, BigDecimal.ROUND_UP)); // -0.09System.out.println(new BigDecimal("0.090").setScale(2, BigDecimal.ROUND_UP)); // 0.09/***  ROUND_DOWN**  舍去制:截断操作,后面所有数字直接去除。结果会向原点方向对齐。*  向接近零的方向舍入。舍弃非零部分,同时不会非零舍弃部分相邻的一位数字加一,采取截取行为。*/System.out.println("------- ROUND_DOWN --------");System.out.println(new BigDecimal("0.098").setScale(2, BigDecimal.ROUND_DOWN)); // 0.09System.out.println(new BigDecimal("0.094").setScale(2, BigDecimal.ROUND_DOWN)); // 0.09System.out.println(new BigDecimal("-0.098").setScale(2, BigDecimal.ROUND_DOWN)); // -0.09System.out.println(new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_DOWN)); // -0.09System.out.println(new BigDecimal("-0.090").setScale(2, BigDecimal.ROUND_DOWN)); // -0.09System.out.println(new BigDecimal("0.090").setScale(2, BigDecimal.ROUND_DOWN)); // 0.09/***  ROUND_CEILING**  向正无穷方向对齐,转换为正无穷方向最接近的数值。如果为正数,行为和 ROUND_UP 一样;如果为负数,行为和 ROUND_DOWN 一样。此模式不会减少数值大小。*/System.out.println("------- ROUND_CEILING --------");System.out.println(new BigDecimal("0.098").setScale(2, BigDecimal.ROUND_CEILING)); // 0.10System.out.println(new BigDecimal("0.094").setScale(2, BigDecimal.ROUND_CEILING)); // 0.10System.out.println(new BigDecimal("-0.098").setScale(2, BigDecimal.ROUND_CEILING)); // -0.09System.out.println(new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_CEILING)); // -0.09System.out.println(new BigDecimal("-0.090").setScale(2, BigDecimal.ROUND_CEILING)); // -0.09System.out.println(new BigDecimal("0.090").setScale(2, BigDecimal.ROUND_CEILING)); // 0.09/***  ROUND_FLOOR**  向负无穷方向对齐。如果为正数,行为和 ROUND_DOWN 一样;如果为负数,行为和 ROUND_UP 一样。此模式不会增加数值大小。*/System.out.println("------- ROUND_FLOOR --------");System.out.println(new BigDecimal("0.098").setScale(2, BigDecimal.ROUND_FLOOR)); // 0.09System.out.println(new BigDecimal("0.094").setScale(2, BigDecimal.ROUND_FLOOR)); // 0.09System.out.println(new BigDecimal("-0.098").setScale(2, BigDecimal.ROUND_FLOOR)); // -0.10System.out.println(new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_FLOOR)); // -0.10System.out.println(new BigDecimal("-0.090").setScale(2, BigDecimal.ROUND_FLOOR)); // -0.09System.out.println(new BigDecimal("0.090").setScale(2, BigDecimal.ROUND_FLOOR)); // 0.09/***  ROUND_HALF_UP**  根据保留数字后一位 >=5 进行四舍五入。如果舍弃部分的最高位大于等于 5,向原点反方向对齐,否则向原点方向对齐。*  向 “最接近” 的数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。*  如果舍弃部分 >= 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同。*  这种模式也就是我们常说的我们的 “四舍五入”。*/System.out.println("------- ROUND_HALF_UP --------");System.out.println(new BigDecimal("0.095").setScale(2, BigDecimal.ROUND_HALF_UP)); // 0.10System.out.println(new BigDecimal("0.094").setScale(2, BigDecimal.ROUND_HALF_UP)); // 0.09System.out.println(new BigDecimal("-0.095").setScale(2, BigDecimal.ROUND_HALF_UP)); // -0.10System.out.println(new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_HALF_UP)); // -0.09System.out.println(new BigDecimal("-0.090").setScale(2, BigDecimal.ROUND_HALF_UP)); // -0.09System.out.println(new BigDecimal("-0.098").setScale(2, BigDecimal.ROUND_HALF_UP)); // -0.10/***  ROUND_HALF_DOWN**  根据保留数字后一位 >5 进行五舍六入。如果舍弃部分的最高位大于 5,向原点反方向对齐,否则向原点方向对齐。*  向 “最接近” 的数字舍入,如果与两个相邻数字的距离相等,则为向下舍入的舍入模式。*  如果舍弃部分 > 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同。*  这种模式也就是我们常说的我们的 “五舍六入”。*/System.out.println("------- ROUND_HALF_DOWN --------");System.out.println(new BigDecimal("0.096").setScale(2, BigDecimal.ROUND_HALF_DOWN)); // 0.10System.out.println(new BigDecimal("0.095").setScale(2, BigDecimal.ROUND_HALF_DOWN)); // 0.09System.out.println(new BigDecimal("-0.096").setScale(2, BigDecimal.ROUND_HALF_DOWN)); // -0.10System.out.println(new BigDecimal("-0.095").setScale(2, BigDecimal.ROUND_HALF_DOWN)); // -0.09System.out.println(new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_HALF_DOWN)); // -0.09/***   ROUND_HALF_EVEN**   向 “最接近” 的数字舍入,如果与两个相邻数字的距离相等,则相邻的偶数舍入。*   如果舍弃部分左边的数字奇数,则舍入行为与 ROUND_HALF_UP 相同;*   如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同。*   注意:在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。*   此舍入模式也称为 “银行家舍入法”,主要在美国使用。*   四舍六入,被舍位为 5 时两种情况,如果前一位为奇数,则入位,否则舍去。*/System.out.println("------- ROUND_HALF_EVEN --------");System.out.println(new BigDecimal("0.095").setScale(2, BigDecimal.ROUND_HALF_EVEN)); // 0.10System.out.println(new BigDecimal("0.094").setScale(2, BigDecimal.ROUND_HALF_EVEN)); // 0.09System.out.println(new BigDecimal("-0.095").setScale(2, BigDecimal.ROUND_HALF_EVEN)); // -0.10System.out.println(new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_HALF_EVEN)); // -0.09System.out.println(new BigDecimal("-0.085").setScale(2, BigDecimal.ROUND_HALF_EVEN)); // -0.08System.out.println(new BigDecimal("-0.084").setScale(2, BigDecimal.ROUND_HALF_EVEN)); // -0.08System.out.println(new BigDecimal("-0.086").setScale(2, BigDecimal.ROUND_HALF_EVEN)); // -0.09/***   ROUND_UNNECESSARY**   断言请求的操作具有精确的结果,因此不需要舍入。如果对获得精确结果的操作指定此舍入模式,则抛出 ArithmeticException。*/System.out.println("------- ROUND_UNNECESSARY --------");System.out.println(new BigDecimal("-0.086").setScale(2, BigDecimal.ROUND_UNNECESSARY));}

因此,Java中如果有计算的话,最好使用BigDecimal(String val) 而不用Double。

/*** 由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精* 确的浮点数运算,包括加减乘除和四舍五入。*/
public class Arith{//默认除法运算精度private static final int DEF_DIV_SCALE = 10;//这个类不能实例化private Arith(){}/*** 提供精确的加法运算。* @param v1 被加数* @param v2 加数* @return 两个参数的和*/public static double add(double v1,double v2){BigDecimal b1 = new BigDecimal(Double.toString(v1));BigDecimal b2 = new BigDecimal(Double.toString(v2));return b1.add(b2).doubleValue();}/*** 提供精确的减法运算。* @param v1 被减数* @param v2 减数* @return 两个参数的差*/public static double sub(double v1,double v2){BigDecimal b1 = new BigDecimal(Double.toString(v1));BigDecimal b2 = new BigDecimal(Double.toString(v2));return b1.subtract(b2).doubleValue();} /*** 提供精确的乘法运算。* @param v1 被乘数* @param v2 乘数* @return 两个参数的积*/public static double mul(double v1,double v2){BigDecimal b1 = new BigDecimal(Double.toString(v1));BigDecimal b2 = new BigDecimal(Double.toString(v2));return b1.multiply(b2).doubleValue();}/*** 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到* 小数点以后10位,以后的数字四舍五入。* @param v1 被除数* @param v2 除数* @return 两个参数的商*/public static double div(double v1,double v2){return div(v1,v2,DEF_DIV_SCALE);}/*** 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指* 定精度,以后的数字四舍五入。* @param v1 被除数* @param v2 除数* @param scale 表示表示需要精确到小数点以后几位。* @return 两个参数的商*/public static double div(double v1,double v2,int scale){if(scale<0){throw new IllegalArgumentException("The scale must be a positive integer or zero");}BigDecimal b1 = new BigDecimal(Double.toString(v1));BigDecimal b2 = new BigDecimal(Double.toString(v2));return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();}/*** 提供精确的小数位四舍五入处理。* @param v 需要四舍五入的数字* @param scale 小数点后保留几位* @return 四舍五入后的结果*/public static double round(double v,int scale){if(scale<0){throw new IllegalArgumentException("The scale must be a positive integer or zero");}BigDecimal b = new BigDecimal(Double.toString(v));BigDecimal one = new BigDecimal("1");return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();}
};

DecimalFormat(取舍问题,DecimalFormat四舍五入的坑)相关推荐

  1. 关于DecimalFormat的取舍问题,DecimalFormat四舍五入的坑

    背景 在使用DecimalFormat对数字进行格式化的时候,发现数字有时候四舍五入了,有时候又没有四舍五入,针对这个问题进行研究. 测试代码 public static void main (Str ...

  2. java中关于DecimalFormat四舍五入的用法

    DecimalFormat 是 NumberFormat 的一个具体子类,用于格式化十进制数字.它可以支持不同类型的数,包括整数 (123).定点数 (123.4).科学记数法表示的数 (1.23E4 ...

  3. java中的%nf_java中DecimalFormat四舍五入用法详解

    DecimalFormat 是 NumberFormat 的一个具体子类,用于格式化十进制数字.它可以支持不同类型的数,包括整数 (123).定点数 (123.4).科学记数法表示的数 (1.23E4 ...

  4. JAVA中DecimalFormat如何进行四舍五入?

    在JAVA中,有时候会对数值进行四舍五入处理,我们很快能想到DecimalFormat 类. 你有可能会用以下这种方式来处理: DecimalFormat df= new DecimalFormat( ...

  5. Java四舍五入、DecimalFormat、BigDecimal、NumberFormat使用方法

    1.BigDecimal实现四舍五入 *public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMod ...

  6. decimalformat精度丢失_笔记一:DecimalFormat BigDecimal

    一.DecimalFormat(数字格式化) 例1: import java.text.DecimalFormat; public class Test{ public static void mai ...

  7. java中NumberFormat 、DecimalFormat的介绍及使用,java数字格式化,BigDecimal数字格式化

    文章目录 前言 一.NumberFormat 1.概述 2.实例化方法 3.货币格式化 4.百分比格式化 5.NumberFormat的坑 5.1.不同的格式化对象处理相同数值返回结果不同 问题 源码 ...

  8. 详细的SQL保留小数点和遇到的坑(四舍五入/非四舍五入)

    引言 首先,明确概念,数字也是字符的一种.字符类型,特别是NVarchar类型强大到可以直接或者间接的转换成任一类型(前提转换内容准确,毕竟不可能把字母'a'转换成'1111'). 主要关键词 截取字 ...

  9. DecimalFormat 保留精度或者是 小数 比较好用

    2019独角兽企业重金招聘Python工程师标准>>> DecimalFormat 还是比较稳定和好用的 .少bug . BigDecimal 类也有 四舍五入 ,和 保留 多少位小 ...

最新文章

  1. HDU ACM 1046 Gridland 找规律
  2. python——多态
  3. pythonurllib微博登录怎么删_Python骚操作之删微博还需用手动吗?Python去做就好了!...
  4. jQuery deferred应用dom加载完毕详细源码分析(三)
  5. CF1556F-Sports Betting【状压dp,数学期望】
  6. jsp form提交到后台中文乱码_JSP与servlet之间的数据传递
  7. Java字符串替换前缀,从Bash中的字符串中删除固定的前缀/后缀
  8. java entity转dto_java 使用反射在dto和entity 实体类之间进行转换
  9. ROS笔记(27) 机械臂的组装
  10. PAT甲级1054 map的使用
  11. ASP.NET Core默认注入方式下如何注入多个实现(多种方式) - sky 胡萝卜星星 - CSDN博客...
  12. 【实例】原生 js 实现全屏滚动效果
  13. 2022Java最新学习路线(初学者必看)
  14. idc机房运维巡检_最全的弱电机房工程运维方案,后附所需表格
  15. 微信朋友圈评论功能测试
  16. 批量网刻操作系统(使用GHOST工具)
  17. V5Shop网店系统更新 版本V8.30.0606 RC1
  18. 18-FreeSwitch-配置G729转码
  19. 谈谈如何做到从未来看向当代的能源技术
  20. 元宇宙办公的未来:突破职场次元壁,重塑的不只是组织形态

热门文章

  1. 0x00B22E97 处有未经处理的异常(在 Project3.exe 中): 0xC00000FD: Stack overflow
  2. WangEditor增加附件上传功能
  3. 计算机毕业设计ssm基于客户时间窗变化的物流配送管理系统设计ro75j系统+程序+源码+lw+远程部署
  4. android 仿蘑菇街首页,高仿蘑菇街欢迎页
  5. 雅虎市值_雅虎如何一次又一次错失千亿美元
  6. 项目用到的注解(乱七八糟)
  7. SAP自动检验批应用中检验开始日期和检验结束日期的产生逻辑
  8. 用Verilog实现串并转换
  9. 必不可少!STL源码目录结构分析,附加源码下载链接
  10. python 过采样 权重实现_不平衡数据集的处理 - osc_sqq5osi1的个人空间 - OSCHINA - 中文开源技术交流社区...