【问题标题】:How to truncate double to N decimal places?如何将double截断为N个小数位?
【发布时间】:2022-03-06 16:36:06
【问题描述】:

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

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

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

所以方法是这样的:

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

但是,由于精度的损失,它返回了一些意想不到的结果:

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. 如何正确截断我的值?

谢谢。

【问题讨论】:

    标签: java rounding bigdecimal


    【解决方案1】:

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

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

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

    【讨论】:

    • 当然,如果你真的需要避免从字符串进行“昂贵”的转换,那么你应该自己编写一个只操作字符串的方法,例如使用 value.indexOf(".") + n
    • 这实际上不起作用,它似乎在大多数情况下都起作用。例如,truncate("0.1234567891012345678", 18) 是错误的。
    • 你是对的 - 我已经修改了答案以返回一个字符串而不是双精度。
    • 是的,用double 播种BigDecimal 看起来不是一个好主意。我真的需要对浮点表示进行自我教育。谢谢。
    • 值得注意的是 BigDecimal.valueOf(0.3).toString() = "0.3" 而不是 "0.2999..." 因为它无论如何都使用双精度的规范字符串表示。见docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html
    【解决方案2】:

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

    【讨论】:

    • 你能解释一下吗
    • 是的,看看你是否有 0.3124 并且你想将它截断为 0.31。你应该将它乘以 100,它会变成 31.24,由于转换为 int,它将是 31。然后当你再次将它除以 100(使用 casting)时,它将是 0.31
    • 这个解决方案已经在here 讨论过,它确实不适用于大值。它很适合我,我只是想知道是否有更通用的方法。
    • 哦,对不起@SqueezyMo 我没看到。
    【解决方案3】:

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

    如果你想知道double0.3的真正价值你可以这样做

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

    你会发现它是真的

    0.299999999999999988897769753748434595763683319091796875
    

    所以0.299 是正确答案。

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

    【讨论】:

      【解决方案4】:

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

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

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

      显然,这可以扩展到任何需要的截断,虽然它可能不会缩小内存分配中的双精度,但它为了显示目的而切断末端。

      【讨论】:

        猜你喜欢
        • 2018-04-12
        • 1970-01-01
        • 2016-06-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-02
        相关资源
        最近更新 更多