我们可以使用double来存储货币字段并使用BigDecimal进行算术运算

我知道double / float的问题,建议使用BigDecimal而不是double / float来表示货币字段。 但是双/浮动更有效且节省空间。 然后我的问题是:使用double / float来表示Java类中的货币字段是可以接受的,但是使用BigDecimal来处理算术(即在任何算术运算之前将double / float转换为BigDecimal)和等同检查?

原因是节省了一些空间。 我真的看到很多项目都使用double / float来表示货币领域。

这有什么陷阱吗? 提前致谢。

不,你不能。

假设double足以存储两个值xy 。 然后将它们转换为安全的BigDecimal并将其转换为多个。 结果是准确的,但是如果将乘法结果存储为double ,则可能会失去精度。 certificate:

 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 

xy是准确的,以及使用BigDecimal的乘法。 然而,在回到double我们松散了最低有效数字。

我还建议您只使用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) 

long会比double / float更好。

你确定使用BigDecimal类型将是一个真正的瓶颈吗?

可接受的取决于您的项目。 你可以在一些项目中使用double和long来预期这样做。 但是在其他项目中,这被认为是不可接受的。 作为一个双倍,你可以代表价值高达70,000,000,000,000.00到分(大于美国国债),固定地点长,你可以准确地代表90,000,000,000,000,000.00。

如果你必须处理超级通货膨胀的货币(在任何情况下都是一个坏主意),但由于某种原因仍然需要考虑每一分钱,使用BigDecimal。

如果使用double或long或BigDecimal,则必须对结果进行舍入。 如何执行此操作因每种数据类型而异,BigDecimal最不容易出错,因为您需要指定不同操作的舍入和精度。 使用double或long,您可以使用自己的设备。

坑落是浮动/双打不能存储所有值而不会失去精度。 即使你在计算过程中使用BigDecimal并保持精度,你仍然将最终产品存储为float / double。

根据我的经验,对此的“正确”解决方案是将货币值存储为代表数千美元的整数(例如, Long )。 这为大多数任务提供了足够的分辨率,例如利息累积,同时提出了使用浮点数/双打的问题。 作为额外的“奖励”,这需要与浮点数/双打数相同的存储量。

如果只使用double存储十进制值,那么是的,你可以在某些条件下:如果你可以保证你的值不超过15位十进制数,那么将一个值转换为double (53位精度)并转换double精度到15位精度(或更小)的十进制数将给出原始值,即没有任何损失,来自David Matula定理在他的文章“ 进出转换”中certificate的应用。 请注意,要使此结果适用,必须使用正确的舍入来完成转换。

但请注意, double可能不是最佳选择:货币值通常不是以浮点表示,而是以小数点后几位数( p )的固定点表示,在这种情况下,将值转换为缩放10 ^ p并存储此整数(如其他建议的那样)更好。