ВАЛЮТА - округлять двойное значение ТОЛЬКО, если оно имеет более 2 десятичных знаков - PullRequest
2 голосов
/ 15 мая 2019

Я пытаюсь разделить счет, и мне нужно рассчитать, сколько должен был бы заплатить каждый человек, если бы счет был разделен на четные суммы. Я знаю, что одна сумма будет отличаться от остальных, чтобы учесть потерянные центы.

Предположим, что 3 человека пытаются разделить счет на 200. 200/3 - 66,6666666667. То, что я планировал сделать, это было выставить 2 первым людям 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);
  }

Но это всегда вернет 66.67. Есть ли способ, которым я могу заставить его округлять только, если число больше 2 десятичных разрядов, если нет, например, оно остается на 66,66?

Может быть, я подхожу к этому неправильно. Я знаю, что валюта может быть привередливой.

Ответы [ 3 ]

2 голосов
/ 15 мая 2019

Прежде чем даже думать об арифметике, вы должны знать, что double это , а не подходящий тип данных для использования с валютой, потому что это неточно .Поэтому прекратите использовать тип с плавающей запятой (например, double) в качестве типа данных для количества долларов и начните использовать точный тип (например, long) в качестве типа данных для количества центов .

Шаги, необходимые для вычисления, должны состоять в немедленном преобразовании всего с округлением в центы:

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 цент, поэтому лучшим решением будет равномерное распределение ошибки путем вычитания (или добавления, если ошибка отрицательная) 1 цента от первого(или последний, или случайным образом выбранный) errorCents закусочных.

Остальная часть решения состоит в том, чтобы разорвать вышеупомянутое, которое я оставляю читателю.

В качестве примечания, используяценты - это то, как банки передают суммы (по крайней мере, для EFTPOS).


Что касается базового дизайна программного обеспечения, я бы создал отдельный метод, который принимает целочисленные центы и количество людей в качестве параметров и возвращает массивиз «разделенных» сумм.Это не только облегчит чтение вашего кода, но и разделит арифметическую операцию и, таким образом, позволит с лёгкостью писать множество простых тестов, чтобы вы знали, что у вас есть все крайние случаи, о которых вы можете подумать.

0 голосов
/ 15 мая 2019

Вы можете просто выполнить все вычисления с базовыми примитивами, преобразовав все в центы (с точностью до 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));
    }
}

Надеюсь, это поможет.

0 голосов
/ 15 мая 2019

Вы можете использовать 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();
    }
...