【问题标题】:Incorrect rounding of currency double values [duplicate]货币双值的不正确舍入[重复]
【发布时间】:2014-06-01 23:52:55
【问题描述】:

我的应用程序中的一些计算存在问题。 它基本上是某种购物清单应用程序。 用户输入数量和价格。 该列表中可能有多达 150 种产品,但通常平均为 20 种左右。

我正在使用JTextFields,从中取出Strings 并将它们放入Arrays。然后我解析Double 并进行计算。 问题是我有时会从我的数据库中的用户那里得到一个总数,当我验证它错误时,例如(我能找到的最短列表有错误):

10 * 27.10
10 * 27.81
Total: 549.08

显然,总数不是549.08,而是549.1

现在,我知道我的计算方法不是最好的,几个月前我对 Java 知之甚少,我就这样放弃了,因为它可以完成工作,我没有计算这是非常复杂的。所以我没有转向 BigDecimal,因为我经常做这个计算,而且从我读过的内容来看,它比 double 和 float 要慢得多。

无论如何,这是我的方法:

Double totaltest=0;
for (int j = 0; j < allcant.size(); j++) {

   cant[j] = allcant.get(j).getText().toString();
   pret[j] = allpret.get(j).getText().toString();
   Double temp = Double.parseDouble(cant[j]) * Double.parseDouble(pret[j]);
   totaltest = totaltest + temp;
}

问题能从这里出来吗?或者我应该在我的代码中查看其他地方吗? 无论如何,我很可能会改变我进行计算的方式,但我想知道最好的方法是什么,以及我是否在这里犯了错误。 我现在知道浮点和其他东西,但奇怪的是,当我尝试复制它时它给出了正确的总数。

【问题讨论】:

  • 使用 BigDecimal 而不是 Double。在您的小型应用程序中,您不会发现 BigDecimal 和 Double 的性能差异;)
  • 扩展above comment:钱通常最好存储在整数值中(即总美分/便士/任何值)。这样可以避免任何这种讨厌的浮点错误。
  • 我已经进行了一些研究,并且了解了你们告诉我的所有事情(使用 BigDecimal 或 long 与美分、浮点数和其他一些东西),但我使用的方法会导致这样的错误吗?为什么我不能复制它?这使我相信问题出在其他地方。我不想创建一个新方法来计算只是发现问题出在其他地方。感谢您的时间!
  • 在四舍五入后你很可能会得到正确的答案,但这不是一个好习惯。此外,不同的实现做不同的事情。例如,在浏览器中尝试 javascript:alert(2.0-1.1)。即使是像这样的简单计算也会让您在 javascript 中得到略微偏离的结果。
  • 是的,您的代码有问题。 10 * 27.10 + 10 * 27.81 = 549.10 在 java 中。一般来说,float 和 double 会关闭,但是会关闭非常非常小的数字,例如 .0000000000001。

标签: java double bigdecimal


【解决方案1】:

Float 和 Double 在设计上并不精确。你不应该用这些来代表货币。而是使用 int 或 long 并以美分表示您的金额。例如 1.00 在内部是 100。

【讨论】:

  • 感谢您的回答!我知道这一点,但仍然会导致与如此短的计算有如此大的差异吗?我认为最大的问题不在这里,而是在我的代码中的其他地方,这就是我要问的原因。再次感谢!
【解决方案2】:

为了获得正确的精度,请使用 BigDecimal。

【讨论】:

  • 再次,我知道这一点!不要吝啬,谢谢你的时间,但我已经写了几次关于知道这一点的文章!我想知道的是,如果使用 double 会导致如此大的差异。我的意思是,对于 2 种产品,它的误差为 0.02。这不是很多吗,即使double 也是如此?
【解决方案3】:

我试图重现你的算法,结果是 549.09999999999990905052982270717620849609375。如果四舍五入到小数点后两位显示,它将是 549.10,所以我认为您的问题不是浮点精度,尽管我同意 BigDecimal 将是此应用程序的更好选择。

(我只在我的程序中使用BigDecimal 来获得结果的准确打印输出。)

这是我的程序。我建议找出你的算术有什么不同,并进行调查。

import java.math.BigDecimal;

public class Test {
  public static void main(String[] args) {
    String[] cant = {"10", "10"};
    String[] pret = {"27.10", "27.81"};
    double totaltest=0;
    for (int j = 0; j < cant.length; j++) {
       Double temp = Double.parseDouble(cant[j]) * Double.parseDouble(pret[j]);
       totaltest = totaltest + temp;
    }
    System.out.println(new BigDecimal(totaltest));
  }
}

【讨论】:

    【解决方案4】:

    我前段时间找到了这个问题的解决方案,忘记回来了。这是我的一个错误。有点。 用户应用程序在某处进行微积分,将其发送到数据库,然后我提取它并在另一个应用程序中重做微积分(使用相同的微积分方法)。

    问题是当我创建数据库时,“客户”告诉我价格永远不会超过 2 位小数,所以我将价格字段声明为 Decimal(10,2),因为我询问并确定了没关系。 例如,他们将价格设置为 0.305。用户应用程序将进行微积分,例如 24*0.305=7.32 并在数据库中记录为 0.30,因为它只能接受 2 位小数,然后,当我从数据库中获取数据并在我的其他应用程序中重做时,当然,24*0.30=7.2

    很抱歉让你走错了路。 现在一切都好。我了解到您永远不应该信任用户/客户,并且总是仔细检查我猜,呵呵。 祝你好运!

    【讨论】:

      猜你喜欢
      • 2020-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-26
      • 2010-11-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多