计算出的 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;
}
作为学校作业的一部分,我正在编写一个程序来帮助确定一袋百吉饼的总价,而我的 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;
}