一尘不染

为什么不使用Double或Float代表货币?

javascript

一直有人告诉我,永远不要用doublefloat类型来代表金钱,这一次我向您提出一个问题:为什么?

我敢肯定有一个很好的理由,我根本不知道这是什么。


阅读 679

收藏
2020-09-29

共1个答案

一尘不染

因为浮点数和双精度数不能准确代表我们用于货币的基数10的倍数。这个问题不仅仅针对Java,而且还针对任何使用base 2浮点类型的编程语言。

在基数10中,您可以将10.25编写为1025 * 10 -2(整数乘以10的幂)。IEEE-754浮点数是不同的,但是考虑它们的一种非常简单的方法是乘以2的幂。例如,您可能正在查看164 * 2 -4(整数乘以2的幂),它也等于10.25。这不是数字在内存中的表示方式,但是数学含义是相同的。

即使在以10为基数的情况下,该符号也无法准确表示最简单的分数。例如,您不能表示1/3:十进制表示形式正在重复(0.3333 …),因此不存在可以乘以10的幂来获得1/3的有限整数。您可以使用3的长序列和较小的指数,例如333333333 * 10 -10,但这并不准确:如果将其乘以3,则不会得到1。

但是,出于数钱的目的,至少对于货币价值在美元数量级内的国家而言,通常您所需要的只是能够存储10 -2的倍数,因此这并不重要不能代表1/3。

浮点数和双精度数的问题在于,绝大多数类似货币的数字都不能精确表示为整数乘以2的幂。实际上,0和1之间只有0.01的倍数(在交易时很重要)可以用金钱来表示,因为它们是整数美分),可以完全表示为IEEE-754二进制浮点数,分别为0、0.25、0.5、0.75和1。所有其他值相差很小。类似于0.333333的示例,如果将浮点值设为0.1,然后将其乘以10,则不会得到1。

首先,将钱表示为double或float可能会很好地解决这个小错误,但是随着您对不精确的数字执行更多的加法,减法,乘法和除法,错误将会加重,最终您将得到明显的值不准确。这使得浮子和双子不足以应付金钱,在这种情况下,要求以10为底的幂的倍数具有完美的精度。

适用于几乎所有语言的解决方案是改用整数,然后计算分。例如,1025为$ 10.25。几种语言还具有内置类型来处理金钱。其中,Java具有BigDecimal类,而C#具有decimal类型。

2020-09-29