Java 数据格式化&小数点后保留指定位数

DecimalFormat

使用 DecimalFormat,但是只能保留指定位数.

DecimalFormat d = new DecimalFormat("0.000");
double x = 3.45125;
System.out.println(d.format(x));  // 3.451

参考上篇博客 详解数字格式化

NumberFormat

public static String format(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);
}

Formatter

java.util.Formatter

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

String.format

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

常见使用方式

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

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

可用标识符

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

可用转换符

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

实例展示

// 测试代码
double num = 123.456789;
System.out.print(String.format("%f %n", num));  // %f:显示9位有效数字,且会进行四舍五入。%n:认换行,123.456790
System.out.print(String.format("%a %n", num)); // %a:浮点数型(十六进制),0x1.edd3c0bb46929p6
System.out.print(String.format("%g %n", num)); // %g:浮点数型(比%f,%a长度短些,显示6位有效数字,且会进行四舍五入),123.457

// 保留小数点后位
System.out.println(String.format("%.2f", num));  // 123.46

// 左对齐
int n = 3;
// 6位右对齐,不足六位补0
System.out.println(String.format("%06d", n));  // 000003

// 6位右对齐,不足六位补空格
System.out.println(String.format("%6d", n));  //      3

// 6位左对齐,不足六位补空格
System.out.println(String.format("%-6d", n)); // 3

结果如下

在这里插入图片描述

printf()

C语言printf 函数使用很方便,很高兴Java中也可以使用 printf 的格式:
System.out.printf();
具体使用格式与 String.format() 方法一样,请参考上面。

double num = 20.45628;
// 保留两位小数,%n 换行
System.out.printf("%.2f %n", num);  // 20.46
int n = 6;
// 右对齐5位
System.out.printf("%5d %n", n);
// 左对齐5位
System.out.printf("%-5d %n", n);

在这里插入图片描述

BigDecimal

A BigDecimal由任意精度整数未缩放值和32位整数比例组成。
如果为零或正数,则刻度是小数点右侧的位数。 如果是负数,则数字的非标定值乘以10,以达到等级的否定的幂。
因此, (unscaledValue × 10-scale)代表的BigDecimal值为(unscaledValue × 10-scale) 。
BigDecimal类提供了算术,缩放操作,舍入,比较,散列和格式转换的操作。 toString()方法提供了一个BigDecimal的规范表示。
BigDecimal类使其用户完全控制舍入行为。 如果未指定舍入模式,并且无法表示确切的结果,则抛出异常; 否则,可以通过向操作提供适当的MathContext对象来进行计算,以选择精度和舍入模式。
在这两种情况下,都提供八种舍入方式来控制舍入。 不建议使用此类中的整数字段(如ROUND_HALF_UP )表示舍入模式; 应该使用RoundingMode 枚举的枚举值(如RoundingMode.HALF_UP )。
---- 官方API

BigDecimal 计算和保留小数位是在Java最常用的,也是在财务或者是数据分析中比较重要的方法

构造方法

Constructor
BigDecimal(char[] in)BigDecimal的字符数组表示 BigDecimal转换为 BigDecimal ,接受与 BigDecimal(String)构造函数相同的字符序列。  
BigDecimal(char[] in, int offset, int len)BigDecimal的字符阵列表示 BigDecimal转换为 BigDecimal ,接受与 BigDecimal(String)构造函数相同的字符序列,同时允许指定子数组。  
BigDecimal(char[] in, int len, MathContext mc)BigDecimal的字符数组表示 BigDecimal转换为 BigDecimal ,接受与 BigDecimal(String)构造函数相同的字符序列,同时允许根据上下文设置指定子数组并进行舍入。  
BigDecimal(char[] in, MathContext mc)BigDecimal的字符数组表示 BigDecimal转换为 BigDecimal ,接受与 BigDecimal(String)构造函数相同的字符序列,并根据上下文设置进行舍入。  
BigDecimal(double val)double转换为 BigDecimal ,它是 double的二进制浮点值的精确十进制表示。  
BigDecimal(double val, MathContext mc)double转换为 BigDecimal ,根据上下文设置进行舍入。  
BigDecimal(int val)int翻译成 BigDecimalBigDecimal(int val, MathContext mc)int转换为 BigDecimal ,根据上下文设置进行舍入。  
BigDecimal(long val)long翻译成 BigDecimalBigDecimal(long val, MathContext mc)long转换为 BigDecimal ,根据上下文设置进行舍入。  
BigDecimal(String val)BigDecimal的字符串表示 BigDecimal转换为 BigDecimalBigDecimal(String val, MathContext mc)BigDecimal的字符串表示 BigDecimal转换为 BigDecimal ,接受与 BigDecimal(String)构造函数相同的字符串,根据上下文设置进行舍入。  
BigDecimal(BigInteger val)BigInteger翻译成 BigDecimalBigDecimal(BigInteger unscaledVal, int scale)BigInteger值和 int量表翻译成 BigDecimalBigDecimal(BigInteger unscaledVal, int scale, MathContext mc)BigInteger值和 int标度转换为 BigDecimal ,根据上下文设置进行舍入。  
BigDecimal(BigInteger val, MathContext mc) 根据上下文设置将 BigInteger转换为 BigDecimal舍入。  

在这里插入图片描述

舍入方式

