我试图使用longs为我自己的货币类,但显然我应该使用BigDecimal
代替。有人可以帮助我开始吗?使用BigDecimal
s作为美元货币的最佳方法是什么,比如至少但不超过2美分小数点等等.BigDecimal
的API很大,我不知道使用哪种方法。此外,BigDecimal
具有更好的精度,但如果它通过double
并不是全部丢失?如果我做新的BigDecimal(24.99)
,它将如何与使用double
不同?或者我应该使用使用String
的构造函数吗?
以下是一些提示:
BigDecimal
进行计算(Money值通常需要此值)。NumberFormat
类进行显示。本课程将处理不同货币金额的本地化问题。但是,它只会接受基元;因此,如果你可以接受由于转换为double
而导致的精确度的微小变化,你可以使用这个类。NumberFormat
类时,在scale()
实例上使用BigDecimal
方法来设置精度和舍入方法。PS:如果你想知道,BigDecimal
is always better than double
, when you have to represent money values in Java。
PPS:
创建BigDecimal
实例
这很简单,因为BigDecimal
provides constructors to take in primitive values和String
对象。你可以使用那些,preferably the one taking the String
object。例如,
BigDecimal modelVal = new BigDecimal("24.455");
BigDecimal displayVal = modelVal.setScale(2, RoundingMode.HALF_EVEN);
显示BigDecimal
实例
您可以使用setMinimumFractionDigits
和setMaximumFractionDigits
方法调用来限制显示的数据量。
NumberFormat usdCostFormat = NumberFormat.getCurrencyInstance(Locale.US);
usdCostFormat.setMinimumFractionDigits( 1 );
usdCostFormat.setMaximumFractionDigits( 2 );
System.out.println( usdCostFormat.format(displayVal.doubleValue()) );
我建议对Money Pattern进行一些研究。 Martin Fowler在他的书“分析模式”中更详细地介绍了这一点。
public class Money {
private static final Currency USD = Currency.getInstance("USD");
private static final RoundingMode DEFAULT_ROUNDING = RoundingMode.HALF_EVEN;
private final BigDecimal amount;
private final Currency currency;
public static Money dollars(BigDecimal amount) {
return new Money(amount, USD);
}
Money(BigDecimal amount, Currency currency) {
this(amount, currency, DEFAULT_ROUNDING);
}
Money(BigDecimal amount, Currency currency, RoundingMode rounding) {
this.currency = currency;
this.amount = amount.setScale(currency.getDefaultFractionDigits(), rounding);
}
public BigDecimal getAmount() {
return amount;
}
public Currency getCurrency() {
return currency;
}
@Override
public String toString() {
return getCurrency().getSymbol() + " " + getAmount();
}
public String toString(Locale locale) {
return getCurrency().getSymbol(locale) + " " + getAmount();
}
}
来使用:
你可以用Money
对象代表所有的钱,而不是BigDecimal
。将钱表示为小十进制意味着您将在每个显示它的地方格式化钱。想象一下显示标准是否会发生变化。您必须在整个地方进行编辑。而是使用Money
模式,将货币格式集中到一个位置。
Money price = Money.dollars(38.28);
System.out.println(price);
或者,等待JSR-354。 Java Money和Currency API即将推出!
1)如果你被限制在double
精度,使用BigDecimal
s的一个原因是用BigDecimal
s创建的double
s实现操作。
2)BigDecimal
由一个任意精度整数非标度值和一个非负32位整数标度组成,而double包含一个对象中基本类型double
的值。 Double
类型的对象包含单个字段,其类型为double
3)它应该没有区别
你应该没有$和精度的困难。一种方法是使用System.out.printf
当你想要舍入到小时的2个小数点时,请使用BigDecimal.setScale(2, BigDecimal.ROUND_HALF_UP)
。但是,在进行计算时请注意舍入错误。当你将货币价值四舍五入时,你需要保持一致。在完成所有计算后,在最后进行四舍五入,或者在进行任何计算之前对每个值应用舍入。哪一个使用将取决于您的业务需求,但一般来说,我认为在最后进行四舍五入似乎对我更有意义。
当你为货币价值构建String
时,请使用BigDecimal
。如果使用double
,它将在末尾有一个尾随浮点值。这是由于计算机架构有关double
/ float
值如何以二进制格式表示。
原始数字类型对于在内存中存储单个值很有用。但是当使用double和float类型处理计算时,舍入存在问题。因为内存表示没有精确映射到值,所以会发生这种情况。例如,double值应该占用64位,但Java不使用所有64位。它只存储它认为数字的重要部分。因此,当您将float或double类型的值一起添加时,可能会出现错误的值。
NumberFormat.getNumberInstance(java.util.Locale.US).format(num);
我会很激进。没有BigDecimal。
这是一篇很棒的文章https://lemnik.wordpress.com/2011/03/25/bigdecimal-and-your-money/
来自这里的想法。
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
testConstructors();
testEqualsAndCompare();
testArithmetic();
}
private static void testEqualsAndCompare() {
final BigDecimal zero = new BigDecimal("0.0");
final BigDecimal zerozero = new BigDecimal("0.00");
boolean zerosAreEqual = zero.equals(zerozero);
boolean zerosAreEqual2 = zerozero.equals(zero);
System.out.println("zerosAreEqual: " + zerosAreEqual + " " + zerosAreEqual2);
int zerosCompare = zero.compareTo(zerozero);
int zerosCompare2 = zerozero.compareTo(zero);
System.out.println("zerosCompare: " + zerosCompare + " " + zerosCompare2);
}
private static void testArithmetic() {
try {
BigDecimal value = new BigDecimal(1);
value = value.divide(new BigDecimal(3));
System.out.println(value);
} catch (ArithmeticException e) {
System.out.println("Failed to devide. " + e.getMessage());
}
}
private static void testConstructors() {
double doubleValue = 35.7;
BigDecimal fromDouble = new BigDecimal(doubleValue);
BigDecimal fromString = new BigDecimal("35.7");
boolean decimalsEqual = fromDouble.equals(fromString);
boolean decimalsEqual2 = fromString.equals(fromDouble);
System.out.println("From double: " + fromDouble);
System.out.println("decimalsEqual: " + decimalsEqual + " " + decimalsEqual2);
}
}
它打印
From double: 35.7000000000000028421709430404007434844970703125
decimalsEqual: false false
zerosAreEqual: false false
zerosCompare: 0 0
Failed to devide. Non-terminating decimal expansion; no exact representable decimal result.
如何将BigDecimal存储到数据库中?地狱,它还存储为双重值???至少,如果我使用没有任何高级配置的mongoDb,它会将BigDecimal.TEN
存储为1E1
。
我带来了一个使用String来将Java中的BigDecimal作为String存储到数据库中。您有验证,例如@NotNull
,@Min(10)
等...然后您可以在更新时使用触发器或保存以检查当前字符串是否是您需要的数字。但是mongo没有触发器。 Is there a built-in way for Mongodb trigger function calls?
有一个缺点我很开心 - BigDecimal as String in Swagger defenition
我需要产生招摇,所以我们的前端团队理解我传给他们一个以字符串形式呈现的数字。 DateTime例如以String形式呈现。
我在上面的文章中读到了另一个很酷的解决方案...使用long来存储精确的数字。
标准长值可以存储美国国债的当前价值(以美分,而不是美元)6477次,没有任何溢出。更多:它是一个整数类型,而不是一个浮点。这使得它更容易和准确地使用,并保证行为。
https://stackoverflow.com/a/27978223/4587961
也许将来MongoDb会增加对BigDecimal的支持。 https://jira.mongodb.org/browse/SERVER-1393 3.3.8似乎已经完成了这项工作。
这是第二种方法的一个例子。使用缩放。 http://www.technology-ebay.de/the-teams/mobile-de/blog/mapping-bigdecimals-with-morphia-for-mongodb.html