复合主键 复合外键_复合双重错误

复合主键 复合外键

总览

在上一篇文章中,我概述了为什么BigDecimal大部分时间都不是答案。 尽管可以构造double会产生错误的情况,但在BigDecimal遇到错误的情况下构造情况也一样容易。

BigDecimal更容易正确,但更容易出错。

轶事证据表明,初级开发人员在正确使用BigDecimal时并不会比在四舍五入时获得双倍麻烦。 但是,我对此表示怀疑,因为在BigDecimal中,错误也容易被忽略。

让我们以这个例子为例,其中double产生错误的答案。

double d = 1.00;
d /= 49;
d *= 49 * 2;
System.out.println("d=" + d);

BigDecimal bd = BigDecimal.ONE;
bd = bd .divide(BigDecimal.valueOf(49), 2, BigDecimal.ROUND_HALF_UP);
bd = bd.multiply(BigDecimal.valueOf(49*2));
System.out.println("bd=" + bd);

版画

d=1.9999999999999998
bd=1.96

在这种情况下,double看起来是错误的,它需要四舍五入,这将给出正确的答案2.0。 但是BigDecimal看起来正确,但这不是由于表示错误。 我们可以更改除法以使用更高的精度,但是尽管可以控制该误差有多小,但总会出现表示错误。

您必须确保数字是实数并使用四舍五入。

即使使用BigDecimal,也必须使用适当的舍入。 假设您有一笔$ 1,000,000的贷款,并且每天要申请0.0005%的利息。 该帐户只能有一个整数,因此需要四舍五入才能使这笔钱成为实际金额。 如果不这样做,需要多长时间才能产生1美分的差异?

double interest = 0.0005;
BigDecimal interestBD = BigDecimal.valueOf(interest);

double amount = 1e6;
BigDecimal amountBD = BigDecimal.valueOf(amount);
BigDecimal amountBD2 = BigDecimal.valueOf(amount);

long i = 0;
do {
    System.out.printf("%,d: BigDecimal: $%s, BigDecimal: $%s%n", i, amountBD, amountBD2);
     i++;
    amountBD = amountBD.add(amountBD.multiply(interestBD)
                       .setScale(2, BigDecimal.ROUND_HALF_UP));
    amountBD2 = amountBD2.add(amountBD2.multiply(interestBD));

} while (amountBD2.subtract(amountBD).abs()
                 .compareTo(BigDecimal.valueOf(0.01)) < 0);
System.out.printf("After %,d iterations the error was 1 cent and you owe %s%n", i, amountBD);

最终打印

8: BigDecimal: $1004007.00, 
   BigDecimal: $1004007.00700437675043756250390625000000000000000
After 9 iterations the error was 1 cent and you owe 1004509.00

您可以四舍五入结果,但是即使您使用BigDecimal,这也掩盖了您实际损失一分钱的事实。

double最终出现表示错误

即使您使用适当的舍入,double也会给您不正确的结果。 它比上一个示例晚得多。

double interest = 0.0005;
BigDecimal interestBD = BigDecimal.valueOf(interest);
double amount = 1e6;
BigDecimal amountBD = BigDecimal.valueOf(amount);
long i = 0;
do {
    System.out.printf("%,d: double: $%.2f, BigDecimal: $%s%n", i, amount, amountBD);
    i++;
    amount = round2(amount + amount * interest);
    amountBD = amountBD.add(amountBD.multiply(interestBD)
                       .setScale(2, BigDecimal.ROUND_HALF_UP));
} while (BigDecimal.valueOf(amount).subtract(amountBD).abs()
                   .compareTo(BigDecimal.valueOf(0.01)) < 0);
System.out.printf("After %,d iterations the error was 1 cent and you owe %s%n", i, amountBD);

最终打印

22,473: double: $75636308370.01, BigDecimal: $75636308370.01
After 22,474 iterations the error was 1 cent and you owe 75674126524.20

从IT角度来看,我们有一个错误的误差,从业务角度来看,我们有一个客户超过9年没有还款,并且还欠银行756亿美元,足以拖垮银行。 如果只有IT人员使用过BigDecimal !?

结论

我的最终建议是,您应该使用自己喜欢的东西,不要忘记取整,不要使用实数,而不要使用任何数学运算法则,例如,我可以赚一分钱,还是可以交易几分之一的份额。 不要忘记业务视角。 您可能会发现BigDecimal对您的公司,项目或团队更有意义。

不要以为BigDecimal是唯一的方法,不要以为双面问题也不适用于BigDecimal。 BigDecimal并不是最佳实践编码的门票,因为自满是引入错误的肯定方法。

翻译自: https://www.javacodegeeks.com/2014/07/compounding-double-error.html

复合主键 复合外键

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值