Java 某些计算返回错误结果的BigDecimal除法

Java 某些计算返回错误结果的BigDecimal除法,java,bigdecimal,Java,Bigdecimal,我读了一些关于炫耀点数及其数学的问题。在我看来,问题只会在更大的范围内发生(即10+小数)。现在,我的问题已经发生在2位小数,在一些情况下,它相当大: 以下代码: System.out.println( String.format("%.2f", BigDecimal.valueOf(2.8).divide(BigDecimal.valueOf(3.87), 2).doubleValue())); System.out.println( String.format

我读了一些关于炫耀点数及其数学的问题。在我看来,问题只会在更大的范围内发生(即10+小数)。现在,我的问题已经发生在2位小数,在一些情况下,它相当大:

以下代码:

System.out.println(
        String.format("%.2f", BigDecimal.valueOf(2.8).divide(BigDecimal.valueOf(3.87), 2).doubleValue()));
System.out.println(
        String.format("%.2f", BigDecimal.valueOf(2.41).divide(BigDecimal.valueOf(2.73), 2).doubleValue()));
分别生成此输出:

0.80
0.89
如果我手动执行计算(使用谷歌计算),我会得到以下结果:

0.72351421188
0.88278388278
第一次计算的结果非常大(~0.08关),而第二次计算的结果非常低(~0.01关)

对于第一个结果为何如此之大,是否有合理的解释?或者使用
BigDecimal

注意

System.out.println(2.8/3.87);
实际返回正确的结果(0.7235142118863048)


还请注意,
String.format
stuff仅用于检查结果是否被它更改,事实并非如此
BigDecimal.valueOf(2.8).divide(BigDecimal.valueOf(3.87),2).doubleValue()
产生完全相同的结果。

问题在于,您使用了错误的
BigDecimal\divide
四舍五入到2位小数。以下是
BigDecimal#divide
方法的可用参数:

  • 除法(大十进制除数)
  • divide(大十进制除数,整数取整模式)
  • divide(大十进制除数,MathContext mc)
  • divide(大十进制除数,舍入模式舍入模式)
  • divide(大十进制除数、整数刻度、整数取整模式)
  • divide(大十进制除数,整数刻度,舍入模式舍入模式)
由于您使用的是带有
BigDecimal
int
参数的
divide
,因此它使用
divide(BigDecimal divisior,int roundingMode)
,其中
2
是舍入模式,而不是刻度。在这种情况下,
2
实际上是
圆形天花板
,比例未指定

相反,您必须使用
除法(BigDecimal除数,int scale,int roundingMode)
除法(BigDecimal除数,int scale,roundingMode roundingMode)
。因此,请将您的通话改为:

System.out.println(BigDecimal.valueOf(2.8).divide(BigDecimal.valueOf(3.87), 2, 
  BigDecimal.ROUND_HALF_UP));
System.out.println(BigDecimal.valueOf(2.41).divide(BigDecimal.valueOf(2.73), 2, 
  BigDecimal.ROUND_HALF_UP));
(可以随意使用取整模式,而不是
向上取整或向上取整

我不确定默认情况下它使用的比例,但是您用
2
指定的
圆形上限导致了计算中的问题


对于上述注释,有三种可能的方法可以使用指定的值创建
BigDecimal

  • BigDecimal.valueOf(2.8)
  • 新的BigDecimal(2.8)
  • new BigDecimal(“2.8”)
新的BigDecimal(2.8)
仍然会给出浮点错误,因此我建议您使用
BigDecimal.valueOf(2.8)
或者字符串构造函数
new BigDecimal(“2.8”)

但是,这与取整问题有点无关,因为无论您使用的是
BigDecimal
初始化,使用正确的
divide
方法都会给出正确的结果:


试试这个
System.out.println(String.format(“%.2f”,新的BigDecimal(2.8).除法(BigDecimal.valueOf(3.87),2.doubleValue())好的,这给了我正确的结果。你能解释一下为什么会有不同吗?为了确保传递给
BigDecimal
的不是
double
值,请尝试基于
字符串的实例化。@Lothar我无法控制
BigDecimal
的实例化,我只能使用它们。虽然
String.format(“%.2f”,BigDecimal.valueOf(2.8))打印精确值:2。80@XtremeBaumer不确定,查看了代码,确实有很多字段可能会受到此
静态
使用的影响,因此基本上问题是初始
大十进制
的比例未指定/太低
2.80
会产生
1
的量表,而在这种情况下它应该是
2
2.81
的结果为
2
,这是正确的,因为我们有2decimals@XtremeBaumer啊,关于默认缩放,你确实是对的;对于
2.8
3.87
的量表
1
,结果也是量表
1
;这将导致
0.8
与您的
圆形天花板相结合。主要问题是使用了错误的
divide
方法,但您确实正确地认为刻度是基于您使用的双精度值。如果使用
new BigDecimal(“2.80”)
而不是
BigDecimal.valueOf(2.8)
,结果也确实会有所不同。这只是因为
Double.toString(2.80)
返回
2.8
而不是
2.80
。如果可以正确应用缩放,则所选的
divide
方法将很好地工作,而本Q中的情况并非如此/A@XtremeBaumer你知道双字面值2.80和2.8是完全相同的,相同的,无法区分的,不是吗?编译器没有发现两者之间的任何区别。顺便说一下,我强烈建议使用
RoundingMode
enum而不是int常量。例如,在
BigDecimal.valueOf(x).divide(BigDecimal.valueOf(y),2,BigDecimal.ROUND\u HALF\u UP)
中,如果错误地交换了小数位数参数(例如2)和舍入模式参数,则在运行时会得到错误的结果。但如果使用RoundingMode枚举,则会出现编译错误。将int圆模式视为遗留(预java 1.6)。