使用浮点数还是小数来表示会计应用程序的美元金额?

时间:2020-03-05 18:53:09  来源:igfitidea点击:

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

我们有什么建议?

浮点数或者小数数据类型应用于美元金额吗?

两者都有哪些利弊?

我们每天的讨论中提到的一个骗局是,当我们计算返回的结果超过两位小数时,必须小心。听起来我们必须将金额四舍五入到小数点后两位。

另一个缺点是所有显示和打印量必须具有显示两个小数位的格式语句。我注意到有几次没有这样做,金额看起来不正确。 (即10.2或者10.2546)

优点是Float仅占用磁盘上的8个字节,而十进制将占用9个字节(十进制12,2)

解决方案

回答

浮点数不是精确的表示形式,例如在添加非常大和非常小的值时,可能会出现精度问题。因此,即使精度问题可能很少出现,也建议将十进制类型用于货币。

为了明确起见,十进制的12,2类型将精确地存储这14位数字,而浮点数则不会,因为它内部使用二进制表示形式。例如,0.01不能精确地用浮点数表示,最接近的表示实际上是0.0099999998

回答

我们是否考虑过使用money-data类型存储美元金额?

关于小数点再占用一个字节的缺点,我会说不关心它。在100万行中,我们将仅再使用1 MB,而如今这些存储非常便宜。

回答

无论我们做什么,都需要注意舍入错误。使用比我们显示的精度更高的精度进行计算。

回答

使用Float赚钱的唯一原因是,如果我们不关心准确的答案。

回答

问你的会计师!他们会因使用浮子而对我们皱眉。像以前发布的一样,仅在不关心准确性的情况下才使用float。尽管在金钱方面我始终会反对。

在会计软件中,不接受浮点数。使用带4个小数点的小数。

回答

首先,我们应该阅读每位计算机科学家应该了解的有关浮点算法的内容。然后,我们应该真正考虑使用某种类型的定点/任意精度数字包(例如java BigNum,python十进制模块),否则我们将遭受很大的伤害。然后确定使用本地SQL十进制类型是否足够。

浮点数/双精度数已存在,以公开现在已经过时的快速x87 fp。如果我们担心计算的准确性和/或者不能完全弥补其局限性,请不要使用它们。

回答

浮点数具有非预期的非理性数字。

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

浮点数实际上以二进制值和2的幂的形式存储。

因此1.5被存储为-1(或者3/2)的3 x 2

使用这些以2为底的指数可以创建一些奇数的无理数,例如:

将1.1转换为float,然后再次将其转换回,结果将类似于:1.0999999999989

这是因为1.1的二进制表示形式实际上是154811237190861 x 2 ^ -47,远远超过了double可以处理的数量。

有关此问题的更多信息,请访问我的博客,但基本上,对于存储,最好使用小数点。

在Microsoft SQL服务器上,我们具有money数据类型,这通常最适合财务存储。精确到小数点后4位。

对于计算,我们更多的是问题,不精确度只是很小的一部分,但是将其放到幂函数中,很快就会变得很重要。

但是,小数对于任何一种数学都不是很好,例如,没有对小数幂的本地支持。

回答

对于我帮助开发的银行系统,我负责系统的"应计利息"部分。每天,我的代码都会计算出当天的余额产生了多少利息。

对于该计算,需要极高的准确性和逼真度(我们使用了Oracle的FLOAT),因此我们可以记录所累积的"十亿分之一美分"。

当涉及"资本化"利息(即将利息退还到帐户中)时,金额四舍五入到一分钱。帐户余额的数据类型为两位小数。 (实际上,它更为复杂,因为它是一种可以在许多小数位工作的多货币系统,但我们总是四舍五入到该货币的"一分钱")。是的,那里有损失和收益的"零碎"部分,但是当计算机数字被实际实现(已付或者已付的钱)时,它始终是真实货币值。

这使会计师,审计师和测试人员满意。

因此,请与客户联系。他们会告诉我们他们的银行/会计规则和惯例。

回答

会计师将要控制四舍五入方式。使用float意味着我们将不断舍入,通常使用FORMAT()类型的语句,这不是我们想要的方式(而是使用floor / ceiling)。

我们有货币数据类型(money,smallmoney),应使用它们而不是float或者real。存储小数(12,2)将消除舍入,但也会在中间步骤中消除舍入,这实际上并不是我们在金融应用程序中所需要的。

回答

始终使用十进制。由于四舍五入的问题,Float会给我们不正确的值。

回答

浮点数只能表示为二进制浮点的基数的负倍之和的数字,当然是2.

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

对于结果的标度很重要的计算,浮点数是一个不错的选择。在试图精确到小数位数的位置时,这是一个错误的选择。

