如何解决Java舍入双重问题

似乎减法正在引发某种问题,结果是错误的。

double tempCommission = targetPremium.doubleValue()*rate.doubleValue()/100d; 

78.75 = 787.5 * 10.0 / 100d

 double netToCompany = targetPremium.doubleValue() - tempCommission; 

708.75 = 787.5-78.75

 double dCommission = request.getPremium().doubleValue() - netToCompany; 

877.8499999999999 = 1586.6 – 708.75

由此产生的预期值将是877.85。

应该做些什么来确保正确的计算?

为了控制浮点运算的精度,你应该使用java.math.BigDecimal 。 阅读John Zukowski对BigDecimal的需求以获取更多信息。

给你的例子,最后一行将如下使用BigDecimal。

 import java.math.BigDecimal; BigDecimal premium = BigDecimal.valueOf(1586.6d); BigDecimal netToCompany = BigDecimal.valueOf(708.75d); BigDecimal commission = premium.subtract(netToCompany); System.out.println(commission + " = " + premium + " - " + netToCompany); 

这导致以下输出。

 877.85 = 1586.6 - 708.75 

正如前面的答案所述,这是做浮点运算的结果。

正如之前的海报提示的那样,在进行数字计算时,使用java.math.BigDecimal

但是,使用BigDecimal有一个问题。 当您从double值转换为BigDecimal ,您可以选择使用新的BigDecimal(double)构造函数或BigDecimal.valueOf(double)静态工厂方法。 使用静态工厂方法。

double构造函数将double的整个精度转换为BigDecimal而静态工厂将其转换为String ,然后将其转换为BigDecimal

当你遇到那些细微的舍入错误时,这变得相关。 一个数字可能显示为.585,但在内部它的值是'0.58499999999999996447286321199499070644378662109375'。 如果你使用BigDecimal构造函数,你会得到不等于0.585的数字,而静态方法会给你一个等于0.585的值。

双倍值= 0.585;
 System.out.println(new BigDecimal(value));
的System.out.println(BigDecimal.valueOf(值));

在我的系统给出

 0.58499999999999996447286321199499070644378662109375
 0.585

另一个例子:

 double d = 0; for (int i = 1; i <= 10; i++) { d += 0.1; } System.out.println(d); // prints 0.9999999999999999 not 1.0 

改用BigDecimal。

编辑:

此外,只是要指出这不是一个“Java”四舍五入问题。 其他语言表现出类似的(虽然不一定一致)的行为。 Java至少保证了这方面的一致行为。

我会修改上面的例子,如下所示:

 import java.math.BigDecimal; BigDecimal premium = new BigDecimal("1586.6"); BigDecimal netToCompany = new BigDecimal("708.75"); BigDecimal commission = premium.subtract(netToCompany); System.out.println(commission + " = " + premium + " - " + netToCompany); 

这样你可以避免使用字符串开始的陷阱。 另一种选择:

 import java.math.BigDecimal; BigDecimal premium = BigDecimal.valueOf(158660, 2); BigDecimal netToCompany = BigDecimal.valueOf(70875, 2); BigDecimal commission = premium.subtract(netToCompany); System.out.println(commission + " = " + premium + " - " + netToCompany); 

我认为这些选择比使用双打更好。 在webapps中,数字始终是字符串。

任何时候你做双打计算,都可能发生。 这段代码会给你877.85:

double answer = Math.round(dCommission * 100000)/ 100000.0;

保存美分的数量而不是美元,只要输出格式为美元即可。 这样你就可以使用一个不受精度问题影响的整数。

看到对这个问题的回应。 基本上你所看到的是使用浮点运算的自然结果。

你可以选择一些任意的精度(你的输入的有效数字?),并将结果舍入到它,如果你觉得这样做很舒服。

这是一个有趣的问题。

Timons答复背后的想法是指定一个代表法定双精度的最小精度的epsilon。 如果你在你的应用程序中知道你永远不需要低于0.00000001的精度,那么他所建议的就足以得到一个非常接近事实的更精确的结果。 在他们知道最高精度的应用中是有用的(例如用于货币精度的财务等)

然而,试图把它舍弃的根本问题是,当你除以一个因素来重新调整它,你实际上引入了另一个精度问题的可能性。 对双打的任何操纵都会引起频率不一的不精确问题。 特别是如果你试图在非常有意义的数字上进行舍入(所以你的操作数<0),例如,如果你用Timons码运行以下代码:

 System.out.println(round((1515476.0) * 0.00001) / 0.00001); 

将导致1499999.9999999998这里的目标是在500000单位(即我们想要150万)

事实上,要完全确定你已经消除了不准确的唯一方法就是通过一个BigDecimal来缩小比例。 例如

 System.out.println(BigDecimal.valueOf(1515476.0).setScale(-5, RoundingMode.HALF_UP).doubleValue()); 

使用epsilon策略和BigDecimal策略的组合可以很好地控制您的精度。 作为epsilon的想法让你非常接近,然后BigDecimal将消除之后重新缩放造成的任何不精确性。 尽管使用BigDecimal会降低应用程序的预期性能。

我已经指出,使用BigDecimal重新调整它的最后一步并不总是需要一些用例,当你可以确定没有输入值时,最终的分割可以重新引入一个错误。 目前我不知道该如何正确判断,所以如果有人知道我会很高兴听到这个消息。

更好的是使用JScience作为BigDecimal是相当有限的(例如,没有sqrt函数)

 double dCommission = 1586.6 - 708.75; System.out.println(dCommission); > 877.8499999999999 Real dCommissionR = Real.valueOf(1586.6 - 708.75); System.out.println(dCommissionR); > 877.850000000000 

到目前为止,在Java中这是最优雅和最有效的方法:

 double newNum = Math.floor(num * 100 + 0.5) / 100; 
 double rounded = Math.rint(toround * 100) / 100; 

虽然你不应该使用双打进行精确的计算,但是如果你总结了结果,下面的技巧帮助了我。

 public static int round(Double i) { return (int) Math.round(i + ((i > 0.0) ? 0.00000001 : -0.00000001)); } 

例:

  Double foo = 0.0; for (int i = 1; i <= 150; i++) { foo += 0.00010; } System.out.println(foo); System.out.println(Math.round(foo * 100.0) / 100.0); System.out.println(round(foo*100.0) / 100.0); 

打印:

 0.014999999999999965 0.01 0.02 

更多信息: http : //en.wikipedia.org/wiki/Double_precision

这很简单。

使用%.2f运算符输出。 问题解决了!

例如:

 int a = 877.8499999999999; System.out.printf("Formatted Output is: %.2f", a); 

上面的代码导致打印输出:877.85

%.2f运算符定义只能使用两个小数位。