double x = 1234567891234.0;
double y = 1234567891234.0;
System.out.println(x);
System.out.println(y);
BigDecimal bigZ = new BigDecimal(x).multiply(new BigDecimal(y));
double z = bigZ.doubleValue();
System.out.println(bigZ);
System.out.println(z);
结果:
1.234567891234E12 //precise 'x'
1.234567891234E12 //precise 'y'
1524157878065965654042756 //precise 'x * y'
1.5241578780659657E24 //loosing precision
x
和y
是精确的,使用BigDecimal
进行乘法也是精确的。但是,将结果转换回double
后,我们会失去一些最不重要的数字。
double
允许您存储最多 16 个最重要的数字,long
- 从 0 开始的 18 个数字。double
可能会在没有警告的情况下丢弃最不重要的数字,而 long
则可能会溢出。如果您真的关心准确性和可靠性,请始终使用 BigDecimal
(除非您 真的 知道值的下限和上限范围)。毕竟,拥有快速还是正确的程序更好呢? - Tomasz NurkiewiczassertEquals(new BigDecimal("0.01").toString(), new BigDecimal(0.01).toString());
expected:<0.01[]> but was <0.01[000000000000000020816681711721685132943093776702880859375]>
事实是,你不能将精确的0.01存储为“double”类型。只有BigDecimal可以完全按你想要的方式存储所需的数字。
请记住,BigDecimal是不可变的。以下代码可以编译:
BigDecimal amount = new BigDecimal("123.45");
BigDecimal more = new BigDecimal("12.34");
amount.add(more);
System.out.println("Amount is now: " + amount);
这是因为你需要将结果赋值给一个新的(或同样的)BigDecimal变量。现在的金额是:123.45
amount = amount.add(more)
可接受的做法取决于您的项目。在某些项目中,使用double和long可能是预期的,但在其他项目中,这被认为是不可接受的。作为double,您可以表示高达70,000,000,000,000.00美分(比美国国家债务还大)的值,而使用固定位数的long,您可以准确地表示90,000,000,000,000,000.00。
如果您必须处理通货膨胀率极高的货币(在任何情况下都是一个坏主意),但由于某种原因仍然需要考虑每一分钱,那么请使用BigDecimal。
如果您使用double或long或BigDecimal,则必须四舍五入结果。如何实现此目标因每种数据类型而异,而BigDecimal是最不容易出错的,因为您需要指定不同操作的舍入和精度。而对于double或long,您需要自行解决。
long
比double
/float
更好的选择。
你确定使用BigDecimal
类型会成为真正的瓶颈吗?
int
或long
,则必须使用某种常数因子,通常为100或1000,以支持小于一货币单位的表示。例如,您可以将$1表示为1000L,将1¢表示为10L。并且最好将此包装在自己的数据类型中。 - ruakhdouble
的唯一用途是存储小数值,那么在某些条件下可以:如果您可以保证您的值不超过 15 个小数位,则将值转换为 double
(53 位精度),然后使用 15 位小数(或更少)将 double
转换回十进制,将会从 David Matula 在他的文章 In-and-out conversions 中所证明的定理的应用中获得原始值,即没有丢失。请注意,为了适用此结果,必须使用正确舍入进行转换。double
可能不是最佳选择:货币值通常不是用浮点数表示,而是用几个小数位 (p) 的固定点表示,在这种情况下,将值转换为一个按 10^p 缩放的整数并存储此整数(正如其他人建议的)更好一些。坑点在于浮点数/双精度浮点数无法存储所有值而不失去精度。即使您使用BigDecimal
并在计算过程中保留精度,最终产品仍然以浮点数/双精度浮点数的形式存储。
根据我的经验,“正确”的解决方案是将货币价值存储为表示每美元千分之一的整数(例如Long
)。这为大多数任务提供了足够的分辨率,例如利息累积,同时避免了使用浮点数/双精度浮点数的问题。作为额外的“奖励”,这需要与浮点数/双精度浮点数相同的存储量。