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

Calculated BigDecimal values returning correct value but adding extra zeros onto number

作为学校作业的一部分,我正在编写一个程序来帮助确定一袋百吉饼的总价,而我的 getTotalPrice() 方法生成的值正在推出正确的值,但是对于其中一些,在最终值中添加了几个零。我尝试使用 setScale() 方法将小数点四舍五入为两个空格,但有人告诉我这不是理想的解决方案。我以前从未使用过 BigDecimals,所以我相信我的代码中缺少一些东西,有什么建议吗?如果需要更多信息,请告诉我,我不太确定 include/exclude.

的代码是什么
// 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",
                Category.OLD_FASHIONED), SESAME_SEED("sesame seed", Category.OLD_FASHIONED), ONION(
                        "onion", Category.OLD_FASHIONED), EVERYTHING("everthing",
                                Category.OLD_FASHIONED), ASIAGO("asiago",
                                        Category.GOURMET), BLUEBERRY("blueberry",
                                                Category.GOURMET), CINNAMON_RAISIN(
                                                        "cinnamon raisin",
                                                        Category.GOURMET), 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;
}