是否有适用于所有货币(甚至包括不同于美元,欧元和英镑的货币)的单一数据表示形式?


12

我可以找到许多有关用于以某种货币表示金额的库的问题。关于为什么不应该将货币存储为IEEE 754浮点数的古老问题。但我似乎找不到更多。当然,在现实世界中,有关货币的知识还有很多。我对您需要了解以物理方式表示它的内容特别感兴趣(例如,对于美元,您永远不会低于0.01美元的精度,允许以整数美分表示)。

但是,当您知道的唯一的货币是西方流行的货币(例如美元,欧元和英镑)时,就很难对程序的通用性做出假设。从纯粹的程序设计角度来看,还有哪些相关知识?我不关心转换主题。

特别是,我们需要知道什么才能能够以某种货币存储值并打印出来?


2
并非所有程序员都在操纵货币数量的行业中工作。
whatsisname

4
哦当然了 但这几乎可以说是一切。我认为我们可以假设那些发现此问题有用的人将是从事货币工作的人。

5
我为纽约的银行和其他金融服务公司做了很多开发工作。而且,我可以保证您的问题没有“封闭式”答案。您可能会问,“我必须知道多少数学?” 例如,在1990年代,股票价格从八分之一和256分跌至十进制,因此需要进行大量重新编程。谁能猜到会发生这种情况?您只需要了解应用程序支持的业务,以及如何最好地表示数据(在本例中为货币)即可满足这些业务需求。
John Forkosh

4
我的0.02:价格是一回事。货币是另一种。
马查多

3
我想,哦,所有货币都必须有一些共同点。但也许不是。一分钱的英镑目前为1⁄100英镑,但为1⁄240英镑。因此,您不仅有一个1/240的除数,而且还有一个转换日期,并且可能还没有转换旧便士的汇率。zh_cn.wikipedia.org/wiki/Penny_sterling 甚至不让我开始使用贝壳壳! en.wikipedia.org/wiki/Shell_money或金钱需要信念才能成为现实的事实:en.wikipedia.org/wiki/Money 即使不是非常适合StackExchange,也要问一个大问题!
GlenPeterson

Answers:


24

例如,使用美元,您的精度永远不会低于$ 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之类的东西对于大于此的项目效果很好。它们只是比本地类型慢。

弄清楚如何存储它只是问题的一半。请记住,您还必须能够显示它。一个好的设计将把这两件事分开。这里使用浮点数的真正问题是将这两件事混在一起。

在此处输入图片说明


6
我并不是真的在寻找关于IEEE 754为什么不赚钱的另一种解释。我在OP中提到,这已在其他地方得到了很好的解释。同样,我使用术语“实际使用情况”也是有原因的。即是因为尽管汽油价格,股票交易价格等可能具有任意精度,但是实物货币(以及大量用户交易)不会超过数百个位置(并且各种软件都只想代表可以用其支付的费用)实物货币)。我确实想知道的一则信息是,是否存在货币进入了更多的小数位。

3
浮点数的“怪异”不仅限于其一半与1/10。名称中的“浮点数”方面有时还会导致意外的结果。
whatsisname

6
在十进制化之前,英国货币为英镑,先令和便士(£sd),其中£1 == 20s,1s = 12d,因此£1 = 240d因此1d = 0.0046666666666 ..甚至不能表示为小数。在加入欧元之前,意大利里拉曾一度突破2346.4美元(在2001年达到1美元),所以工资和房价之类的价格有时达到了单精度浮动汇率的上限,经济数据达到了两倍的最高水平。
史蒂夫·巴恩斯

2
我只是争辩/指出货币是一回事,而价格是另一回事。您可以在价格中获得超过$ 0.01的精度,但是您无法有效地支付该精度。
马查多

1
另外,还有全家福照片。磅 :)
马查多(Machado)

10

我可以找到许多有关用于以某种货币表示金额的库的问题。

正如我将要解释的,除非您的语言的标准库在某些数据类型中缺少,否则“库”是不必要的。

当然,在现实世界中,有关货币的知识还有很多。我对您需要了解以物理方式表示它的内容特别感兴趣(例如,对于美元,您永远不会低于0.01美元的精度,允许以整数美分表示)。

很简单,您需要定点小数,而不是浮点小数。例如,Java的BigDecimal类可用于存储货币金额。其他现代语言具有类似的内置类型,包括C#Python。实现方式各不相同,但是它们通常将数字存储为整数,而将小数点位置存储为单独的数据成员。即使执行算术运算会产生奇数余数(例如0.0000001)和IEEE浮点数,这也可以提供精确的精度。

特别是,我们需要知道什么才能以某种货币存储值并打印出来?

