java 四舍五入new BigDecimal(double)及BigDecimal valueOf(double)的区别

最近在研究java的四舍五入,其中有一个方法如下

1
new BigDecimal(val).setScale(newScale, BigDecimal.ROUND_HALF_DOWN));

Click and drag to move

其中val是要处理的浮点数

newScale表示要保留小数点后几位

BigDecimal.ROUND_HALF_DOWN表示若舍弃的部分>0.5则进位,否则直接舍弃,说白了就是五舍六入

比如说1.234,保留两位小数处理结果为1.23

比如说1.235,保留两位小数处理结果为1.23

比如说1.236,保留两位小数处理结果为1.24

为了验证我的猜测,马上写几行代码验证下

1
2
3
System.out.println(new BigDecimal(1.234).setScale(2, BigDecimal.ROUND_HALF_DOWN));
System.out.println(new BigDecimal(1.235).setScale(2, BigDecimal.ROUND_HALF_DOWN));
System.out.println(new BigDecimal(1.236).setScale(2, BigDecimal.ROUND_HALF_DOWN));

Click and drag to move

发现第二条结果与我的猜测不一致,感觉有点受伤

1
2
3
1.23
1.24
1.24

Click and drag to move

1.235小数点后三位为5,明明是要舍弃的,为何进位了,难道网上的教程都是骗人

折腾了好久,最后发现原来我的写法跟网上别人的还是有细微差别

我的是:new BigDecimal(1.235).setScale(2, BigDecimal.ROUND_HALF_DOWN)

别人家的是:new BigDecimal(“1.235”).setScale(2, BigDecimal.ROUND_HALF_DOWN),这样的写法结果确实是1.23

可是为何同样的数值,传double类型不行,传String就可以?难道这两种方法初始化的BigDecimal大小不一样?打印出来发现确实不一样

1
2
3
4
// 打印结果为1.2350000000000000976996261670137755572795867919921875	
System.out.println(new BigDecimal(1.235).toString());
// 打印结果为1.235
System.out.println(new BigDecimal("1.235").toString());

Click and drag to move

new BigDecimal(1.235)的真实值为1.2350000000000000976996261670137755572795867919921875这一长串数字,舍弃的部分是大于0.5的,所以才进位的。

所以为了避免得到错误的结果还是建议传String类型的值,如果是double类型就用如下方法先转成String再处理

1
2
new BigDecimal(String.valueOf(1.235)).setScale(2, BigDecimal.ROUND_HALF_DOWN);
new BigDecimal(Double.toString(1.235)).setScale(2, BigDecimal.ROUND_HALF_DOWN);

Click and drag to move

还有另一种方法是直接传double类型的

1
BigDecimal.valueOf(1.235).setScale(2, BigDecimal.ROUND_HALF_DOWN);

Click and drag to move

查看BigDecimal.valueOf源码,其内部实现其实也是先转成String

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* Translates a {@code double} into a {@code BigDecimal}, using
* the {@code double}'s canonical string representation provided
* by the {@link Double#toString(double)} method.
*
* <p><b>Note:</b> This is generally the preferred way to convert
* a {@code double} (or {@code float}) into a
* {@code BigDecimal}, as the value returned is equal to that
* resulting from constructing a {@code BigDecimal} from the
* result of using {@link Double#toString(double)}.
*
* @param val {@code double} to convert to a {@code BigDecimal}.
* @return a {@code BigDecimal} whose value is equal to or approximately
* equal to the value of {@code val}.
* @throws NumberFormatException if {@code val} is infinite or NaN.
* @since 1.5
*/
public static BigDecimal valueOf(double val) {
// Reminder: a zero double returns '0.0', so we cannot fastpath
// to use the constant ZERO. This might be important enough to
// justify a factory approach, a cache, or a few private
// constants, later.
return new BigDecimal(Double.toString(val));
}

Click and drag to move

总结

用BigDecimal处理浮点数时BigDecimal的初始化最好采用如下方法

1
2
double d = 1.235;
BigDecimal bd = BigDecimal.valueOf(d);

Click and drag to move