在十进制列中存储货币 - 精度和规模是多少?

Iva*_*van 165 sql database database-design currency

我正在使用十进制列来存储数据库中的货币值,而今天我想知道要使用的精度和规模.

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

我应该使用什么精度和规模?我正在考虑精确24/8.这有点矫枉过正,还不够或没问题?


这就是我决定做的事情:

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

我不认为转换率的浮点数是一个问题,因为它主要用于参考,我将把它转换为十进制.

谢谢大家的宝贵意见.

one*_*hen 165

如果你正在寻找DECIMAL(19, 4)一个通用的,我建议是一个受欢迎的选择(快速谷歌承担这一点).我认为这源于旧的VBA/Access/Jet Currency数据类型,是该语言中的第一个定点十进制类型; Decimal在VB6/VBA6/Jet 4.0中只有'版本1.0'样式(即未完全实现).

存储固定点十进制值的经验法则是存储至少比您实际需要的小数位数多一个以允许舍入.Currency将前端的旧类型映射到后端的DECIMAL(19, 4)类型的原因之一是Currency银行家本质上是DECIMAL(p, s)圆形的,而截断则是圆形的.

存储中的额外小数位DECIMAL允许实现自定义舍入算法而不是采用供应商的默认值(并且银行家的舍入是令人震惊的,至少可以说,设计师期望所有以.5结尾的值从零开始舍入) .

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

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


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


微软和语言设计师选择了银行家的四舍五入,因为硬件设计师选择了它[引用?].例如,它被载入电气和电子工程师协会(IEEE)标准.硬件设计师选择它是因为数学家更喜欢它.见维基百科 ; 换句话说:1906年版的"概率论和错误论"称这是"计算机的规则"("计算机"意味着执行计算的人).

  • 那么比特币应该用什么呢? (5认同)
  • 是的,这是一个很好的答案,但现在不再如此。如果您正在为加密货币制作软件,那么比特币的聪是比特币的百万分之一,因此您需要 DECIMAL (19, 8)。如果您使用以太坊,则需要 DECIMAL (38, 18)。 (4认同)
  • @onedaywhen 为什么 `DECIMAL(19, 4)` 比 `DECIMAL(19, 2)` 更受欢迎?大多数世界货币只有小数点后两位。 (3认同)
  • @ zypA13510:是的,十年前我的断言就是这样!但这是我投票的第三大回答,并且考虑到我的SO代表有很大一部分变化,所以我在这里回答:) (3认同)
  • [请参阅此答案](http://stackoverflow.com/a/311704/877069) 和 [此页面](http://www.xbeat.net/vbspeed/i_BankersRounding.htm) 了解更多关于语言设计者选择 Banker 的原因四舍五入。 (2认同)
  • onedaywhen:银行家的四舍五入并非由微软造成。@NickChammas:它也不是语言设计师的发明。微软和语言设计师基本上选择了它,因为硬件设计师选择了它;例如,它被纳入电气和电子工程师协会 (IEEE) 标准中。硬件设计师选择它是因为数学家更喜欢它。请参阅http://en.wikipedia.org/wiki/Rounding#History;释义:1906 年版的《概率与误差论》将其称为“计算机规则”(“计算机”指的是执行计算的人类)。 (2认同)

Mar*_*ing 97

我们最近实现了一个系统,它需要处理多种货币的价值并在它们之间进行转换,并且很难找到一些东西.

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

浮点运算引入了一些不准确的问题,直到他们搞砸了一些东西才会被注意到.所有值都应该存储为整数或固定小数类型,如果您选择使用固定小数类型,那么请确保您确切了解该类型的内容(即内部使用整数或浮点数)类型).

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

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

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

将值的类型与值一起存储

如果您只处理一种货币,这对您来说可能不那么重要,但对我们来说处理多种货币非常重要.我们使用3字符代码表示货币,例如美元,英镑,日元,欧元等.

根据具体情况,存储可能也会有所帮助:

  • 税号是税前还是税后(以及税率是多少)
  • 该数字是否是转换的结果(以及转换后的数字)

知道你正在处理的数字的准确性界限

对于实际值,您希望与货币的最小单位一样精确.这意味着您没有小于1美分,1美分,日元,分钟等的值.不要存储比无理由更高精度的值.

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


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

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

class Price {
   Currency grossValue;
   Currency netValue;
   Tax taxRate;
}
Run Code Online (Sandbox Code Playgroud)

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

USD:2500
Run Code Online (Sandbox Code Playgroud)

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


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

  • ++建议永远不要使用浮点货币. (31认同)
  • 我坚持不懈.浮点规范具有不准确性,这将使您执行的计算越多.如果您需要存储小于1美分或1美分的值,请定义您需要的准确度级别并坚持下去.不要使用浮动.认真.这是个坏主意. (20认同)
  • 这个答案也与Douglas Crockford在他的"Crockford on JavaScript系列"中概述的javascript最佳实践一致,他建议在PENNIES中进行所有货币计算以避免舍入时出现机器错误.因此,如果您在javascript中使用货币,那么以这种方式存储价值会很有意义. (2认同)
  • @onedaywhen 在这些净每股情况下,您可以将按比例的金额相加并与原始总额进行比较,并设计处理剩余部分的策略(当使用小数/整数类型时)。 (2认同)
  • 对于Mysql,如果您打算按金额排序,我建议将整数(2500)存储为"bigint".在处理大整数时,不要浪费你的时间使用PHP 32bit,升级到64位或Node.JS;) (2认同)

pol*_*1er 5

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

DECIMAL(13, 2)
Run Code Online (Sandbox Code Playgroud)

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

DECIMAL(13, 4)
Run Code Online (Sandbox Code Playgroud)

  • 您能否链接到 GAAP 指南的_具体_部分,而不是 2500 页文档的内容?谢谢。 (9认同)