有几点要点。

  1. 使用实际的十进制类型而不是浮点数。

  2. 了解货币金额有两个组成部分:值(5.63)和货币代码或类型(USD,CAD,GBP,EUR等)。有时您可以忽略货币代码,而其他时候则至关重要。如果您正在使用允许多种货币的金融或零售/电子商务系统,该怎么办?如果您试图从CAD客户那里收款,但他们想用MXN付款,会发生什么情况?您需要带有货币代码和货币金额的“ money”类型,以便能够混合这些值(也包括汇率,但我不希望切线过大)。同时,我的个人理财软件无需担心这一点,因为所有内容都是美元(可以混合货币,但我永远不需要)。

  3. 尽管实际上货币可能具有最小的物理单位(加元和美元只有美分,日元只是...日元),但货币可能会变小。CandiedOrange的答案指出燃油价格仅为十分之一美分。我的财产税是按每1美元的工厂价格计算的,即十分之一美分(1/1000美元的美元)。不要将自己限制在$ 0.01美元。尽管您可能大部分时间都显示这些值,但您的类型应允许较小(上面引用的十进制类型可以)。

  4. 中间计算当然必须允许比一分钱更高的精度。我曾在零售/电子商务系统上工作,在内部内部价值四舍五入为$ 0.00000001。十进制类型(或SQL)通常不支持无限精度,因此必须有一些限制。例如,使用Java的BigDecimal进行1/3的除法将抛出一个异常,而未指定RoundingMode或MathContext,因为无法精确表示该值。

    无论如何,这在某些情况下至关重要。让我们假设您有一个用户的购物车中有六个商品,他去结账。您的系统必须计算税款,并且对每个项目进行计算,因为项目的税款可能不同。如果您对每个项目的税款进行四舍五入,则在交易/购物车级别可能会得到一分钱的四舍五入错误。解决此问题的一种方法可能是将税项存储到每个项目的小数点后的位数,获取整个交易的总金额,然后返回并四舍五入,以使总税额正确(也许一个项目向上舍入,另一个向下舍入)。

从所有这一切中认识到的重要一点是,对四舍五入来说,重要的事情对于合适的人(例如我以前的一些客户,他们必须代表客户缴纳政府营业税)非常重要。但是,这些都是已解决的问题。请牢记以上几点,并自己做一些实验,您会从中学到东西。


在2上,我认为汇率足以至少获得自己的数字。您必须考虑到费率会随时间变化,这有两种不同的处理方式。
伊兹方(Izkata)

2
+1。另外,货币会随着时间而变化。在巴西,我们在80年代和90年代反复进行了货币兑换,由于通货膨胀失控,削减了零并在必要时更名了货币。这意味着货币的各个方面都可能随时间变化。
马查多

@Izkata当然可以,我曾从事过改变货币的系统。我想谈谈这个主题,因为它很重要。但是,这不是问题的重点,我可以输入另一个答案,只要这个答案与汇率问题有关即可。

正如我将解释的那样,“除非您的语言的标准库在某些数据类型中缺少,否则“库”是不必要的。” 货币可以有一半,四分之一,十分之一,八分之一等,这意味着我们至少需要用十进制表示。但是,我们是否完全可以确定,没有一种货币的总和为1/3?
丹尼尔·麦克劳里

4

许多开发人员面对任何货币的单一数据表示的一个地方是iOS应用程序的应用内购买。您的客户可以连接到世界上几乎任何国家的商店。在这种情况下,您将获得包含双精度数字和货币代码的购买价格。

您需要注意,这个数字可能很大。在有些货币中,相当于十美元的货币超过100,000个单位。而且我们很幸运,目前没有像津巴布韦元这样的货币存在,那里可能有数百万亿美元的钞票!

为了显示货币,您将需要一些图书馆-您没有机会自行解决。显示取决于两件事:货币代码和用户的语言环境。想一想如何在美国语言环境和加拿大语言环境中显示美元和加元:在美国,您有$ vs CAN $,在加拿大,有$ vs. $。希望它已内置在操作系统中,或者您有一个不错的库。

对于计算,任何计算都将以舍入步骤结束。您将必须了解如何合法地进行四舍五入。这不是编程问题,而是法律问题。例如,如果您在英国计算增值税,则必须计算每个项目或每个项目行的税,并将其四舍五入到几美分。您要舍入的内容取决于货币。但是规则显然取决于国家。您不能指望在英国合法合法的计算在日本合法合法,反之亦然。


0

特别是,我们需要知道什么才能能够以某种货币存储值并打印出来?

  • “存储值”:定点数字数据类型和货币类型。我认为这些非常明显。存储到非定点数可能需要四舍五入并更改值。货币计算将是另一个问题。
  • “打印出来”:用户区域设置。如果幸运的话,您可以使用标准库来执行所有操作,例如货币符号,千位分隔符,十进制分隔符,精度等。

与语言环境有关的一些示例问题:

  • 在美国语言环境中,100美元应为100.00美元,在其他语言环境中,以小数点分隔的点应为100.00美元。
  • 一些国家/地区使用无数而不是千位来对数字进行分组,例如,不是用10,000.00来分​​组,那只是1,0000.00
  • 出于实际原因,人们并不会打印所有小的货币数字,例如,您只希望您打印10亿美元,而不是1,000,000,000.00美元。

另一个潜在的问题是,即使您将货币正确地存储在定点数中,也可能需要将其转换为浮点数,因为用于打印的库仅支持浮点数。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.