Java提供了两种数据类型存储小数:double
和float
,double
是默认的小数类型,比如:
double PI = 3.1415;
如果想使用float
则需要在小数后面加上f
,否则会报错
float f = 3.14159f;
然而,当使用精确数值的时候这两种类型都不能使用,例如金钱和四舍五入。此时我们可以使用java.math.BigDecimal
类。
如果取一个数字到N位小数点,要怎么做呢?
这里总结有两种方式:格式化和四舍五入。
如果只是想打印n位小数,可以使用字符串格式化进行打印:
System.out.printf("格式化成小数点后3位: %.3f %n", PI);
//打印结果:格式化成小数点后3位: 3.142
如果想要格式化成某个值,可以使用java.text.DecimalFormat
类,比如:
public static void bigDecimalFormatting(double PI){
DecimalFormat df = new DecimalFormat("###.###");
System.out.println(df.format(PI));
}
//打印结果:3.142
DecimalFormat允许显式地设置舍入行为,比上面使用的String.format()
提供更多的输出控制。
可以写一个工具方法使用BigDecimal四舍五入double类型数值
private static double round(double value, int places) {
if (places < 0) {
throw new IllegalArgumentException();
}
BigDecimal bd = new BigDecimal(Double.toString(value));
bd = bd.setScale(places, RoundingMode.HALF_UP);
double doubleValue = bd.doubleValue();
System.out.println( doubleValue);
return doubleValue;
}
public static void main(String[] args) {
double PI = 3.141592;
round(PI , 3);
}
// 输出:3.142
使用BigDecimal时,重要的一点要「特别注意」:「当使用BigDecimal的构造方法时,一定要使用BigDecimal(String)
构造方法」,要不然可能会精度问题出现,得不到想要的结果。
除了写工具类之外,也可以引入Apache Commons Math 依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId>
<version>3.6.1</version>
</dependency>
引入依赖之后可以使用Precision.round()
方法,它也是对BigDecimal的封装
double value = Precision.round(PI, 3);
System.out.println(value);
//打印结果:3.142
默认情况下,Precision.round()
方法是BigDecimal.ROUND_HALF_UP
舍入方式的,和上面写的round()方法一样,所以结果也是一样的。
DoubleRounder是decimal4j库中的一个工具类,它提供了从0到18位小数点快速且使用Garbage-free思想(避免或减少对象的创建)的四舍五入方法。
依赖如下:
<dependency>
<groupId>org.decimal4j</groupId>
<artifactId>decimal4j</artifactId>
<version>1.0.3</version>
</dependency>
简单使用一下:
public static void doubleRounder(double PI){
double round = DoubleRounder.round(PI, 3);
System.out.println(round);
}
// 打印结果:3.142
但是,某些场景下DoubleRounder会出现精度问题,比如:
System.out.println(DoubleRounder.round(256.025d, 2));
本来应该打印的是256.03
结果打印的却是256.03
。
另一种对小数进行舍入的方法是Math.round()
方法。
在这种情况下,我们可以通过乘和除以10^n来控制小数点后n位:
public static double roundAvoid(double value, int places) {
double scale = Math.pow(10, places);
double v = Math.round(value * scale) / scale;
System.out.println(v);
return v;
}
//打印结果:3.142
这个方法列出来只用于学习的目的,「不建议使用此方法,因为它会截断值」。在很多情况下舍入的值都是不正确的。
roundAvoid(1000.0d, 17);
// 打印结果: 92.23372036854776
roundAvoid(260.775d, 2);
// 打印的是 260.77 而不是预想的 260.78
本篇文章介绍了不同的方式取一个数字到N位小数点,我们可以在不改变值的情况下进行格式化输入,也可以通过四舍五入的方式进行取值,同时也列举出几个类库来解决四舍五入的问题。