会计申请美元金额使用浮点数还是小数点?

我们正在重写VB.NET和SQL Server中的传统会计系统。 我们引入了一个新的.NET / SQL程序员团队来进行重写。 该系统的大部分已经完成与美元金额使用浮动。 我编程的遗留系统语言没有浮点数,所以我可能会使用十进制数。

你的build议是什么?

应该使用浮点数还是小数点数据types?

这两者的优点和缺点是什么?

一个骗局在我们的日常scrum中提到,当你计算一个返回结果超过两位小数的数量时,你必须小心。 这听起来像你将不得不四舍五入小数点后两位。

另一个Con是所有的显示和打印量必须有格式声明,显示两个小数位。 我注意到几次没有完成,数额看起来不正确。 (即10.2或10.2546)

一个职业是浮动只占用8个字节在十进制将占用9个字节(十进制12,2)

应该使用浮点数还是小数点数据types?

答案很简单。 永远不会漂浮。 永远不要

按照IEEE 754总是二进制浮点数 ,只有新的标准IEEE 754R定义了十进制格式。 许多分数二进制部分不能等于精确的十进制表示。 任何二进制数都可以写成m / 2 ^ n(m,n个正整数),任何十进制数都是m /(2 ^ n * 5 ^ n)。 由于二进制文件缺less主要因素5,所有二进制数字都可以用小数来表示,但反之亦然。

0.3 = 3 /(2 ^ 1 * 5 ^ 1)= 0.3

0.3 = [0.25 / 0.5] [0.25 / 0.375] [0.25 / 3.125] [0.2825 / 3.125]

1/4 1/8 1/16 1/32 

所以你最终得到的数字要么高于给定的十进制数,要么低于给定的十进制数。 总是。

为什么这很重要? 四舍五入。 正常舍入意味着0..4下,5..9上。 所以,如果结果是0.049999999999 ….或0.0500000000 …,你可能知道这意味着5分,但电脑不知道,并且0.4999 …下(错误)和0.5000 .. (右)。 鉴于浮点计算的结果总是包含小的错误项,这个决定是纯粹的运气。 如果你想用二进制数字进行小数循环处理,那么它就没有希望了。

不服气? 你坚持认为,在你的账户系统中,一切都很好? 资产和负债相等吗? 好吧,然后把每一个给定的格式化数字的每个条目,parsing它们,并将它们与一个独立的十进制系统! 将其与格式化的总和进行比较。 哎呀,有什么不对的,不是吗?

为了这个计算,需要极高的准确性和逼真度(我们使用Oracle的FLOAT),所以我们可以logging下“十亿分之一便士”。

不利于这个错误。 因为所有人都自动假定电脑正确,实际上没有人独立检查。

这张照片回答:

照片1,C.O。

这是另外一种情况: 北安普顿的一位男士收到一封信,说如果他不支付零美元和零美分,他的住房将被扣押!

照片2,C.O。

首先,你应该阅读这个每个计算机科学家应该知道的浮点运算 。 那么你应该真的考虑使用某种types的定点/任意精度数字包(例如java BigNum,python decimal模块),否则你将会受到伤害。 然后找出是否使用本机SQL十进制types就足够了。

浮动/双打存在(编辑)揭露快速x87 fp现在已经非常陈旧。 如果您关心计算的准确性和/或不完全弥补它们的限制,请不要使用它们。

正如一个额外的警告,SQL Server和.Net框架使用不同的默认algorithm进行舍入。 请确保您检查Math.Round()中的MidPointRounding参数。 .Net框架默认使用Bankersalgorithm,SQL Server使用对称algorithm舍入。 查看这里的维基百科文章

问你的会计师! 他们会因为使用浮游物而皱眉。 就像之前发布的一样,如果你不关心准确性,只使用float。 虽然我会一直反对它的钱。

在会计软件是不可接受的一个浮动。 使用4位小数的小数点。

浮点数有意想不到的不合理数字。

例如,你不能存储1/3作为小数,它将是0.3333333333 …(依此类推)

