如何截断双倍到N位小数?

Squ*_*yMo 5 java rounding bigdecimal

我想将我的double值舍入到N个小数位(比如说一个),实际上只是省略了后面的所有数字:

0.123 #=> 0.1
0.19  #=> 0.1
0.2   #=> 0.2
Run Code Online (Sandbox Code Playgroud)

这个问题已经多次提出,例如这里这里.推荐的方法是使用BigDecimal然后缩放它,特别是为了避免昂贵的转换为字符串和返回.我需要的舍入模式显然是RoundingMode.DOWN.

所以方法是这样的:

static double truncate(double value, int places) {
    return new BigDecimal(value)
        .setScale(places, RoundingMode.DOWN)
        .doubleValue();
}
Run Code Online (Sandbox Code Playgroud)

但是,由于精度的降低,它会带来一些意想不到的结果:

truncate(0.2, 1) #=> 0.2
truncate(0.3, 1) #=> 0.2
truncate(0.4, 1) #=> 0.4

truncate(0.2, 3) #=> 0.2
truncate(0.3, 3) #=> 0.299
truncate(0.4, 3) #=> 0.4
Run Code Online (Sandbox Code Playgroud)

这引出了两个问题:

  1. 是它应该如何工作0.3?为什么在这种情况下会失去精确度?难道它没有击败拥有的全部目的BigDecimal吗?

  2. 如何正确截断我的值?

谢谢.

use*_*323 4

“特别是避免昂贵的字符串转换和转换” - 这正是避免精度损失的原因。您无法免费获得任意精度。

如果您需要任意精度,那么您不应该使用double而是应该使用:

static String truncate(String value, int places) {
return new BigDecimal(value)
    .setScale(places, RoundingMode.DOWN)
    .stripTrailingZeros()
    .toString()
}
Run Code Online (Sandbox Code Playgroud)

  • 值得注意的是 BigDecimal.valueOf(0.3).toString() = "0.3" 而不是 "0.2999..." 因为它无论如何都使用 double 的规范字符串表示形式。请参阅http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html (2认同)