我知道double / float的问题,建议使用BigDecimal而不是double / float来表示货币字段。但是双/浮动更有效且节省空间。然后我的问题是: 使用double / float来表示Java类中的货币字段是可以接受的,但是使用BigDecimal来处理算术(即在任何算术运算之前将double / float转换为BigDecimal)和等同检查?
原因是节省了一些空间。我真的看到很多项目正在使用double / float来代表货币领域。
这有什么陷阱吗? 提前谢谢。
答案 0 :(得分:8)
不,你不能。
假设double
足以存储两个值x
和y
。然后将它们转换为安全BigDecimal
并将其转换为多个。结果是准确的,但是如果将乘法结果存储回double
,则可能会失去精度。证明:
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
后,我们会丢失最不重要的数字。
答案 1 :(得分:5)
我还建议您只使用BigDecimal进行可能涉及货币的所有算术。
确保始终使用BigDecimal的String构造函数。为什么?在JUnit测试中尝试以下代码:
assertEquals(new BigDecimal("0.01").toString(), new BigDecimal(0.01).toString());
您将获得以下输出:
expected:<0.01[]> but was <0.01[000000000000000020816681711721685132943093776702880859375]>
事实是,您不能将确实 0.01存储为“双倍”金额。只有BigDecimal可以根据需要存储您需要的数字完全。
请记住,BigDecimal是不可变的。以下将编译:
BigDecimal amount = new BigDecimal("123.45");
BigDecimal more = new BigDecimal("12.34");
amount.add(more);
System.out.println("Amount is now: " + amount);
但结果输出为:
金额现在为:123.45
那是因为您需要将结果分配给新的(或相同的)BigDecimal变量。
换句话说:
amount = amount.add(more)
答案 2 :(得分:2)
long
将是比double
/ float
更好的选择。
您确定使用BigDecimal
类型会成为真正的瓶颈吗?
答案 3 :(得分:2)
什么是可接受的取决于您的项目。你可以在一些项目中使用double和long来预期这样做。但是在其他项目中,这被认为是不可接受的。作为双倍,您可以表示高达70,000,000,000,000.00到分钱(大于美国国债)的值,固定地点长,您可以准确地代表90,000,000,000,000,000.00。
如果你必须处理超级通货膨胀的货币(在任何情况下都是一个坏主意)但由于某些原因仍然需要考虑每一分钱,请使用BigDecimal。
如果使用double或long或BigDecimal,则必须对结果进行舍入。如何执行此操作因每种数据类型而异,BigDecimal最不容易出错,因为您需要指定不同操作的舍入和精度。使用double或long,您可以使用自己的设备。
答案 4 :(得分:0)
坑落是浮动/双打无法存储所有值而不会丢失精度。即使您在计算过程中使用BigDecimal
并保持精度,您仍然将最终产品存储为float / double。
根据我的经验,对此的“正确”解决方案是将货币值存储为表示数千美元的整数(例如Long
)。这为大多数任务提供了足够的分辨率,例利息累积,而侧面踩着使用浮动/双打的问题。作为一个额外的“奖励”,这需要与浮点数/双打数相同的存储量。
答案 5 :(得分:0)
如果double
的唯一用途是存储十进制值,那么您可以在某些条件下:如果您可以保证您的值不超过15位小数,然后将值转换为double
(精度为53位)并将double
转换回十进制,精度为15位(或更低)将为您提供原始值,即没有任何损失, David Matula定理的应用在他的文章In-and-out conversions中得到了证明。请注意,要使此结果适用,必须使用correct rounding完成转换。
但请注意,double
可能不是最佳选择:货币值通常不是以浮点表示,而是以小数点后的几位数( p )表示的固定点点,在这种情况下,将值转换为一个整数,其缩放比例为10 ^ p 并存储此整数(如其他建议的那样)更好。