实际上浮点数是作为二进制值存储的,并且是2次方的幂。

所以1.5被存储为3 x 2到-1(或3/2)

使用这些基2指数会创build一些奇数的非理性数字,例如:

将1.1转换为一个浮点数,然后再将其转换回来,结果如下:1.0999999999989

这是因为1.1的二进制表示实际上是154811237190861 x 2 ^ -47,比一个double可以处理多了。

更多关于这个问题在我的博客 ,但基本上,存储,你最好用小数。

在Microsoft SQL服务器上,您money数据types – 这通常最适合于财务存储。 精确到4位小数位。

对于计算,你有更多的问题 – 不准确只是一小部分,但把它放到一个幂函数,它很快变得重要。

然而,小数对于任何一种math都不是很好 – 例如,对小数的function没有本地支持。

我build议使用64位整数存储在美分的整个事情。

这里有一点背景….

没有一个号码系统可以准确处理所有的实际号 都有其局限性,这包括标准的IEEE浮点和带符号的十进制。 IEEE浮点使用比特更准确,但在这里并不重要。

财务数据是基于几个世纪的纸笔练习和相关的惯例。 他们是相当准确的,但更重要的是,他们是可重复的。 两位会计师使用不同的数字和费率应该拿出相同的数字。 任何差异的空间是欺诈的空间。

因此,对于财务计算而言,正确的答案就是提供与算术优秀的注册会计师相同的答案。 这是十进制算术,而不是IEEE浮点。

如果你不关心准确的答案,使用浮动金钱的唯一原因是。

浮点数不是精确表示,精度问题是可能的,例如添加非常大和非常小的值时。 这就是为什么十进制types被推荐用于货币的原因,尽pipe精确度问题可能非常less见。

为了澄清,十进制12,2types将精确地存储这14位数字,而浮动将不会因为它在内部使用二进制表示。 例如,0.01不能完全由浮点数表示 – 最接近的表示实际上是0.0099999998

对于我帮助发展的银行体系,我负责系统的“利息收益”部分。 每天,我的代码都会计算当天余额的累计利息。

为了这个计算,需要极高的准确性和逼真度(我们使用Oracle的FLOAT),所以我们可以logging下“十亿分之一便士”。

当把利息“资本化”时(即把利息还给你的账户),这笔钱就被四舍五入了。 账户余额的数据types是小数点后两位。 (事实上​​,这是一个复杂的货币体系,可以在许多小数点后面工作 – 但我们总是四舍五入到货币的“一分钱”)。 是的 – 那里有损失和收益的“分数”,但是当计算机数字被实际化(支付或支付的金钱)时,它总是真正的货币价值。

这满足了会计师,审计师和testing人员。

所以,请检查你的客户。 他们会告诉你他们的银行/会计规则和做法。

使用SQL服务器的十进制types。

不要用浮动

钱使用​​小数点后4位,比使用小数点快, 但是有一些明显的和一些不太明显的舍入问题( 见这个连接问题 )

在会计系统中应该注意的另一件事是,没有人应该直接访问表。 这意味着所有对会计系统的访问必须通过存储的特效。 这是防止欺诈不仅仅是SQl注入攻击。 想要进行欺诈的内部用户不应该有能力直接更改数据库表中的数据。 这是您系统上的一个关键内部控制。 你真的想要一些心存不满的员工去你的数据库的后端,并开始对他们的支票? 或者当他们没有批准权限时,隐藏他们批准了未经授权的供应商的费用? 您整个组织中只有两个人应该能够直接访问您的财务数据库中的数据,您的dba和他的备份。 如果你有很多dbas,只有两个应该有这个权限。

我提到这一点,因为如果你的程序员在会计系统中使用float,他们可能完全不熟悉内部控制的想法,并没有考虑到他们的编程工作。

甚至比使用小数更好的是使用普通的旧整数(或者某种bigint)。 这样你总是有最高的准确度,但是可以指定精度。 例如,数字100可能意味着1.00 ,格式如下:

 int cents = num % 100; int dollars = (num - cents) / 100; printf("%d.%02d", dollars, cents); 

