Как округлить до ближайшего .05 в Java? - PullRequest
0 голосов
/ 01 февраля 2020

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

public class OrderMethod {

    static Map<String, BigDecimal> newOrder;

    static final BigDecimal BASICSALES = new BigDecimal(.10);
    static final BigDecimal IMPORTSALES = new BigDecimal(.05);
    static final BigDecimal BASICANDIMPORTSALES = new BigDecimal(.15);
    static final BigDecimal Rounding = new BigDecimal(0.05);
    static BigDecimal total = new BigDecimal(0);

    public void convertOrders() throws IOException {

        ReadFile readfile = new ReadFile();
        ArrayList<String> order = readfile.getFile();
        newOrder = new LinkedHashMap<String, BigDecimal>();

        for(String i : order) {
            String[] splitOrder = i.split("at\\ ");
            newOrder.put(splitOrder[0], new BigDecimal(splitOrder[1]));
        }
    }

    public void calculate() {
        newOrder.forEach((k, v) -> {

            if(!((k.contains("chocolate")) || k.contains("book") || k.contains("pill")) && k.contains("import")){
                v = v.multiply(BASICANDIMPORTSALES).add(v);
                v = v.round(new MathContext(4));

                System.out.println("Both " + k + " tax: $" + v);
                newOrder.put(k, v);
            }
            else {
                if(!(k.contains("chocolate")|| k.contains("book") || k.contains("pill"))) {

                    v = v.multiply(BASICSALES).add(v);
                    v = v.round(new MathContext(4));

                    System.out.println("Basic " + k + " tax: $" + v);
                newOrder.put(k, v);
            }
            if(k.contains("import")) {
                v = v.multiply(IMPORTSALES).add(v);
                v = v.round(new MathContext(4));

                System.out.println("Import " + k + " tax: $" + v);

                newOrder.put(k, v);
            }
            }

            total = total.add(v);

        });
    }

    public void print() {
        newOrder.forEach((k, v) -> {
            System.out.println(k + ": $" + v);
        });
        System.out.println("Total: $" + total);
    }

    public static void main(String[] args) throws IOException {
        OrderMethod om = new OrderMethod();
        om.convertOrders();

        om.calculate();
        om.print();
    }
}

Итак, у меня есть программа, которая читает текстовый файл, содержащий входные данные, такие как

Input 1:
1 book at 12.49
1 music CD at 14.99
1 chocolate bar at 0.85

Input 2:
1 imported box of chocolates at 10.00
1 imported bottle of perfume at 47.50

Input 3:
1 imported bottle of perfume at 27.99
1 bottle of perfume at 18.99
1 packet of headache pills at 9.75
1 box of imported chocolates at 11.25

Вот список решений: Вывод:

Output 1:
1 book : 12.49
1 music CD: 16.49
1 chocolate bar: 0.85
Sales Taxes: 1.50
Total: 29.83

Output 2:
1 imported box of chocolates: 10.50
1 imported bottle of perfume: 54.65
Sales Taxes: 7.65
Total: 65.15

Output 3:
1 imported bottle of perfume: 32.19
1 bottle of perfume: 20.89
1 packet of headache pills: 9.75
1 imported box of chocolates: 11.85
Sales Taxes: 6.70
Total: 74.68

У меня небольшая проблема с моими вычислениями. Независимо от того, что мои ответы на входы 2 и 3, кажется, несколько разрядов. Я получаю $ 65,12 за ввод 2 и $ 74,64 за ввод 3. Мне нужна помощь в том, как лучше округлить мои ответы, а также что вы думаете о моем коде.

Ответы [ 2 ]

0 голосов
/ 01 февраля 2020

Чтобы округлить до 5 центов, вы можете:

private static final BigDecimal TWENTY = new BigDecimal(20);

public BigDecimal roundUp5(BigDecimal value)
{
    // The final setScale(2) is just to add the ".00" when you display
    return value.multiply(TWENTY).setScale(0, BigDecimal.ROUND_CEILING).divide(TWENTY).setScale(2);
}

// You don't need this one as you're using BigDecimal;  it's just for completeness!
public double roundUp5(double value)
{
    return Math.ceil(value * 20) / 20;
}

С радостью изложить свои мысли в виде обзора кода.

Во-первых, почему раздельные convertOrders и calculate методы? Создание Map в convertOrders, а затем итерация по нему и повторная вставка в calculate является грязным и запутанным.

Гораздо лучше было бы иметь convertOrders call calculate как часть его итерация при чтении каждой строки и, следовательно, правильное создание каждой записи Map.

У вас есть несколько повторяющихся выражений, которые я бы преобразовал в методы для удобочитаемости и перспективы будущих бизнес-изменений, поэтому:

public boolean isBasic(String itemName) {
    String name = itemName.toLowerCase();
    return !( name.contains("chocolate") || name.contains("book") || name.contains("pill") );
}


public boolean isImport(String itemName) {
    String name = itemName.toLowerCase();
    return name.contains("import");
}

В ваших if выражениях в calculate в настоящее время выполняются как:

if (isBasic(k) && isImport(k)) {
    // Basic and Import
} else {
   if (isBasic(k)) {
   }
   if (isImport(k)) {
   }
}

Этот поток немного сбивает с толку, и было бы неудобно проводить рефакторинг, если в будущем вам понадобится также обрабатывать неосновные c и не импортированные элементы.

Существует некоторый общий код между всеми этими if блоками, поэтому я бы переместил его после if - что оставило бы оператор log и установка коэффициента умножения единственные строки, оставшиеся в блоках if.

Пока мы говорим о коэффициентах умножения, у вас есть те, которые установлены на значения меньше чем 1 (например, 0,15) ... бит, то вы добавляете v к результату! Было бы намного лучше, чтобы коэффициенты были> 1, и поэтому избавились бы от add(v).

Кстати, также предпочтительно инициализировать BigDecimal в String s, а не в буквальное значение float или double, так как они могут получить ошибки округления (например, 0,15, поскольку число с плавающей запятой может фактически быть 0,1499999999), тогда как BigDecimal, созданное из String, является точным.

Предполагая, что вы счастливы покинуть регистрация окончательной суммы до конца, что приводит нас к чему-то вроде:

static final BigDecimal BASICSALES = new BigDecimal("1.10");
static final BigDecimal IMPORTSALES = new BigDecimal("1.05");
static final BigDecimal BASICANDIMPORTSALES = new BigDecimal("1.15");


   for(String orderLine : order) {
        String[] splitOrder = orderLine.split("at\\ ");
        String name = splitOrder[0];
        BigDecimal value = new BigDecimal(splitOrder[1]));

       BigDecimal taxRate;

       if(isBasic(name)) {
           if (isImport(name)) {
               taxRate = BASICANDIMPORTSALES;
               System.out.println("Both " + k);
           } else {
               taxRate = BASICSALES;
               System.out.println("Basic " + k);
           }
        } else {
           if (isImport(name)) {
               taxRate = IMPORTSALES;
               System.out.println("Import " + k);
           } else {
               taxRate = BigDecimal.ONE;
               System.out.println("Neither " + k);
           }
        }

        BigDecimal amountWithTax = amount.multiply(taxRate).round(new MathContext(4));
        System.out.println(name + " tax: $" + amountWithTax);
        newOrder.put(name, amountWithTax);
   }
0 голосов
/ 01 февраля 2020

Я не понимаю ваш код, но я должен был сделать это на экзамене.

Я использую оператор%, как это, и он работает.

if ((number * 100) % 10> = 5)

...