浅谈使用DecimalFormat保留小数点的问题

1,126 阅读5分钟

在某些场景下,我们经常要将数字进行格式化,比如取2位小数,或者四舍五入、取整等等,这是最常见的。那么我们应该怎么去实现呢?

Java 提供DecimalFormat类,它可以帮你用最快的速度将数字格式化为你需要的样子。详情请看下面案例列举:

double pi = 3.1415927;//圆周率约等
//取一位整数
System.out.println(new DecimalFormat("0").format(pi));//3
//取一位整数和两位小数
System.out.println(new DecimalFormat("0.00").format(pi));//3.14
//取两位整数和三位小数,整数不足部分以0填补。
System.out.println(new DecimalFormat("00.000").format(pi));//03.142
//取所有整数部分
System.out.println(new DecimalFormat("#").format(pi));//3
//以百分比方式计数,并取两位小数
System.out.println(new DecimalFormat("#.##%").format(pi));//314.16%
long c = 299792458;//光速
//显示为科学计数法,并取五位小数
System.out.println(new DecimalFormat("#.#####E0").format(c));//2.99792E8
//显示为两位整数的科学计数法,并取四位小数
System.out.println(new DecimalFormat("00.####E0").format(c));//29.9792E7
//每三位以逗号进行分隔。
System.out.println(new DecimalFormat(",###").format(c));//299,792,458
//将格式嵌入文本
System.out.println(new DecimalFormat("光速大小为每秒,###米。").format(c));//光速大小为每秒299,792,458米。

DecimalFormat 类主要靠 # 和 0 两种占位符号来指定数字长度。

  • 0:表示如果位数不足则以 0 填充,
  • :表示只要有可能就把数字拉上这个位置(不包括 0)。

上面的例子包含了差不多所有的基本用法,如果你想了解更多,请参考 DecimalFormat 类的文档。

📌个别案例分析:

针对上面的使用,常用的就是保留小数点问题,对此,程序羊将DecimalFormat 抽取进行三个简单的封装,分别是默认的、正常四舍五入和只保留,不进行四舍五入的三个方法,方便下面案例进行调用演示:

package com.example.business.util;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;

/**
 * @ClassName: DecimalFormatUtil
 * @Description: 保留小数点相关类
 * @Author yang
 * @Date 2022/9/21
 * @Version 1.0
 */
public class DecimalFormatUtil {

    /**
     * 获取Double格式化,根据format保留n个小数点(不会四舍五入)
     * 默认采用的舍入策略是:四舍六入五奇偶。(以保留一位小数为例)
     * 如 4.56 就是 4.6, 最后一位小数大于等于 6 就向前进一位.
     * 4.54 就是 4.5, 最后一位小数小于等于 4, 就舍去
     * 4.45 就是 4.4 ; 4.55 就是 4.6 最后一位小数是 5 的时候,当前一位是奇数的时候就进一位凑成偶数,当前一位是偶数的时候就舍去.
     * 不要四舍五入的方法就是: df.setRoundingMode(RoundingMode.DOWN);
     *
     * @param strVal Double值
     * @param format 格式化字符串,#.##代表获取所有整数位并保留两位小数
     * @return
     */
    public static String getDoubleFormat(Double strVal, String format) {
        if (format == null || format.isEmpty() || format.equals("null")) {
            format = "#.##";
        }
        DecimalFormat df = new DecimalFormat(format);
        df.setRoundingMode(RoundingMode.DOWN);
        String fm = df.format(strVal);
        return fm;
    }

    /**
     * 获取Double格式化,根据format保留n个小数点(会四舍五入)
     *
     * @param strVal Double值
     * @param format 格式化字符串,#.##代表获取所有整数位并保留两位小数
     * @return
     */
    public static String getDoubleFormatByRound(Double strVal, String format) {
        if (format == null || format.isEmpty() || format.equals("null")) {
            format = "#.##";
        }
        DecimalFormat df = new DecimalFormat(format);
        df.setRoundingMode(RoundingMode.HALF_UP);//HALF_UP四舍五入
        String fm = df.format(new BigDecimal(String.valueOf(strVal)));
        return fm;
    }

    /**
     * 获取Double格式化,根据format保留n个小数点(默认策略)
     *
     * @param strVal Double值
     * @param format 格式化字符串,#.##代表获取所有整数位并保留两位小数
     * @return
     */
    public static String getDoubleFormatByDefault(Double strVal, String format) {
        if (format == null || format.isEmpty() || format.equals("null")) {
            format = "#.##";
        }
        DecimalFormat df = new DecimalFormat(format);
        String fm = df.format(new BigDecimal(String.valueOf(strVal)));
        return fm;
    }

}