如果你想要更精确的话,你可以把100改成一个更大的值,比如:10 ^ n,其中n是小数位数。

你总是可以为.Net写一个Moneytypes的东西。

看看这篇文章: CLR的钱types – 笔者在我看来做了一个很好的工作。

我一直在使用SQL的钱types来存储货币价值。 最近,我不得不使用一些在线支付系统,并注意到其中一些使用整数来存储货币价值。 在我目前和新的项目中,我已经开始使用整数,而且我非常满意这个解决scheme。

在100个分数n / 100中,其中n是0 <= n且n <100的自然数,只有4个可以表示为浮点数。 看看这个C程序的输出:

 #include <stdio.h> int main() { printf("Mapping 100 numbers between 0 and 1 "); printf("to their hexadecimal exponential form (HEF).\n"); printf("Most of them do not equal their HEFs. That means "); printf("that their representations as floats "); printf("differ from their actual values.\n"); double f = 0.01; int i; for (i = 0; i < 100; i++) { printf("%1.2f -> %a\n",f*i,f*i); } printf("Printing 128 'float-compatible' numbers "); printf("together with their HEFs for comparison.\n"); f = 0x1p-7; // ==0.0071825 for (i = 0; i < 0x80; i++) { printf("%1.7f -> %a\n",f*i,f*i); } return 0; } 

您是否考虑过使用货币数据types来存储美元数额?

关于小数再占用一个字节的Con,我会说不关心它。 在100万行中,您只能使用1 MB以上,而且这些日子的存储非常便宜。

无论你做什么,你都需要小心四舍五入的错误。 使用比您显示的更高的精度计算。

您可能会想要使用货币值的某种forms的定点表示。 您也将要调查银行家的四舍五入(也称为“圆半”)。它避免了通常的“半轮”方法中存在的偏差。

你的会计师会想要控制你的方式。 使用float意味着你会不断地四舍五入,通常是用一个FORMAT()types的语句,这不是你想要的方式(使用floor / ceiling代替)。

你有货币数据types(钱,smallmoney),应该使用,而不是浮动或真实的。 存储十进制(12,2)将消除你的舍入,但也将在中间步骤中消除它们 – 这实际上不是你在金融应用程序中想要的。

总是使用十进制。 由于四舍五入问题,Float会给你不准确的价值。

浮点数只能表示基数的负数倍数的数字 – 对于二进制浮点数,当然这是两个数。

在二进制浮点中只能精确地表示四个小数:0,0.25,0.5和0.75。 其他的一切都是近似的,就像0.3333 …是十进制算术中1/3的近似值一样。

对于计算结果的规模是重要的,浮点是一个很好的select。 这是一个不错的select,你想要精确到一些小数位数。

这是一篇很好的文章,描述何时使用浮点数和小数 。 Float存储一个近似值,小数存储一个确切的值。

总之,像货币这样的精确值应该使用十进制,而像科学测量这样的近似值应该使用浮点数。

这是一个有趣的例子,表明float和decimal都能够失去精度。 当添加一个不是整数的数字,然后减去相同的数字时,float会导致精度下降,而decimal不会:

  DECLARE @Float1 float, @Float2 float, @Float3 float, @Float4 float; SET @Float1 = 54; SET @Float2 = 3.1; SET @Float3 = 0 + @Float1 + @Float2; SELECT @Float3 - @Float1 - @Float2 AS "Should be 0"; Should be 0 ---------------------- 1.13797860024079E-15 

当乘以一个非整数并除以相同的数字时,小数点将失去精度,而浮点数则不会。

 DECLARE @Fixed1 decimal(8,4), @Fixed2 decimal(8,4), @Fixed3 decimal(8,4); SET @Fixed1 = 54; SET @Fixed2 = 0.03; SET @Fixed3 = 1 * @Fixed1 / @Fixed2; SELECT @Fixed3 / @Fixed1 * @Fixed2 AS "Should be 1"; Should be 1 --------------------------------------- 0.99999999999999900 
    Interesting Posts