Currency 什么是一个现实的例子,表明需要一个货币类?

Currency 什么是一个现实的例子,表明需要一个货币类?,currency,floating-accuracy,rational-numbers,Currency,Floating Accuracy,Rational Numbers,我一直听说,由于浮点不准确,您应该使用money类。然而,令人惊讶的是,很难找到任何浮点不准确实际导致错误结果的例子 我选择的编程语言是Python。为了测试结果是否与预期不同,我使用: expected = '1.23' result = '{:0.2f}'.format(result) assert expected == result 因此,虽然下面是浮点不准确的一个很好的例子,但对于大多数用例来说,它不是使用有理数类(如Pythons分数)的money类的需要的例子: a = 10.0

我一直听说,由于浮点不准确,您应该使用money类。然而,令人惊讶的是,很难找到任何浮点不准确实际导致错误结果的例子

我选择的编程语言是Python。为了测试结果是否与预期不同,我使用:

expected = '1.23'
result = '{:0.2f}'.format(result)
assert expected == result
因此,虽然下面是浮点不准确的一个很好的例子,但对于大多数用例来说,它不是使用有理数类(如Pythons分数)的money类的需要的例子:

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.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美元与10亿美元相比并不是一大笔钱,但是如果我们不能正确计算出1亿乘以0.1的值,那么我们就没有理由对这个系统产生的任何计算有信心

误差可以保证为零;为什么不希望错误为零

练习:您意味着您知道在何处放置圆角,以确保消除此错误。那么:他们去哪里


受您的评论启发,还有一些想法:

虽然我认为money类肯定需要十进制数据类型,但我认为这还不够。我认为货币类也应该(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类型系统来发现bug,其中一个整数可能意味着十几个不同数据结构的地址,或者这些结构的偏移量。它可以快速发现bug,并且运行时开销最小。度量单位类型非常棒,即使是对于一些简单的问题,比如确保你不会把美元和日元混在一起。

如果你想把所有东西四舍五入到两位数,那么你已经有了你的货币类型;它是“整数”,它计算的是美分数。就我个人而言,我永远不会用浮点数来表示精确的十进制数,特别是在我错了的情况下,如果进行的计算是真的钱就在这条线上的话。我会使用一个经过仔细设计和测试的类型来准确地模拟我正在执行的真实事务。你的问题基本上是“我可以在做金融交易编码时抄近路吗?”我不会!让我们考虑一下。你的代码运行的每个芯片都支持非规范化吗?面对非规范化,你的算法正确吗?有些浮点芯片使用80位精度,有些使用64位精度,有些基本上是随机来回切换;这对算法的正确性、可测试性和可重复性有任何影响吗?您的系统是否处理无穷大和N?它是否正确地处理了浮动中同时存在正零和负零的事实?在我使用浮点数之前,这些都是我想知道的答案。当使用浮点数时,人们通常不知道的一个例子是何时使用Kahan求和,何时不使用。现在,如果你想说“但我从来没有把这么多的数字加起来,以至于在我使用哪种求和算法进行四舍五入后会产生差异”,那么,你要做的再次说明,你知道一些关于问题空间的知识,而我们不知道,你正在解决一个比一般的求和问题更简单的问题。所以,如果你的问题是:在简单的情况下,使用浮点数和舍入是安全的,那么安全吗?是的,根据定义。我仍然会避免浮点值,即使你知道其固有的危险,除非你是唯一一个会看这段代码的人。使用货币类型可以告诉读者你的意图。它还避免了将来的增强在使用浮点值时不安全以及出现bug的问题。