如何将 double 截断为 N 位小数?

How to truncate double to N decimal places?

我想将我的 double 值四舍五入到小数点后 N 位(比如一位),基本上只是忽略后面的所有数字:

0.123 #=> 0.1
0.19  #=> 0.1
0.2   #=> 0.2

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

所以方法是这样的:

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

但是,由于精度的损失,它returns有些出乎意料的结果:

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

这有两个问题:

  1. 0.3 应该如何工作?为什么在这种情况下会损失精度?它不会破坏 BigDecimal 的全部目的吗?

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

谢谢。

将数字乘以 pow(10,n) 并以整数类型存储,然后再除以 pow(10,n)

double 截断为 N 位小数并不是一个真正有意义的问题,因为简单的十进制数(例如 0.3)不能表示为 double

如果你想知道 double 0.3 的真实价值,你可以

System.out.println(new BigDecimal(0.3));

你会发现它真的是

0.299999999999999988897769753748434595763683319091796875

所以 0.299 是正确答案。

这个问题只有使用 BigDecimal 并以 BigDecimalString 而非 double 给出答案才真正有意义。 @user1886323 的回答显示了如何做到这一点。

"in particular to avoide the expensive conversion to String and back" - 这正是避免精度损失的原因。您无法免费获得任意精度。

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

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

我知道这是一个古老的post,但我只是想出了一个简单的解决方案:

有第二个双精度数,它接收要截断的数字的模数,然后从第一个数中减去它。示例(截断为一位数)

    double original = 23.875114784205696;
    double truncater = original % 0.1;
    //truncater now equals 0.075114784205696;
    original - truncater = 23.8;

显然这可以扩展到任何需要截断的地方,虽然它可能不会缩小内存分配中的双倍,但它为了显示目的切断结尾。