BigDecimal.setScale(int newScale, int roundingMode);  // 设置格式化小数位
// 在这方法中int roundingMode具体的参数如下
BigDecimal.ROUND_UP; //向远离0的方向舍入,进位处理,2.35变为2.4
BigDecimal.ROUND_DOWN;  // 向零方向舍入,3.35变为3.3
BigDecimal.ROUND_CEILING;  // 向正无穷方向舍入
BigDecimal.ROUND_FLOOR;  // 向负无穷方向舍入 
BigDecimal.ROUND_HALF_UP;  // 向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向上舍入,1.55保留一位小数结果为1.6 
BigDecimal.ROUND_HALF_DOWN;  // 向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向下舍入,例如1.55 保留一位小数结果为1.5 
BigDecimal.ROUND_HALF_EVEN;  // 向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,如果保留位数是奇数,使用ROUND_HALF_UP ,如果是偶数,使用 
BigDecimal.ROUND_UNNECESSARY;  // 计算结果是精确的,不需要舍入模式 

上面的BigDecimal.舍入方式已经过时,改为RoundingMode.舍入方式
如下图:

在这里插入图片描述

具体示例

double n = sc.nextDouble();  // 输入一个
int k = sc.nextInt();  // 保留 k 位小数
double x = 100.0 * n / 7;
BigDecimal bd = new BigDecimal(x);  // 获取
bd = bd.setScale(k, RoundingMode.HALF_UP);  // 设置保留k位,舍入方式 RoundingMode.HALF_UP:四舍五入

注意事项

对于RoundingMode.HALF_UP行为,如果丢弃的分数的左边的数字是奇数的;
像RoundingMode.HALF_DOWN如果它是RoundingMode.HALF_DOWN行为。
请注意,这是舍入模式,统计最小化在一系列计算中重复应用时的累积误差。 有时被称为“银行家四舍五入”,主要用于美国。
这种舍入模式类似于Java中用于float和double算术的舍入策略。

如何理解这句话呢?

一个规则:

四舍六入五考虑,
五后非零就进一,
五后皆零看奇偶,
五前为偶应舍去,
五前为奇要进一。

就是四舍六入,5单独考虑,如果 5 后面有非0数那就进1,如果5后面全为0,那就看5前的数是奇数还是偶数,奇数就进1,偶数就舍去。

这样舍入模式称作“银行家四舍五入”,感觉像是以前美国的银行家,搞的舍入方式,不然遇到5就进1,若是5后面么有零头,全是零,直接进位就不太好,这样舍入对于偶数,奇数可以均分一下,这样操作可以均衡进位与舍入吧。

小数点后保留指定n位

上面谈了这么久,下面引入正题 小数点后保留指定n位

printf()

经常使用的 System.out.printf(),想着能不能指定保留的位数 k。

比如
保留2位
printf("%.2f")
能不能这样保留 k 位?
printf("%.kf")

System.out.printf("%.(%n)f",k,n); ×
发现不可以
o(╥﹏╥)o

后来发现大佬的C语言题解,有一种方法

#include <stdio.h>
int main()
{
#ifdef LOCAL
	freopen("E:\input.txt", "r", stdin);
#endif
	char s[] = "%.0f%%\n";
	int t, n, k;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d%d", &n, &k);
		printf((s[2] = k + '0', s), 100.0 * n / 7);
	}
	return 0;
}

试着改成Java版

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int t = sc.nextInt();
        char[] s = {'%', '.', '0', 'f', '%', '\n'};
        while (t-- > 0) {
            int n = sc.nextInt();
            int k = sc.nextInt();
            s[2] = (char)(k + '0');  // '0' ASCII:48
            System.out.printf(new String(s), 100.0 * n / 7);
        }
    }
}

结果如下

在这里插入图片描述


在这里插入图片描述

BigDecimal

参考上面介绍:
BigDecimal.setScale(int newScale,int roundingMode);

double n = sc.nextDouble();
int k = sc.nextInt();  // 保留 k 位
double x = 100.0 * n / 7;
BigDecimal bd = new BigDecimal(x);
bd = bd.setScale(k, RoundingMode.HALF_UP);  // 设置 保留位数,及舍入方式:四舍五入
System.out.println(bd);

结果如下

在这里插入图片描述

特别注意
在练习使用过程中,发现一个特殊的地方:

当浮点数位 0 时,保留 k 位小数,输出结果会有变化:

在这里插入图片描述

这算一个特殊的地方,比赛时试了几次,一直错误,罚时++
o(╥﹏╥)o

后来我们对 0 的情况特殊考虑,就 AK 了!

题目大意

计算 n 门 course 成绩,计算平均成绩,并保留指定位数。

代码如下

int n = sc.nextInt();
    int k = sc.nextInt();
    double sum = 0;
    for (int i = 0; i < n; i++) {
        sum += sc.nextInt();
    }
    if(sum!=0) {
        BigDecimal bd = new BigDecimal(sum);
        BigDecimal N = new BigDecimal(n);
        bd = bd.divide(N, k, RoundingMode.FLOOR);  // 除 N,保留 k位,舍入方式:下舍入
        System.out.println(bd);
    } else {
        System.out.print(0+".");
        for(int i=0;i<k;i++){
            System.out.print(0);
        }
        System.out.println();
    }
}

另外还有一种保留 k位小数的方式:模拟手工除法

联想下:手工如何计算 除法

余数 不足以 整除 除数时,余数乘 10,然后计算循环 k 次,除以 除数。注意:每次保留 商的结果。

代码就不再实现了,o(╥﹏╥)o,两篇博客,整理了几个小时.

感谢博客 Link

感谢 两位队友&宋佬

加油!!!

相关文章

HashMap是Java中最常用的集合类框架,也是Java语言中非常典型...
在EffectiveJava中的第 36条中建议 用 EnumSet 替代位字段,...
介绍 注解是JDK1.5版本开始引入的一个特性,用于对代码进行说...
介绍 LinkedList同时实现了List接口和Deque接口,也就是说它...
介绍 TreeSet和TreeMap在Java里有着相同的实现,前者仅仅是对...
HashMap为什么线程不安全 put的不安全 由于多线程对HashMap进...