回答

我们可能需要对货币值使用某种形式的定点表示形式。我们还将需要研究Banker的舍入(也称为"半舍入")。它避免了通常的"四舍五入"方法中存在的偏差。

回答

就像其他警告一样,SQL Server和.Net框架使用不同的默认算法进行舍入。确保在Math.Round()中签出MidPointRounding参数。 .Net框架默认情况下使用Bankers算法,而SQL Server使用对称算法舍入。在此处查看Wikipedia文章

回答

Should Float or Decimal data type be used for dollar amounts?

答案很简单。永不漂浮。绝不 !

浮点数根据IEEE 754始终为二进制,只有新的标准IEEE 754R定义了十进制格式。许多小数二进制部分永远不能等于精确的小数
表示。任何二进制数都可以写为m / 2 ^ n(m,n个正整数),任何十进制数都可以写成m /(2 ^ n * 5 ^ n)。由于二进制文件缺少素数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 ...向上(右侧)四舍五入。鉴于浮点计算的结果始终包含较小的误差项,因此该决定纯属偶然。如果要十进制舍入到偶数,它将变得毫无希望
用二进制数字处理。

For that calculation, extreme accuracy and fidelity was required (we used Oracle's
  FLOAT) so we could record the "billionth's of a penny" being accrued.

不服气?我们坚持认为在帐户系统中一切正常吗?
资产负债相等吗?好的,然后获取每个条目的每个给定格式的数字,解析它们并使用独立的十进制系统求和!
将其与格式化的总和进行比较。糟糕,出问题了,不是吗?

回答

无助于解决此错误。因为所有人都会自动假定计算机正确,所以实际上没有人独立检查。

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

比使用小数点更好的是只使用普通的旧整数(或者某种bigint)。这样,我们始终可以获得最高的精度,但是可以指定精度。例如,数字" 100"可能表示" 1.00",其格式如下:

回答

如果我们想获得更高的精度,则可以将100更改为更大的值,例如:10 ^ n,其中n是小数位数。

我们总是可以为.Net编写类似Money类型的内容。

回答

看一下这篇文章:CLR的Money类型在我看来,作者做得非常出色。

使用SQL Server的十进制类型。

不要用钱或者浮动。

回答

货币使用4个小数位,比使用小数要快,但四舍五入会遇到一些明显的问题,以及一些不是那么明显的问题(请参阅此连接问题)

回答

我建议使用64位整数,以分号存储整个数据。

回答

我一直在使用SQL的money类型来存储货币值。最近,我不得不使用许多在线支付系统,并且注意到其中一些使用整数来存储货币值。在当前和新项目中,我已经开始使用整数,对此解决方案我很满意。

这里有点背景...

没有数字系统可以准确地处理所有实数。它们都有其局限性,包括标准的IEEE浮点数和带符号的十进制数。 IEEE浮点数使用的每位精度更高,但这无关紧要。

财务数字基于数百年来的纸笔练习及相关惯例。它们相当准确,但更重要的是,它们是可重现的。两名使用不同编号和费率的会计师应得出相同的编号。任何差异的空间就是欺诈的空间。

回答

因此,对于财务计算,正确的答案是与擅长算术的CPA给出相同答案的一切。这是十进制算法,不是IEEE浮点数。

在会计系统中,我们还应该注意的另一件事是,没有人可以直接访问这些表。这意味着对记帐系统的所有访问都必须通过存储的proc进行。这不仅可以防止欺诈,而且还可以防止SQ1注入攻击。想要进行欺诈的内部用户永远都不能直接更改数据库表中的数据。这是系统上的关键内部控制。我们是否真的要让一些心怀不满的员工进入数据库后端,并让它开始写支票?还是隐瞒他们在没有批准权限的情况下批准了对未经授权的供应商的费用?整个组织中只有两个人应该能够直接访问财务数据库,dba及其备份中的数据。如果我们有许多dbas,则其中只有两个应该具有此访问权限。

回答

我之所以这样说是因为,如果程序员在会计系统中使用了float,那么他们很可能完全不了解内部控制的概念,并且在编程工作中没有考虑到它们。

#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;
}

回答

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

这张照片回答:

回答

这是另一种情况:北安普敦的人收到一封信,说如果他不付零美元零美分,他的家就会被扣押!

这是一篇很棒的文章,描述了何时使用浮点数和十进制。浮点数存储一个近似值,十进制数存储一个精确值。

总而言之,精确值(如货币)应使用小数,而近似值(如科学测量)应使用浮点。

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

这是一个有趣的示例,表明浮点数和十进制数均会丢失精度。当添加非整数的数字然后减去相同的数字时,float会导致精度下降,而十进制则不会:

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

段落数量不匹配