CURRENCY - 仅当它有超过 2 个小数位时才舍入双精度值

CURRENCY - Round double value ONLY if it has more than 2 decimal places

我正在尝试拆分账单,并且需要计算如果账单被平均拆分的话每个人会欠多少钱。我知道一笔金额将与其余金额不同,以弥补损失的美分。

假设 3 个人尝试分摊 200 的账单。200 / 3 是 66.6666666667。我打算做的是向前两个人收取 66.67 的费用,最后一个幸运地支付了 66.66 的账单。

目前,我有这个:

private String calculateAmountToPay(String noOfParticipants, String owed) {
    double amountOwed = Double.parseDouble(owed);
    int noOfMembers = Integer.parseInt(noOfParticipants);
    DecimalFormat amountFormat = new DecimalFormat("#.##");
    amountFormat.setRoundingMode(RoundingMode.CEILING);
    return amountFormat.format((amountOwed/(double)noOfMembers) / 100);
  }

但这总是 return66.67。有没有一种方法可以让它只在有大于 2 位小数的数字时四舍五入,如果不是,它保持在 66.66,例如?

也许我处理这个问题的方式不对。我知道处理货币可能很挑剔。

您可以在半舍入模式下使用 BigDecimal:

    private String calculateAmountToPay(String noOfParticipants, String owed) {
        double amountOwed = Double.parseDouble(owed);
        int noOfMembers = Integer.parseInt(noOfParticipants);
        BigDecimal amount= new BigDecimal((amountOwed / (double) noOfMembers) / 100);
        return amount.setScale(2, RoundingMode.HALF_UP).toString();
    }

您可以使用基本原语进行所有计算,将所有内容转换为分(精度为 2 位小数),并将剩余的分分给一部分成员,无需额外 sdks/math 使其过于复杂操纵。以下是使用这些建议完全解决此问题的工作示例:

public class AmountDivider {
    private int totalMembers, luckies, unluckies;
    private double totalAmount, amountPerLucky, amountPerUnlucky;

    public AmountDivider(int numMembers, double amountOwed) {
        totalMembers = numMembers;
        totalAmount = amountOwed;

        double centsOwed = amountOwed * 100;
        int centsPerMember = (int)(centsOwed / totalMembers);
        int centsLeft = (int)centsOwed - centsPerMember * totalMembers;

        luckies = totalMembers - centsLeft;
        amountPerLucky = centsPerMember / 100.0;
        unluckies = centsLeft;
        amountPerUnlucky = (centsPerMember + 1) / 100.0;
    }

    public String toString() {
        String luckiesStr = String.format("%d lucky persons will pay %.2f", luckies, amountPerLucky);
        String unluckiesStr = String.format("%d unlucky persons will pay %.2f", unluckies, amountPerUnlucky);
        return String.format("For amount %f divided among %d: \n%s\n%s\n", 
                                totalAmount, totalMembers, luckiesStr, unluckiesStr);
    }

    public static void main(String[] args) {
        System.out.println(new AmountDivider(3, 200));
        System.out.println(new AmountDivider(17, 365.99));
    }
}

Complete code on GitHub

希望对您有所帮助。

在考虑算术之前,您需要知道 double 不是 适合与货币一起使用的数据类型,因为它 不精确。因此,停止使用浮点类型(例如 double)作为数量 dollars 的数据类型,并开始使用精确类型(例如 long)作为cents.

数量的数据类型

然后进行计算的步骤是立即将所有内容四舍五入转换为美分:

double amountOwed = ...;
int noOfMembers = ...;
long centsOwed = Math.round(amountOwed * 100);
long portionCents = Math.round(amountOwed * 100 / noOfMembers);
long errorCents = portionCents * noOfMembers - centsOwed;

这是处理错误的一种方法:

long lastPortionCents = portionCents - errorCents;

但误差可能超过 1 美分,因此更好的解决方案是通过从第一个(或最后一个,或随机选择)errorCents 位食客。

剩下的解决方案就是关于撕裂上面的内容,我留给 reader。

附带说明一下,使用美分是银行传输金额的方式(至少对于 EFTPOS 而言)。


关于基本软件设计,我会创建一个单独的方法,它接受整数美分和人数作为其参数,returns 一个 "split" 数量的数组。这样做不仅会让你的代码更容易阅读,而且会划分算术运算,从而使许多简单的测试更容易编写,所以你可以知道你已经涵盖了你能想到的所有边缘情况。