计算得出的BigDecimal值返回正确的值,但在数字上添加了额外的零

问题描述

作为学校作业的一部分,我正在编写一个程序,该程序将帮助确定一包百吉饼的总价格,而我的getTotalPrice()方法所产生的值将推出正确的值,但是对于某些情况其中有两个零添加到最终值。我尝试使用setScale()方法将小数点四舍五入为两个空格,但被告知这不是理想的解决方案。我以前从未使用过BigDecimals,所以我相信我的代码中缺少某些东西,有什么建议吗?请让我知道是否需要更多信息,我不太确定要包含/排除的代码

// test that fails
@ParameterizedTest
@ArgumentsSource(BagArgumentsProvider_getTotalPrice.class)
void getTotalPrice_assertEquals_DiffBagelAmounts(Bag bag,BigDecimal cost) {
    assertEquals(cost,bag.getTotalPrice());
}

//some to the produced errors
expected 3.99 but was 3.9900
expected 1.99 but was 1.9950


/**
 * A bag of any quantity of a single type of bagel.
 *
 * @author Ellen Spertus
 *
 */
public class Bagel {
    private final Type type;
    private Category currentCategory;
    // Visible for Testing
    protected static final BigDecimal OLD_FASHIONED_PRICE = new BigDecimal("0.50");
    protected static final BigDecimal GOURMET_PRICE = new BigDecimal("0.70");
    protected static final BigDecimal DAY_OLD_PRICE = new BigDecimal("0.35");


    enum Type {
        PLAIN("plain",Category.OLD_FASHIONED),POPPY_SEED("poppy seed",SESAME_SEED("sesame seed",ONION(
                        "onion",EVERYTHING("everthing",ASIAGO("asiago",Category.GOURMET),BLUEBerry("blueBerry",CINNAMON_RAISIN(
                                                        "cinnamon raisin",SUN_DRIED_TOMATO(
                                                                "sun dried tomato",Category.GOURMET);

        private final String name;
        private final Category category;

        private Type(String name,Category category) {
            this.name = name;
            this.category = category;
        }// end constructor

        @Override
        public String toString() {
            return name;
        }// end toString

    }// end enum

    enum Category {
        OLD_FASHIONED(OLD_FASHIONED_PRICE),GOURMET(GOURMET_PRICE),DAY_OLD(DAY_OLD_PRICE);

        private final BigDecimal price;

        private Category(BigDecimal price) {
            this.price = price;
        }// end constructor

        public BigDecimal getPrice() {
            return price;
        }// end getPrice
    }// end enum

    /**
     * Constructs a bagel of the given type.
     *
     * @param the type
     */
    public Bagel(Type type) {
        this.type = type;
        this.currentCategory = type.category;
    }// end constructor

    /**
     * Gets the type of the bagel.
     *
     * @return the type of bagel
     */
    public Type getType() {
        return type;
    }// end getType

    /**
     * Gets the category of the bagel.
     *
     * @return the category
     */
    public Category getCategory() {
        return currentCategory;
    }// end get Category

    /**
     * Marks down this bagel.
     */
    public void markDown() {
        currentCategory = Category.DAY_OLD;
    }// end markDown
}// end class

 /**
 * A type of bagel.
 *
 * @author Ellen Spertus
 */
public class Bag {
    // We provide a Baker's Dozen: 13 bagels for the price of 12.
    private static final int BUY_ONE_GET_ONE_FREE_QUANTITY = 13;

    // If the Baker's Dozen discount doesn't apply,we give a percentage discount.
    private static final int BULK_disCOUNT_MINIMUM = 6;
    private static final BigDecimal BULK_disCOUNT_PERCENTAGE = new BigDecimal(".05");
    private static final BigDecimal BULK_disCOUNT_MULTIPLIER =
            new BigDecimal("1.00").subtract(BULK_disCOUNT_PERCENTAGE);

    private final Bagel bagel;
    private final int quantity;

    /**
     * Constructs a bag with the given quantity of the given type of bagel.
     *
     * @param bagel the type of bagel
     * @param quantity the quantity
     * @throws IllegalArguementException if the quantity passed is out of range from 1 to 13
     */
    public Bag(Bagel bagel,int quantity) {
        this.bagel = bagel;
        this.quantity = quantity;
        if (this.quantity > 13) {
            throw new IllegalArgumentException("Orders must be under 13 bagels");
        }

        if (this.quantity < 1) {
            throw new IllegalArgumentException("Not a valid order");
        }

    }// end constructor

    /**
     * Gets the bagel held in this bag.
     *
     * @return the bagel
     */
    public Bagel getBagel() {
        return bagel;
    }// end getBagel

    /**
     * Gets the number of bagels in this bag.
     *
     * @return the number of bagels in this bag
     */
    public int getQuantity() {
        return quantity;
    }// end getQuantity

    /**
     * Gets the total price for this bag of bagels. This may be less than the per-bagel price times
     * the number of bagels because of quantity discounts.
     *
     * @return the total price
     */
    public BigDecimal getTotalPrice() {
        BigDecimal undiscountedPrice = getPerBagelPrice().multiply(new BigDecimal(quantity));
        if (quantity == BUY_ONE_GET_ONE_FREE_QUANTITY) {
            undiscountedPrice = undiscountedPrice.subtract(getPerBagelPrice());
        }
        if (quantity >= BULK_disCOUNT_MINIMUM) {
            undiscountedPrice = undiscountedPrice.multiply(BULK_disCOUNT_MULTIPLIER);
        }
        //undiscountedPrice = undiscountedPrice.setScale(2,RoundingMode.HALF_UP);
        return undiscountedPrice;
    }// end getTotalPrice

    /**
     * Returns the price associated with the bagel type.
     *
     * @return price based on bagel type
     */
    public BigDecimal getPerBagelPrice() {
        return bagel.getCategory().getPrice();
    }// end getPerBagelPrice
}

解决方法

哦!我相信我已经找到问题了! .multiply()方法将小数精度从2增加到4。解决方案是只创建一个精度为3的新MathContext,然后使用RoundingMode.Half_DOWN推出正确的价格值。

让我知道是否有更好的解决方案。

public BigDecimal getTotalPrice() {
    System.out.println(getPerBagelPrice());
    BigDecimal undiscountedPrice = getPerBagelPrice().multiply(new BigDecimal(quantity));
    if (quantity == BUY_ONE_GET_ONE_FREE_QUANTITY) {
        undiscountedPrice = undiscountedPrice.subtract(getPerBagelPrice());
    }
    if (quantity >= BULK_DISCOUNT_MINIMUM && quantity != 13) {
        undiscountedPrice = undiscountedPrice.multiply(BULK_DISCOUNT_MULTIPLIER,new MathContext(3,RoundingMode.HALF_DOWN));
    }
    System.out.println(undiscountedPrice);
    return undiscountedPrice;
}