(1)默认的:比较少用,使用的是进位方式是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。
(2)如果想实现普通的四舍五入,增加下面代码,切换进位方式 df.setRoundingMode(RoundingMode.HALF_UP);

(3)如果不想实现四舍五入,增加下面代码,切换进位方式,即相当于截断 df.setRoundingMode(RoundingMode.DOWN);

format的时候注意要使用 new BigDecimal,防止浮点型出现精度丢失的问题!

📌各种场景案例演示:

1.使用#占位符号,保留一位小数点,没有0的情况

double num = 4.56;
String doubleFormat1 = DecimalFormatUtil.getDoubleFormatByDefault(num, "#.#");
String doubleFormat2 = DecimalFormatUtil.getDoubleFormatByRound(num, "#.#");
String doubleFormat3 = DecimalFormatUtil.getDoubleFormat(num, "#.#");
System.out.println("DecimalFormat默认策略:" + doubleFormat1);
System.out.println("HALF_UP正常四舍五入:" + doubleFormat2);
System.out.println("只保留,不进行四舍五入" + doubleFormat3);

输出结果:

DecimalFormat默认策略:4.6
HALF_UP正常四舍五入:4.6
只保留,不进行四舍五入:4.5

2.使用#占位符号,保留一位小数点,有0的情况,不保留0

double num = 3.05;
String doubleFormat1 = DecimalFormatUtil.getDoubleFormatByDefault(num, "#.#");
String doubleFormat2 = DecimalFormatUtil.getDoubleFormatByRound(num, "#.#");
String doubleFormat3 = DecimalFormatUtil.getDoubleFormat(num, "#.#");
System.out.println("DecimalFormat默认策略:" + doubleFormat1);
System.out.println("HALF_UP正常四舍五入:" + doubleFormat2);
System.out.println("只保留,不进行四舍五入:" + doubleFormat3);

输出结果:

DecimalFormat默认策略:3
HALF_UP正常四舍五入:3.1
只保留,不进行四舍五入:3

PS:使用#占位符号的时候,遇到保留位数刚好有0存在的时候,则会省略,要想保留,使用0占位符号来指定,如下:

3.使用0占位符号,保留一位小数点,有0的情况,保留0

double num = 3.05;
String doubleFormat1 = DecimalFormatUtil.getDoubleFormatByDefault(num, "0.0");
String doubleFormat2 = DecimalFormatUtil.getDoubleFormatByRound(num, "0.0");
String doubleFormat3 = DecimalFormatUtil.getDoubleFormat(num, "0.0");
System.out.println("DecimalFormat默认策略:" + doubleFormat1);
System.out.println("HALF_UP正常四舍五入:" + doubleFormat2);
System.out.println("只保留,不进行四舍五入:" + doubleFormat3);

输出结果:

DecimalFormat默认策略:3.0
HALF_UP正常四舍五入:3.1
只保留,不进行四舍五入:3.0

4.使用0占位符号,保留四位小数点,且目标数的位数不够时

double num = 4.54;
String doubleFormat1 = StringVerifyUtil.getDoubleFormatByDefault(num, "0.0000");
String doubleFormat2 = StringVerifyUtil.getDoubleFormatByRound(num, "0.0000");
String doubleFormat3 = StringVerifyUtil.getDoubleFormat(num, "0.0000");
System.out.println("DecimalFormat默认策略:" + doubleFormat1);
System.out.println("HALF_UP正常四舍五入:" + doubleFormat2);
System.out.println("只保留,不进行四舍五入:" + doubleFormat3);

输出结果:

DecimalFormat默认策略:4.5400
HALF_UP正常四舍五入:4.5400
只保留,不进行四舍五入:4.5400

5.使用#占位符号,保留四位小数点,且目标数的位数不够时

double num = 4.54;
String doubleFormat1 = StringVerifyUtil.getDoubleFormatByDefault(num, "#.####");
String doubleFormat2 = StringVerifyUtil.getDoubleFormatByRound(num, "#.####");
String doubleFormat3 = StringVerifyUtil.getDoubleFormat(num, "#.####");
System.out.println("DecimalFormat默认策略:" + doubleFormat1);
System.out.println("HALF_UP正常四舍五入:" + doubleFormat2);
System.out.println("只保留,不进行四舍五入:" + doubleFormat3);

输出结果:

DecimalFormat默认策略:4.54
HALF_UP正常四舍五入:4.54
只保留,不进行四舍五入:4.54