BigDecimal 在与浮点数转换时不保留舍入值 [duplicate]

BigDecimal not retaining rounded value when converting to/from float [duplicate]

提问人:jeninja 提问时间:11/12/2021 最后编辑:jeninja 更新时间:11/14/2021 访问量:755

问:

我有一个函数,可以使用BigDecimal.setScale将浮点数四舍五入到n位数

private float roundPrice(float price, int numDigits) {
    BigDecimal bd = BigDecimal.valueOf(price);
    bd = bd.setScale(numDigits, RoundingMode.HALF_UP);
    float roundedFloat = bd.floatValue();
    return roundedFloat;
}

public void testRoundPrice() {
    float numberToRound = 0.2658f;
    System.out.println(numberToRound);
    float roundedNumber = roundPrice(numberToRound, 5);
    System.out.println(roundedNumber);
    BigDecimal bd = BigDecimal.valueOf(roundedNumber);
    System.out.println(bd);
}

输出:

0.2658
0.2658
0.26579999923706055

如何防止 BigDecimal 在四舍五入值的末尾添加所有这些额外的数字?

注意:我无法执行以下操作,因为我无法访问 api 调用函数中的位数。

System.out.println(bd.setScale(5, RoundingMode.CEILING));
Java 舍入 大十进制

评论

0赞 kiner_shah 11/12/2021
看这个:docs.oracle.com/javase/7/docs/api/java/math/...
7赞 Louis Wasserman 11/12/2021
在任何时候使用 float 都会破坏 BigDecimal 的好处。要获得一致的小数位数,任何地方都不能有任何浮点数。
0赞 jeninja 11/12/2021
@LouisWasserman那么,如果我使用双精度,那应该可以解决它吗?
4赞 chrylis -cautiouslyoptimistic- 11/12/2021
@jeninja double 是一个浮点数,只是多了几位数字。使用 BigDecimal
2赞 Louis Wasserman 11/12/2021
@jeninja:没有。除了 BigDecimal 之外,您不能使用任何东西。任何浮点数,任何双精度值,都可能破坏小数精度。甚至不要用它来初始化 BigDecimal;使用字符串,例如new BigDecimal("0.2658")

答:

5赞 Anonymous 11/12/2021 #1

反之亦然。 说的是实话。0.26579999923706055 更接近您在四舍五入之前和之后一直得到的值。A 是二进制而不是十进制数,不能精确地保持 0.2658。实际上,0.265799999237060546875 是我们所能得到的。BigDecimalfloatfloat

打印浮点数时,不会获得完整值。会发生一些舍入,因此尽管具有上述值,但您只能看到 .float0.2658

当你从 创建 时,你实际上是首先转换为 a(因为这是接受的)。的值与 相同,但打印为 0.26579999923706055,这也是您获得的值。BigDecimalfloatdoubleBigDecimal.valueOf()doublefloatBigDecimal

如果您想要一个打印值而不是其中的确切值或接近的东西,以下方法可能会起作用:BigDecimalfloat

    BigDecimal bd = new BigDecimal(String.valueOf(roundedNumber));
    System.out.println(bd);

输出:

0.2658

但是,您可能会对其他值感到惊讶,因为 a 没有那么高的精度。float

编辑:您有效地将float ->double -> String -> BigDecimal 转换。

达伍德·伊本·卡里姆(Dawood ibn Kareem)的这些有见地的评论让我进行了一些研究:

实际上是0.265799999237060546875。

嗯,是通过调用值返回的值。这与数字不同 其实由那来代表.这就是为什么通常不返回相同值的原因 如。了解 如果您要使用浮点值,则有所不同 和 .0.26579999923706055toStringdoubledoubleBigDecimal.valueOf(double)new BigDecimal(double)BigDecimal

那么到底发生了什么:

  1. 在四舍五入之前和之后,您的内部值为 0.265799999237060546875。float
  2. 当您将 传递给 时,您实际上是在将 -> -> -> 转换。floatBigDecimal.valueOf(double)floatdoubleStringBigDecimal
    • 的值与 0.265799999237060546875 相同。doublefloat
    • 将 的转换四舍五入 一点 .String"0.26579999923706055"
    • 因此,你得到的值是 0.26579999923706055,即你看到和询问的值。BigDecimal

从以下文档:BigDecimal.valueOf(double)

使用 's 将 a 转换为 a 方法提供的规范字符串表示形式。doubleBigDecimaldoubleDouble.toString(double)

链接

评论

1赞 Dawood ibn Kareem 11/14/2021
嗯,是通过调用值返回的值。这与实际表示的数字不同。这就是为什么通常不会返回与 相同的值的原因。如果您要使用浮点值和 .0.26579999923706055toStringdoubledoubleBigDecimal.valueOf(double)new BigDecimal(double)BigDecimal
0赞 Anonymous 11/14/2021
@DawoodibnKareem 你确实是对的!今天我学会了。谢谢。我已经编辑过了。
0赞 Dawood ibn Kareem 11/14/2021
我已经投了赞成票。您的编辑使答案变得更好。
2赞 jeninja 11/12/2021 #2

我决定修改我的程序,以使用 BigDecimal 作为对象中财产价格的基本类型,而不是类型 float。虽然一开始很棘手,但从长远来看,它绝对是更清洁的解决方案。

public class Order {
    // float price; // old type
    BigDecimal price; // new type
}