题
这个问题已经有一个答案在这里:
- 保持精密的双在Java 20答案
似乎是减触发一些种问题和得到的数值是错误的。
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。数学。BigDecimal.阅读 需要BigDecimal 由约翰*Zukowski的更多信息。
给你的例子,最后一行,将作为下列用BigDecimal.
import java.math.BigDecimal;
BigDecimal premium = BigDecimal.valueOf("1586.6");
BigDecimal netToCompany = BigDecimal.valueOf("708.75");
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);
这种结果在下面的产出。
877.85 = 1586.6 - 708.75
其他提示
如先前的答复指出,这是一个后果的做浮点运算。
作为前海报的建议,当你们在做数字计算,使用 java.math.BigDecimal
.
然而,有一个陷阱使用 BigDecimal
.当你把从双值 BigDecimal
, 你有选择的使用一个新的 BigDecimal(double)
构造或 BigDecimal.valueOf(double)
静态工厂的方法。使用静态工厂的方法。
双构造将整个精密的 double
来一个 BigDecimal
而静态工厂有效地将其转换为 String
, 然后转到一个 BigDecimal
.
这将成为有关当你遇到这些微妙的四舍五入的错误。一些可能显示如.585,但国内其价值是'0.58499999999999996447286321199499070644378662109375'.如果你使用了 BigDecimal
构造你会得到的数字,是不是等于0.585,而静态的方法会给你一个值等于0.585.
double value = 0.585; System.out.println(new BigDecimal(value)); System.out.println(BigDecimal.valueOf(value));
在我的系统
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);
我认为,这些选项优于使用增加一倍。在webapp号码开始作为串无论如何。
任何时间你做的计算加倍,这有可能发生。这个代码会给你877.85:
双答=数学。圆(dCommission*100000)/100000.0;
保存号码的美分,而不是美元,而只是做的格式为美元时产出。这样你可以使用一个整数而不遭受的精确度问题。
看到的答复 这个问题.基本上你看到的是一个自然的结果的使用浮点运算。
你可以挑选一些任意的精确度(显着位数的投入?) 以及你的结果,如果你觉得舒适这样做。
这是一个有趣的问题。
背后的想法Timons答复是你指定一个小量其表示最精密的法律双重即可。如果你知道在你的,你将永远不再需要精密的下0.00000001然后什么他建议足以获得更精确的结果非常接近真理。有用的应用中,他们知道他们的最大的精确度(对于在实例金融货币精度等)
然而根本的问题,试图圆它是当你鸿沟的一个因素来重新调整了它,你实际上介绍的另一种可能性精密的问题。任何操纵的一倍,可以介绍的不精确性问题有不同的频率。尤其是如果你想在一个非常重要的数字(所以你的操作数的 < 0)例如,如果运行以下与Timons码:
System.out.println(round((1515476.0) * 0.00001) / 0.00001);
将会导致 1499999.9999999998
在这里的目标是在该单位的500000(i。电子我们希望1500000)
事实上,只有这样,才能完全相信你已经淘汰的不是要通过一个BigDecimal规模关闭。例如
System.out.println(BigDecimal.valueOf(1515476.0).setScale(-5, RoundingMode.HALF_UP).doubleValue());
混合使用的小量的战略和BigDecimal战略会给你很好的控制你的精度。想法是epsilon得到你非常靠近然后BigDecimal将消除任何不准确所引起的重新调整之后。虽然使用BigDecimal将减少预期绩效的应用程序。
它已经向我指出,最后的步骤的使用BigDecimal要重新调整它并不总是必要的,对于某些用途的案件时,你可以确定,有的没有输入值,最后划分可以重新提出一个错误。目前我不知道如何正确地确定这个所以如果有人知道如何然后我会很高兴听到它。
迄今为止的最高和最有效的方式来这样做,在Java:
double newNum = Math.floor(num * 100 + 0.5) / 100;
更好的使用 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
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
这是很简单的。
使用%.2f操作人员对于输出。问题解决了!
例如:
int a = 877.8499999999999;
System.out.printf("Formatted Output is: %.2f", a);
上述代码的结果的打印输出:877.85
该%.2f操作者的定义,只有两位小数位的应用。