【问题标题】:Correctly divide a double to be able to get back the correct amount正确除以双倍才能取回正确的金额
【发布时间】:2015-08-25 05:47:18
【问题描述】:

我有一个表示金额的方法的输入,即 X 件商品的总价格。
这可以是多种货币下的金额,并由 double 表示。
问题:我想将此金额分解为 X 件商品的价格。考虑到金额本身是double,我担心的是,如果我只是这样做:amount/X 我可能会得到一个number*X 不会给我amount 的数字,这正是由于例如四舍五入。我怎样才能正确地做到这一点?
注意:请帮我理所当然地认为我不能将 amount 更改为 double 以外的其他东西

【问题讨论】:

  • 您可以将双精度转换为 BigDecimal 并使用它进行计算。
  • @Kayaman:你认为这会有什么帮助?
  • @tmyklebu 好吧,它可以让你进行精确的计算。但是,如果其他部分期望 double,这仅意味着代码一开始就被破坏了。如果您正在处理金钱,您需要使用BigDecimal,直到您为用户打印结果(他们很​​可能不希望看到诸如10.000000000256 之类的值)。
  • @Kayaman:BigDecimal 不能准确地表示有理数。它仍然是一种浮点格式,并且鸽笼原理仍然适用——仍然有一个 BigDecimal f 使得 (1/f)*f != 1。(“如果你在处理金钱,你需要工作顺便说一句,使用 BigDecimal" 是完全错误的;许多金融应用程序都可以使用双精度数。)

标签: java types floating-point double rounding


【解决方案1】:

正如您已经提到的,这并非在所有情况下都是可能的。通常,当总价格确实是单件价格和数量的乘积时,得到的双精度将有足够的精度来进行此计算。但通常你必须存储这两个价格。

我实现了一个完整的 ERP,我们还有一个功能,用户可以直接指定总行金额,也许在与客户讨论后四舍五入。这使得以 1000 美元的价格出售 7 件商品成为可能。那么就没有办法为单个项目创建一个精确的价格表示。因此,我们还存储了四舍五入的商品价格(4 位数后)。

回到您的答案:根据具体情况,您稍后将需要总金额和单项金额,因此只需存储两者。

【讨论】:

  • 我没有问题保留两者。但是以 4 位数显示价格(因为只有这样我们才能乘以返回正确的金额),这对您的客户来说是个问题吗?我不记得见过小数点后 4 位的价格
  • 至少在我的国家,燃料价格以更高的精度给出,有用。一升成本例如1,299 欧元,乘以装载的燃料量后四舍五入。这显然是一个骗局,因为 1,30 是有效价格。尽管如此,单项价格总是可以比订单项金额具有更高的精度。
【解决方案2】:

你可以使用BigDecimal类:

    double a=456.6556756;
    BigDecimal amount=BigDecimal.valueOf(a);
    BigDecimal priceOfEachItem=amount.divide(BigDecimal.valueOf(8));   
    BigDecimal amountAgain=priceOfEachItem.multiply(BigDecimal.valueOf(8));
    System.out.println(amountAgain.doubleValue());

Output:456.6556756

如您所见,您可以获得确切的原始金额

【讨论】:

  • 是的,但是如果我将结果转换回双精度以传递给其他函数会发生什么?
  • @Jim 你完全没有问题。您可以将双精度值检索为: double value=amountAgain.doubleValue();并将其传递给其他函数
  • 但是如果你想要一个完整的精度精度那么你也必须在其他函数中使用 BigDecimal..
  • 这个除法只适用于精确的倍数。如果单价不是一分钱怎么办。顺便说一句,您不能拥有$456.6556756
  • 据我所知,该问题要求提供确切的倍数。 :) 我只想证明 BigDecimal 完全准确地保留了精度。没有考虑货币方面:)
【解决方案3】:

您可以通过将单个项目的价格作为不会四舍五入的值的双值来做到这一点(是的,您可以限制小数点后的位数)。

double totalPrice = 500.33;
int totalItems = 6;
double singleItemPrice = totalPrice/totalItems;
System.out.println("Single Item Price = " + singleItemPrice);
System.out.println("Total Price = " + (singleItemPrice * totalItems));
DecimalFormat df = new DecimalFormat("#.##"); 
Double formatted = Double.parseDouble(df.format(singleItemPrice * totalItems)); 
System.out.println("Formatted Value = "formatted);

输出:

单件价格 = 83.38833333333334
总价格 = 500.33000000000004
格式化值 = 500.33

或者使用其他人提到的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

希望这就是你要找的。​​p>

【讨论】:

  • 是的,但是如果我将结果转换回双精度以传递给其他函数会发生什么?
【解决方案4】:

您可以使用 Math.round 进行舍入。您遇到的问题是您不知道您需要什么舍入amount/X。不管你用double还是BigDecimal,这都是个问题

你可以这样做

public static double round2(double d) {
    if (d < -Long.MAX_VALUE / 100 || d > Long.MAX_VALUE / 100)
        return d;
    return Math.round(d * 100) / 100.0;
}

public static void main(String[] args) throws IOException {
    double amount = 1000.00;
    int units = 9;
    System.out.println("amount: " + amount );
    System.out.println("units: " + units);
    double perUnit = amount / units;
    double amount2 = round2(perUnit * units);
    System.out.println("perUnit: " + perUnit);
    System.out.println("amount2: " + amount2);
}

打印

amount: 1000.0
units: 9
perUnit: 111.11111111111111
amount2: 1000.0

【讨论】:

    猜你喜欢
    • 2018-08-15
    • 1970-01-01
    • 2021-12-13
    • 2011-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多