188

我正在使用十进制列在数据库中存储货币值,今天我想知道使用什么精度和比例。

由于据说固定宽度的 char 列效率更高,我认为十进制列也是如此。是吗?

我应该使用什么精度和规模?我在想 24/8 精度。这是矫枉过正,不够还是可以?


这就是我决定做的事情:

  • 将转换率(如果适用)存储在交易表本身中,作为浮点数
  • 将货币存储在帐户表中
  • 交易金额将为DECIMAL(19,4)
  • 所有使用转换率的计算都将由我的应用程序处理,因此我可以控制舍入问题

我认为转换率的浮点数不是问题,因为它主要用于参考,无论如何我都会将其转换为小数。

谢谢大家的宝贵意见。

4

10 回答 10

207

如果您正在寻找一刀切,我建议这DECIMAL(19, 4)是一个流行的选择(快速谷歌证实了这一点)。我认为这源于旧的 VBA/Access/Jet Currency 数据类型,是该语言中第一个定点十进制类型;Decimal仅在 VB6/VBA6/Jet 4.0 中以“1.0 版”样式出现(即未完全实现)。

存储定点十进制值的经验法则是存储至少比实际需要的小数位允许舍入。Currency将前端的旧类型映射到后端的类型的原因之一DECIMAL(19, 4)Currency表现出银行家的四舍五入性质,而DECIMAL(p, s)截断四舍五入。

存储中的额外小数位DECIMAL允许实现自定义舍入算法,而不是采用供应商的默认值(至少可以说,对于期望所有以 0.5 结尾的值从零舍入的设计人员来说,银行家的舍入令人担忧) .

是的,DECIMAL(24, 8)对我来说听起来有点矫枉过正。大多数货币的报价精确到小数点后四位或五位。我知道需要小数位数 8(或更多)的情况,但这是按比例分配“正常”货币金额(例如小数点后四位)的情况,这意味着小数精度应相应降低(也考虑这种情况下的浮点类型)。现在没有人有那么多钱来要求小数精度为 24 :)

然而,一些研究可能是有序的,而不是一刀切的方法。向您的设计师或领域专家询问可能适用的会计规则:GAAP、欧盟等。我模糊地回忆起一些欧盟国家内转移,其中明确规定了四舍五入到小数点后五位,因此DECIMAL(p, 6)用于存储。会计师通常似乎偏爱小数点后四位。


PS 避免使用 SQL Server 的MONEY数据类型,因为它在舍入时存在严重的准确性问题,以及可移植性等其他考虑因素。请参阅Aaron Bertrand 的博客


微软和语言设计师选择了银行家的四舍五入,因为硬件设计师选择了它[引文?]。例如,它被载入电气和电子工程师协会 (IEEE) 标准。硬件设计师之所以选择它,是因为数学家更喜欢它。见维基百科;释义:1906 年版的《概率与错误理论》将此称为“计算机规则”(“计算机”是指执行计算的人)。

于 2008-10-22T08:26:49.220 回答
114

我们最近实施了一个系统,需要处理多种货币的价值并在它们之间进行转换,并通过艰难的方式解决了一些问题。

永远不要使用浮点数来赚钱

浮点算术引入了一些不准确性,直到他们把事情搞砸了才会被注意到。所有值都应存储为整数或固定十进制类型,如果您选择使用固定十进制类型,请确保您准确了解该类型在后台的作用(即,它在内部使用整数还是浮点类型)。

当您确实需要进行计算或转换时:

  1. 将值转换为浮点数
  2. 计算新值
  3. 将数字四舍五入并将其转换回整数

在步骤 3 中将浮点数转换回整数时,不要只转换它 - 首先使用数学函数对其进行舍入。这通常是round,尽管在特殊情况下可能是floorceil。了解差异并谨慎选择。

将数字的类型与值一起存储

如果您只处理一种货币,这对您来说可能不那么重要,但它对我们处理多种货币很重要。我们使用 3 字符代码来表示货币,例如 USD、GBP、JPY、EUR 等。

根据情况,存储:

  • 这个数字是税前还是税后(以及税率是多少)
  • 数字是否是转换的结果(以及它是从什么转换而来的)

了解您正在处理的数字的准确度范围

对于实际值,您希望与货币的最小单位一样精确。这意味着您没有小于一美分、一分钱、一日元、一分等的值。不要无缘无故地存储比这更准确的值。

