很难找到浮点不准确实际上导致错误结果的任何示例。
我不会说这非常困难。一个著名的现实世界例子,虽然不涉及金钱,但爱国者导弹系统代码累积了每秒 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 美元。当然,这是每笔交易的一个小错误,与 10 亿美元相比,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# 等语言如此感兴趣,因为 F# 使得在类型系统中跟踪属性变得超级容易。
几年前,我在我的博客上做了一个系列文章,内容是在实现虚拟机时使用 ML 类型系统来查找错误,其中整数可能意味着十几个不同数据结构的地址,或者这些结构的偏移量。它可以快速发现错误,并且运行时开销很小。度量单位类型很棒,即使是简单的问题,比如确保你不会把美元和日元混在一起。