我们可以使用double来存储货币字段并使用BigDecimal进行算术运算
我知道double / float的问题,建议使用BigDecimal而不是double / float来表示货币字段。 但是双/浮动更有效且节省空间。 然后我的问题是:使用double / float来表示Java类中的货币字段是可以接受的,但是使用BigDecimal来处理算术(即在任何算术运算之前将double / float转换为BigDecimal)和等同检查?
原因是节省了一些空间。 我真的看到很多项目都使用double / float来表示货币领域。
这有什么陷阱吗? 提前致谢。
不,你不能。
假设double
足以存储两个值x
和y
。 然后将它们转换为安全的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
x
和y
是准确的,以及使用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并存储此整数(如其他建议的那样)更好。