例如,使用美元,您的精度永远不会低于$ 0.01
真的吗?
为什么不应该将货币存储为IEEE 754浮点数的古老问题。
请随时将英寸存储在IEEE 754 浮点数中。它们精确地存储了您的期望。
请随意在IEEE 754浮点数中存储任何金额,您可以使用刻度将刻度尺分成几分之一英寸的数字进行存储。
为什么?因为当您使用IEEE 754时,就是这样存储它。
关于英寸的事情是,它们分为两半。关于大多数货币的事情是,它们被分为十分之一(有些不是,但让我们保持专注)。
除了对于大多数编程语言而言,IEEE 754浮点数的输入和输出以小数表示之外,这种差异不会造成任何混乱。这很奇怪,因为它们不是以小数存储的。
因此,当您要求计算机进行存储时,您将永远看不到这些位是如何使事情变得奇怪的0.1
。对它进行数学运算时,您只会看到怪异的东西,并且有奇怪的错误。
从Josh Bloch的有效java中:
System.out.println(1.03 - .42);
产生 0.6100000000000001
最能说明问题的不是1
右边那边的坐姿。这是必须使用的怪异数字。而不是使用最受欢迎的示例,0.1
我们必须使用一个示例来显示问题并避免将其隐藏的舍入。
例如,为什么这样做?
System.out.println(.01 - .02);
产生 -0.01
因为我们很幸运。
我讨厌难以诊断的问题,因为有时我会变得“幸运”。
IEEE 754根本无法精确存储0.1。但是,如果您要求它存储0.1,然后要求它进行打印,那么它将显示0.1,您会认为一切都很好。不好,但是您看不到它,因为四舍五入回到了0.1。
有些人通过将这些差异四舍五入来混淆其他人。不,这些不是舍入错误。四舍五入正在执行应有的操作,并将非十进制转换为十进制,以便可以在屏幕上打印。
但这掩盖了数字的显示方式和存储方式之间的不匹配。舍入时未发生该错误。当您决定将数字放入无法精确存储的系统,并假定数字被精确存储时,它就发生了。
没有人期望π可以精确地存储在计算器中,并且他们可以很好地使用它。因此,问题甚至与精度无关。这与预期的精度有关。计算机的显示结果0.1
与计算器的十分之一相同,因此我们希望它们能够像计算器一样完美地存储十分之一。他们没有。这是令人惊讶的,因为计算机更昂贵。
让我告诉您不匹配的地方:
请注意,1/2和0.5完美对齐。但是0.1并没有排队。当然,如果您始终将其除以2,则可以拉近距离,但永远不会精确击中它。而且每次除以2时,我们都需要越来越多的位。因此,用除以2的任何系统表示0.1都需要无限数量的位。我的硬盘不是那么大。
因此,IEEE 754在比特用完时会停止尝试。很好,因为我需要硬盘驱动器上的空间来存放...全家福。不完全是。全家福照片。:P
无论如何,您键入的内容和所看到的都是小数点(右侧),但是存储的是小数点(左侧)。有时这些完全一样。有时他们不是。有时看起来就像它们完全不同时一样。这就是四舍五入。
特别是,我们需要知道什么才能以某种货币存储值并打印出来?
请,如果您要处理基于十进制的货币,请不要使用浮点数或双精度数。
如果您确定不会涉及十分之几的便士,则只需存储便士即可。如果您不知道该货币的最小单位,那就使用它。如果不能,请使用BigDecimal之类的方法。
我的净资产可能总是适合64位整数,但BigInteger之类的东西对于大于此的项目效果很好。它们只是比本地类型慢。
弄清楚如何存储它只是问题的一半。请记住,您还必须能够显示它。一个好的设计将把这两件事分开。这里使用浮点数的真正问题是将这两件事混在一起。