在内部,您可以选择处理较小的值,在这种情况下,这是一种不同类型的货币值。确保您的代码知道哪个是哪个,并且不会混淆它们。即使在这里也避免使用浮点值。


将所有这些规则加在一起,我们决定了以下规则。在运行代码中,货币使用整数作为最小单位进行存储。

class Currency {
   String code;       //  eg "USD"
   int value;         //  eg 2500
   boolean converted;
}

class Price {
   Currency grossValue;
   Currency netValue;
   Tax taxRate;
}

在数据库中,值以以下格式存储为字符串:

USD:2500

这存储了 25.00 美元的价值。我们能够做到这一点只是因为处理货币的代码不需要在数据库层本身中,因此所有值都可以首先转换到内存中。毫无疑问,其他情况也适用于其他解决方案。


如果我之前没有说清楚,请不要使用浮动!

于 2008-10-22T12:47:39.380 回答
4

在 MySQL 中处理货币时,如果您知道货币值的精度,请使用 DECIMAL(13,2),如果您只想快速获得足够好的近似值,请使用 DOUBLE。因此,如果您的应用程序需要处理高达一万亿美元(或欧元或英镑)的货币价值,那么这应该可行:

DECIMAL(13, 2)

或者,如果您需要遵守GAAP ,请使用:

DECIMAL(13, 4)
于 2014-05-25T13:34:50.443 回答
2

4 位小数可以让您准确地存储世界上最小的货币子单位。如果您需要小额支付(纳米支付?!)的准确性,您可以进一步降低它。

我也更喜欢DECIMALDBMS 特定的货币类型,在应用程序 IMO 中保留这种逻辑会更安全。沿着相同思路的另一种方法是简单地使用 [long] 整数,并在应用程序级别完成格式化为 ¤unit.subunit 以提高人类可读性(¤ = 货币符号)。

于 2008-10-22T08:56:06.040 回答
1

SQL Server 上的货币数据类型在小数点后有四位数字。

来自 SQL Server 2000 联机丛书:

货币数据代表正数或负数的货币量。在 Microsoft® SQL Server™ 2000 中,货币数据使用 money 和 smallmoney 数据类型进行存储。货币数据可以精确到小数点后四位。使用 money 数据类型来存储从 -922,337,203,685,477.5808 到 +922,337,203,685,477.5807 范围内的值(需要 8 个字节来存储一个值)。使用 smallmoney 数据类型存储范围从 -214,748.3648 到 214,748.3647 的值(需要 4 个字节来存储一个值)。如果需要更多的小数位数,请改用小数数据类型。

于 2008-10-22T04:10:49.827 回答
1

如果您使用的是 IBM Informix Dynamic Server,您将拥有一个 MONEY 类型,它是 DECIMAL 或 NUMERIC 类型的次要变体。它始终是定点类型(而 DECIMAL 可以是浮点类型)。您可以指定从 1 到 32 的比例,以及从 0 到 32 的精度(默认为 16 的比例和 2 的精度)。因此,根据您需要存储的内容,您可以使用 DECIMAL(16,2) - 仍然足够大以将美国联邦赤字保持到最接近的美分 - 或者您可以使用更小范围或更多小数位。

于 2008-10-22T06:05:43.933 回答
1

有时您需要花费不到一分钱,并且有些国际货币使用非常大的恶魔。例如,您可能会向您的客户收取每笔交易 0.088 美分的费用。在我的 Oracle 数据库中,列定义为 NUMBER(20,4)

于 2008-10-22T06:11:10.583 回答
1

如果您要在数据库中进行任何类型的算术运算(乘以计费率等),您可能需要比这里的人建议的更精确,原因与您永远不会希望在应用程序代码中使用小于双精度浮点值的任何值。

于 2008-10-22T11:34:07.000 回答
0

我认为在很大程度上,您或您的客户的要求应该决定使用什么精度和规模。例如,对于我正在开发的仅处理英镑货币的电子商务网站,我被要求将其保留为 Decimal(6, 2)。

于 2008-10-22T06:16:16.893 回答
0

这里的答案很晚,但我用过

DECIMAL(13,2)

我的想法是正确的,应该允许最多 99,999,999,999.99。

于 2015-08-24T12:54:16.390 回答