我一直听说你应该使用钱币类,因为浮点不准确。但是,很难找到浮点不准确实际导致错误结果的任何示例。
我选择的编程语言是Python。为了测试结果是否与预期不同,我使用:
expected = '1.23'
result = '{:0.2f}'.format(result)
assert expected == result
因此,虽然以下是浮点不准确的一个很好的例子,但对于大多数用例来说,它不是一个使用有理数字类(如Pythons分数)的货币类的例子:
a = 10.0
b = 1.2
assert a + b - a == b
我能想到的最好的事情是
result = (a + b - a) * 10**14 - b * 10**14
expected = 0
但乘以与10**14
相关的金钱似乎真的已经弥补了。
现在我想知道是否有任何现实的例子显示需要一个金钱类,或者是否只需四舍五入到两位数即可“捕捉”所有内容。
答案 0 :(得分:11)
令人惊讶的是,很难找到任何浮点不准确导致错误结果的示例。
我不会说这太难了。一个着名的现实世界的例子,虽然不涉及金钱,但爱国者导弹系统代码累积了每秒0.000000095秒的浮点舍入误差;如果系统没有每五天重新启动一次,它将会在几分之一秒内完成。它截获的导弹每秒可以移动几千米,它会错过。
由于此浮点错误,至少有28人死亡。
我们可以证明爱国者的错误,而不会让更多的生命处于危险之中。这是一个小C#程序。假设我们加起来一角钱;在我们收到重大错误之前,我们需要添加多少?
double sum = 0.0;
long k = 0;
long report = 1;
while (true) {
k += 1;
sum += 0.1;
if (k == report) {
Console.WriteLine($"{k} {k / 10.0 - sum}");
report *= 10;
}
}
只要你愿意,就让它运行。机器上的输出开始了:
1 0
10 1.11022302462516E-16
100 1.95399252334028E-14
1000 1.406874616805E-12
10000 -1.58820512297098E-10
100000 -1.88483681995422E-08
1000000 -1.33288267534226E-06
10000000 0.00016102462541312
100000000 0.0188705492764711
1000000000 1.25458218157291
10000000000 -163.12445807457
仅有一亿次计算 - 所以,1000万美元 - 我们已经下了2美分。通过100亿次计算,我们的票价为163.12美元。当然,这是每次交易的一个小错误,并且可能是163.12美元在宏观计划中不是很多钱,而不是十亿美元,但如果我们无法正确计算1亿次0.1 那么我们有没有理由对这个系统产生的任何计算都有信心。
错误可能保证为零;为什么你不希望错误为零?
练习:您暗示您知道将舍入放在何处以确保消除此错误。那么:他们去哪了?
一些额外的想法,受到你的评论的启发:
虽然我认为金钱类肯定需要十进制数据类型,但我认为这还不够。我认为金钱类也应该(1)防止增加非货币数量(2)防止增加两种不同的货币(3)不允许取得权力/根源。
如果您想要的是真实世界中涉及度量单位未被类型系统所困的金钱错误的例子,那么有许多这样的例子。
我曾经在一家编写检测软件缺陷的软件的公司工作。最神奇的缺陷检查器之一是“剪切和粘贴错误”检测器,它发现了现实世界代码中的缺陷,如
dollarTot = (euros1 + euros2) * dollarEuroRate;
pesoTot = (euros3 + euros4) * pesoEuroRate;
... dozens more like this...
然后在代码中
dollarTot = (yen1 + yen2) * yenDollarRate;
pesoTot = (yen3 + yen4) * pesoEuroRate;
...
糟糕。
拥有该缺陷的主要国际贸易公司打电话给我们,并说下次我们在瑞士时啤酒就在他们身上。
这些例子说明了为什么金融机构对F#这样的语言如此感兴趣,这使得超级简单来跟踪类型系统中的属性。
几年前我在我的博客上做了一系列关于使用ML类型系统在实现虚拟机时查找错误的系列文章,其中整数可能意味着十几个不同数据结构的地址,或者偏移到这些结构中。它可以快速发现错误,并且运行时开销很小。度量单位类型非常棒,即使对于简单的问题,例如确保您不会将美元与日元混淆。