将钱存储在小数列中 - 啥精度和规模? [关闭]
Posted
技术标签:
【中文标题】将钱存储在小数列中 - 啥精度和规模? [关闭]【英文标题】:Storing money in a decimal column - what precision and scale? [closed]将钱存储在小数列中 - 什么精度和规模? [关闭] 【发布时间】:2010-09-18 11:26:04 【问题描述】:我正在使用十进制列在数据库中存储货币值,今天我想知道使用什么精度和比例。
由于据说固定宽度的 char 列效率更高,我认为十进制列也是如此。是吗?
我应该使用什么精度和比例?我在想 24/8 精度。这是矫枉过正,不够还是可以?
这是我决定要做的:
将转换率(如果适用)以浮点数形式存储在交易表本身中 将货币存储在帐户表中 交易金额为DECIMAL(19,4)
所有使用转换率的计算都将由我的应用程序处理,因此我可以控制舍入问题
我认为转换率的浮点数不是问题,因为它主要用于参考,无论如何我都会将其转换为小数。
感谢大家的宝贵意见。
【问题讨论】:
问自己一个问题:真的有必要以十进制形式存储数据吗?我不能将数据存储为美分/便士 -> 整数吗?DECIMAL(19, 4)
是一个流行的选择检查this也检查here世界货币格式来决定使用多少小数位,希望有所帮助。
【参考方案1】:
如果您正在寻找一种万能的,我建议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's blog。
微软和语言设计师选择银行家的四舍五入是因为硬件设计师选择了它 [引文?]。例如,它被载入电气和电子工程师协会 (IEEE) 标准。硬件设计师之所以选择它,是因为数学家更喜欢它。见Wikipedia;套用一句话:1906 年版的《概率与错误理论》将此称为“计算机规则”(“计算机”是指执行计算的人类)。
【讨论】:
See this answer 和 this page 了解更多关于语言设计人员选择 Banker 舍入的原因。 onedaywhen:银行家的四舍五入不是因为微软。 @NickChammas:也不是语言设计师的发明。微软和语言设计师基本上选择了它,因为硬件设计师选择了它;例如,它被载入电气和电子工程师协会 (IEEE) 标准。硬件设计师之所以选择它,是因为数学家更喜欢它。见en.wikipedia.org/wiki/Rounding#History;释义:1906 年版的《概率与错误理论》将此称为“计算机规则”(“计算机”是指执行计算的人)。 @onedaywhen 为什么DECIMAL(19, 4)
比DECIMAL(19, 2)
更受欢迎?大多数世界货币只有小数点后两位。
@zypA13510:是的,我的断言是十年前的事了!但这是我投票最多的答案,并且在我的 SO 代表方面发生了相当大的变化,所以我是 quids in :)
是的,这是一个很好的答案,但现在它不再正确了。如果您正在为加密货币制作软件,那么比特币的 satoshi 是比特币的百万分之一,因此您需要 DECIMAL (19, 8)。如果你使用以太坊,你需要 DECIMAL (38, 18)。【参考方案2】:
我们最近实现了一个系统,该系统需要处理多种货币的值并在它们之间进行转换,并通过艰难的方式解决了一些问题。
切勿使用浮点数来赚钱
浮点运算引入了一些不准确之处,直到他们搞砸了一些东西才可能被注意到。所有值都应存储为整数或固定十进制类型,如果您选择使用固定十进制类型,请确保您准确了解该类型在后台的作用(即,它在内部使用整数还是浮点类型)。
当您确实需要进行计算或转换时:
-
将值转换为浮点数
计算新值
将数字四舍五入并将其转换回整数
在第 3 步中将浮点数转换回整数时,不要只转换它 - 首先使用数学函数对其进行舍入。这通常是round
,但在特殊情况下可能是floor
或ceil
。了解区别,谨慎选择。
在值旁边存储数字的类型
如果您只处理一种货币,这对您来说可能不那么重要,但它对我们处理多种货币很重要。我们为货币使用了 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 美元的价值。我们能够做到这一点只是因为处理货币的代码不需要在数据库层本身中,因此所有值都可以首先转换到内存中。其他情况无疑会适用于其他解决方案。
如果我之前没有说清楚,不要使用浮动!
【讨论】:
永远不要说永远:有时金额是按比例计算的,以后需要重新加起来。示例:将总股息(相对较小)除以已发行股份数量(相对较小)得出每股净额。有时浮动更好:) 我永远支持我。浮点规范有不准确之处,会增加你做的更多计算。如果您需要存储小于一美分或一美分的值,请定义您确实需要的准确度级别并坚持下去。不要使用浮动。严重地。这是个坏主意。 这个答案也符合 Douglas Crockford 在他的“Crockford on javascript 系列”中概述的 javascript 最佳实践,他建议使用 PENNIES 进行所有货币计算,以避免四舍五入时出现机器错误。因此,如果您在 javascript 中使用货币,那么以这种方式存储值非常有意义。 @onedaywhen 在那些每股净额的情况下,您可以将按比例计算的金额相加并与原始总数进行比较,并制定处理剩余部分的策略(使用小数/整数类型时)。 对于 mysql,如果您打算按数量排序,我建议将整数 (2500) 存储为bigint
。在处理大整数时,不要在 php 32bit 上浪费时间,升级到 64bit 或 Node.JS ;)【参考方案3】:
在 MySQL 中处理货币时,如果您知道货币值的精度,请使用 DECIMAL(13,2),如果您只想快速获得足够好的近似值,请使用 DOUBLE。 因此,如果您的应用程序需要处理高达一万亿美元(或欧元或英镑)的货币价值,那么这应该可以:
DECIMAL(13, 2)
或者,如果您需要遵守GAAP,则使用:
DECIMAL(13, 4)
【讨论】:
您能否链接到 GAAP 指南的特定部分而不是 2500 页文档的内容?谢谢。 @ReactingToAngularVues 页面似乎发生了变化。对不起【参考方案4】:4 位小数可以让您准确地存储世界上最小的货币子单位。如果您需要小额支付(纳米支付?!)的准确性,您可以进一步降低它。
我也更喜欢 DECIMAL
而不是 DBMS 特定的货币类型,在应用程序 IMO 中保留这种逻辑会更安全。沿着相同思路的另一种方法是简单地使用 [long] 整数,并在应用程序级别完成格式化为 ¤unit.subunit 以提高人类可读性(¤ = 货币符号)。
【讨论】:
【参考方案5】: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 个字节来存储一个值)。如果需要更多的小数位数,请改用小数数据类型。
【讨论】:
【参考方案6】:如果您使用的是 IBM Informix Dynamic Server,您将拥有一个 MONEY 类型,它是 DECIMAL 或 NUMERIC 类型的次要变体。它始终是定点类型(而 DECIMAL 可以是浮点类型)。您可以指定从 1 到 32 的比例,以及从 0 到 32 的精度(默认为 16 的比例和 2 的精度)。因此,根据您需要存储的内容,您可以使用 DECIMAL(16,2) - 仍然足够大以将美国联邦赤字保持到最接近的美分 - 或者您可以使用更小范围或更多小数位。
【讨论】:
【参考方案7】:有时您需要花费不到一美分,而且有些国际货币使用非常大的贬值。例如,您可能会向客户收取每笔交易 0.088 美分的费用。在我的 Oracle 数据库中,列定义为 NUMBER(20,4)
【讨论】:
【参考方案8】:如果您要在数据库中进行任何类型的算术运算(乘以计费费率等),您可能需要比这里的人们建议的更精确,原因与您'd 永远不想在应用程序代码中使用小于双精度浮点值的任何值。
【讨论】:
这就是我的想法,但在汇率方面(即,将津巴布韦元兑换成美元)。我将对我正在使用的数据库(psql、sqlite)进行一些实验,以了解它们如何处理非常小的小数的舍入。 另外,浮点数在某些 dbms/语言中是否存在准确性问题? 浮点数在所有语言中都存在准确性问题。 现在最常见的建议是使用任意精度(想想BigDecimal
),但很长一段时间它都是双精度(想想double
而不是float
)。此外,在某些情况下,任意精度会导致显着的性能损失。测试绝对是正确的方法。【参考方案9】:
我认为在很大程度上,您或您的客户的要求应该决定使用什么精度和规模。例如,对于我正在开发的仅处理英镑货币的电子商务网站,我被要求将其保留为 Decimal(6, 2)。
【讨论】:
【参考方案10】:这里回答晚了,但我用过
DECIMAL(13,2)
我的想法是正确的,应该允许最多 99,999,999,999.99。
【讨论】:
以上是关于将钱存储在小数列中 - 啥精度和规模? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章