使用浮点数还是小数来计算会计应用程序的美元金额?
我们正在 VB.NET 和 SQL Server 中重写旧的会计系统。 我们引入了一个新的 .NET/SQL 程序员团队来进行重写。 系统的大部分已经完成,美元金额使用浮动。 我编程时使用的遗留系统语言没有浮点数,因此我可能会使用小数。
你的建议是什么?
美元金额应该使用浮点数还是小数数据类型?
两者都有哪些优点和缺点?
我们的每日 scrum 中提到了一个缺点当您计算返回的结果超过两位小数的金额时,您必须小心。 听起来您必须将金额四舍五入到小数点后两位。
另一个缺点是所有显示和打印的金额都必须有一个显示两位小数的格式声明。 我注意到有几次没有这样做,而且金额看起来不正确。 (即 10.2 或 10.2546)
pro 是仅浮点方法,在磁盘上占用 8 个字节,其中小数将占用 9 个字节(十进制 12,2)。
We are rewriting our legacy accounting system in VB.NET and SQL Server. We brought in a new team of .NET/ SQL Programmers to do the rewrite. Most of the system is already completed with the dollar amounts using floats. The legacy system language, I programmed in, did not have a float, so I probably would have used a decimal.
What is your recommendation?
Should the float or decimal data type be used for dollar amounts?
What are some of the pros and cons for either?
One con mentioned in our daily scrum was you have to be careful when you calculate an amount that returns a result that is over two decimal positions. It sounds like you will have to round the amount to two decimal positions.
Another con is all displays and printed amounts have to have a format statement that shows two decimal positions. I noticed a few times where this was not done and the amounts did not look correct. (i.e. 10.2 or 10.2546)
A pro is the float-only approach takes up eight bytes on disk where the decimal would take up nine bytes (decimal 12,2).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(24)
浮点数只能表示基数负倍数之和的数字 - 对于二进制浮点数来说,当然是二。
二进制浮点数只能精确表示四种小数:0、0.25、0.5 和 0.75。 其他一切都是近似值,就像 0.3333... 是十进制算术中 1/3 的近似值一样。
对于结果规模很重要的计算来说,浮点是一个不错的选择。 当您试图精确到小数点后的几位时,这是一个糟糕的选择。
Floating point numbers can only represent numbers that are a sum of negative multiples of the base - for binary floating point, of course, that's two.
There are only four decimal fractions representable precisely in binary floating point: 0, 0.25, 0.5 and 0.75. Everything else is an approximation, in the same way that 0.3333... is an approximation for 1/3 in decimal arithmetic.
Floating point is a good choice for computations where the scale of the result is what is important. It's a bad choice where you're trying to be accurate to some number of decimal places.
这是一篇优秀的文章,描述了何时使用浮点数和小数。 浮点数存储近似值,小数存储精确值。
总之,像金钱这样的精确值应该使用小数,而像科学测量这样的近似值应该使用浮点数。
这是一个有趣的示例,表明浮点型和小数型都可能会丢失精度。 当添加一个非整数的数字,然后减去相同的数字时,浮点数会导致精度损失,而小数不会:
当乘以非整数并除以相同的数字时,小数会损失精度,而浮点数则不会。
This is an excellent article describing when to use float and decimal. Float stores an approximate value and decimal stores an exact value.
In summary, exact values like money should use decimal, and approximate values like scientific measurements should use float.
Here is an interesting example that shows that both float and decimal are capable of losing precision. When adding a number that is not an integer and then subtracting that same number float results in losing precision while decimal does not:
When multiplying a non integer and dividing by that same number, decimals lose precision while floats do not.
您的会计师会想要控制您的舍入方式。 使用 float 意味着您将不断舍入,通常使用
FORMAT()
类型语句,这不是您想要的方式(使用floor
/ <代码>天花板代替)。您有货币数据类型(
money
、smallmoney
),应该使用它们而不是 float 或 real。 存储小数 (12,2) 将消除您的舍入,但也会在中间步骤中消除它们 - 这实际上不是您在财务应用程序中想要的。Your accountants will want to control how you round. Using float means that you'll be constantly rounding, usually with a
FORMAT()
type statement, which isn't the way you want to do it (usefloor
/ceiling
instead).You have currency datatypes (
money
,smallmoney
), which should be used instead of float or real. Storing decimal (12,2) will eliminate your roundings, but will also eliminate them during intermediate steps - which really isn't what you'll want at all in a financial application.始终使用十进制。 由于舍入问题,浮动会给您提供不准确的值。
Always use Decimal. Float will give you inaccurate values due to rounding issues.
您可能希望使用某种形式的定点表示形式来表示货币值。 您还需要研究银行家舍入(也称为“四舍五入”) )。 它避免了通常的“四舍五入”方法中存在的偏差。
You will probably want to use some form of fixed point representation for currency values. You will also want to investigate banker's rounding (also known as "round half to even"). It avoids bias that exist in the usual "round half up" method.
您始终可以为 .NET 编写类似 Money 类型的内容。
请查看这篇文章:CLR 的 Money 类型。 在我看来,作者做得非常出色。
You can always write something like a Money type for .NET.
Take a look at this article: A Money type for the CLR. The author did an excellent work in my opinion.
100 个分数中的 n/100,其中 n 是自然数,使得 0 <= n 且 n < 100,只有四位可以表示为浮点数。 看一下这个 C 程序的输出:
Out of the 100 fractions n/100, where n is a natural number such that 0 <= n and n < 100, only four can be represented as floating point numbers. Take a look at the output of this C program:
我一直在使用 SQL 的货币类型来存储货币值。 最近,我不得不使用许多在线支付系统,并注意到其中一些系统使用整数来存储货币值。 在我当前和新的项目中,我已经开始使用整数,并且我对这个解决方案非常满意。
I had been using SQL's money type for storing monetary values. Recently, I've had to work with a number of online payment systems and have noticed that some of them use integers for storing monetary values. In my current and new projects I've started using integers and I'm pretty content with this solution.
无论您做什么,都需要小心舍入错误。 使用比显示的精度更高的精度进行计算。
Whatever you do, you need to be careful of rounding errors. Calculate using a greater degree of precision than you display in.
您是否考虑过使用货币数据类型来存储美元金额?
关于十进制多占用一个字节的缺点,我想说不要关心它。 在 100 万行中,您将仅多使用 1 MB,而且如今存储非常便宜。
Have you considered using the money-data type to store dollar-amounts?
Regarding the con that decimal takes up one more byte, I would say don't care about it. In 1 million rows you will only use 1 more MB and storage is very cheap these days.
浮点数有意想不到的无理数。
例如,您不能将 1/3 存储为小数,它将是 0.3333333333...(等等)
浮点数实际上存储为二进制值和 2 指数的幂。
因此 1.5 存储为 3 x 2 到 -1(或 3/2)
使用这些以 2 为底的指数创建一些奇数无理数,例如:
将 1.1 转换为浮点数,然后再次将其转换回来,你的结果将是比如: 1.0999999999989
这是因为 1.1 的二进制表示实际上是 154811237190861 x 2^-47,超过了 double 可以处理的范围。
有关此问题的更多信息,请访问我的博客,但基本上,对于存储来说,最好使用小数。
在 Microsoft SQL Server 上,您拥有
money
数据类型 - 这通常最适合财务存储。 它精确到小数点后 4 位。对于计算来说,你会遇到更多的问题 - 不准确性只是很小的一部分,但将其放入幂函数中,它很快就会变得很重要。
然而,小数对于任何类型的数学都不是很好 - 例如,没有对小数幂的原生支持。
Floating points have unexpected irrational numbers.
For instance you can't store 1/3 as a decimal, it would be 0.3333333333... (and so on)
Floats are actually stored as a binary value and a power of 2 exponent.
So 1.5 is stored as 3 x 2 to the -1 (or 3/2)
Using these base-2 exponents create some odd irrational numbers, for instance:
Convert 1.1 to a float and then convert it back again, your result will be something like: 1.0999999999989
This is because the binary representation of 1.1 is actually 154811237190861 x 2^-47, more than a double can handle.
More about this issue on my blog, but basically, for storage, you're better off with decimals.
On Microsoft SQL server you have the
money
data type - this is usually best for financial storage. It is accurate to 4 decimal positions.For calculations you have more of a problem - the inaccuracy is a tiny fraction, but put it into a power function and it quickly becomes significant.
However decimals aren't very good for any sort of maths - there's no native support for decimal powers, for instance.
使用 Float 赚钱的唯一原因是您不关心准确的答案。
The only reason to use Float for money is if you don't care about accurate answers.
使用 SQL Server 的十进制类型。
不要使用金钱或浮动。
money 使用四位小数,比使用小数更快,但是 在舍入方面遇到一些明显和不那么明显的问题(查看此连接问题)。
Use SQL Server's decimal type.
Do not use money or float.
money uses four decimal places and is faster than using decimal, but suffers from some obvious and some not so obvious problems with rounding (see this connect issue).
我建议使用 64 位整数来存储整个内容(以分为单位)。
I'd recommend using 64-bit integers that store the whole thing in cents.
在会计系统中您应该注意的另一件事是,任何人都不应直接访问表格。 这意味着对会计系统的所有访问都必须通过存储过程。
这是为了防止欺诈,而不仅仅是 SQL 注入攻击。 想要进行欺诈的内部用户永远不应该有能力直接更改数据库表中的数据。 这是您系统上的关键内部控制。
您真的希望一些心怀不满的员工进入您的数据库后端并让它开始给他们写支票吗? 或者在没有批准权限的情况下隐藏他们批准了未经授权的供应商的费用? 整个组织中只有两个人能够直接访问财务数据库中的数据:数据库管理员 (DBA) 及其备份。 如果您有许多 DBA,则只有其中两人应具有此访问权限。
我提到这一点是因为,如果您的程序员在会计系统中使用浮点数,他们可能完全不熟悉内部控制的概念,并且在编程工作中没有考虑它们。
Another thing you should be aware of in accounting systems is that no one should have direct access to the tables. This means all access to the accounting system must be through stored procedures.
This is to prevent fraud, not just SQL injection attacks. An internal user who wants to commit fraud should not have the ability to directly change data in the database tables, ever. This is a critical internal control on your system.
Do you really want some disgruntled employee to go to the backend of your database and have it start writing them checks? Or hide that they approved an expense to an unauthorized vendor when they don't have approval authority? Only two people in your whole organization should be able to directly access data in your financial database, your database administrator (DBA) and his backup. If you have many DBAs, only two of them should have this access.
I mention this because if your programmers used float in an accounting system, likely they are completely unfamiliar with the idea of internal controls and did not consider them in their programming effort.
浮点数不是精确的表示,可能存在精度问题,例如在添加非常大和非常小的值时。 这就是为什么建议货币使用小数类型,即使精度问题可能非常罕见。
澄清一下,十进制 12,2 类型将准确存储这 14 位数字,而浮点数则不会,因为它内部使用二进制表示形式。 例如,0.01 不能用浮点数精确表示 - 最接近的表示实际上是 0.0099999998
Floats are not exact representations, precision issues are possible, for example when adding very large and very small values. That's why decimal types are recommended for currency, even though the precision issue may be sufficiently rare.
To clarify, the decimal 12,2 type will store those 14 digits exactly, whereas the float will not as it uses a binary representation internally. For example, 0.01 cannot be represented exactly by a floating point number - the closest representation is actually 0.0099999998
对于我帮助开发的银行系统,我负责系统的“应计利息”部分。 每天,我的代码都会计算当天余额应计(赚取)了多少利息。
对于该计算,需要极高的准确性和保真度(我们使用 Oracle 的 FLOAT),以便我们可以记录应计的“十亿分之一便士”。
当涉及到利息“资本化”(即将利息返还到您的帐户)时,金额四舍五入到美分。 帐户余额的数据类型为两位小数。 (事实上,它更复杂,因为它是一个多货币系统,可以在许多小数位上工作 - 但我们总是四舍五入到该货币的“便士”)。 是的 - 存在损失和收益的“分数”,但是当计算机数字被实现(支付或支付的钱)时,它始终是真实的货币价值。
这让会计师、审计师和测试人员感到满意。
因此,请咨询您的客户。 他们会告诉您他们的银行/会计规则和惯例。
For a banking system I helped develop, I was responsible for the "interest accrual" part of the system. Each day, my code calculated how much interest had been accrued (earnt) on the balance that day.
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.
When it came to "capitalising" the interest (ie. paying the interest back into your account) the amount was rounded to the penny. The data type for the account balances was two decimal places. (In fact it was more complicated as it was a multi-currency system that could work in many decimal places - but we always rounded to the "penny" of that currency). Yes - there where "fractions" of loss and gain, but when the computers figures were actualised (money paid out or paid in) it was always REAL money values.
This satisfied the accountants, auditors and testers.
So, check with your customers. They will tell you their banking/accounting rules and practices.
比使用小数更好的是仅使用普通的旧整数(或者可能是某种 bigint)。 这样您始终可以获得尽可能高的准确度,但可以指定精度。 例如,数字
100
可能表示1.00
,其格式如下:如果您想要更精确,可以将 100 更改为更大的值,例如:10 ^ n,其中 n 是小数位数。
Even better than using decimals is using just plain old integers (or maybe some kind of bigint). This way you always have the highest accuracy possible, but the precision can be specified. For example the number
100
could mean1.00
, which is formatted like this:If you like to have more precision, you can change the 100 to a bigger value, like: 10 ^ n, where n is the number of decimals.
答案很简单。 永远不会漂浮。 永远!
浮点数按照 IEEE 754 始终为二进制,仅新标准 < a href="http://www.intel.com/technology/itj/2007/v11i1/s2-decimal/1-sidebar.htm" rel="nofollow noreferrer">IEEE 754R 定义的十进制格式。 许多小数二进制部分永远不能等于精确的十进制表示形式。
任何二进制数都可以写成
m/2^n
(m
,n
正整数),任何十进制数可以写成m/ (2^n*5^n)
。由于二进制缺少质数
因子5
,所有二进制数都可以用小数精确表示,但反之则不然。所以你最终会得到一个比给定的十进制数更高或更低的数字。 总是。
为什么这很重要? 四舍五入。
正常舍入意味着向下 0..4,向上 5..9。 所以结果是否确实很重要
0.049999999999
.... 或0.0500000000
... 您可能知道这意味着 5 美分,但计算机不知道这一点并舍入0.4999
code>... 向下(错误)和0.5000
... 向上(正确)。鉴于浮点计算的结果总是包含小误差项,这个决定纯粹是运气。 如果你想用二进制数进行十进制舍入到偶数处理,那就毫无希望了。
不服气? 您坚持认为您的账户系统一切正常?
资产和负债相等吗? 好的,然后取出每个条目的每个给定格式化数字,解析它们并用独立的十进制系统将它们求和!
将其与格式化的总和进行比较。 哎呀,有什么不对劲,不是吗?
它无助于解决此错误。 因为所有人都会自动认为计算机的求和是正确的,并且实际上没有人独立检查。
The answer is easy. Never floats. NEVER!
Floats were according to IEEE 754 always binary, only the new standard IEEE 754R defined decimal formats. Many of the fractional binary parts can never equal the exact decimal representation.
Any binary number can be written as
m/2^n
(m
,n
positive integers), any decimal number asm/(2^n*5^n)
.As binaries lack the prime
factor 5
, all binary numbers can be exactly represented by decimals, but not vice versa.So you end up with a number either higher or lower than the given decimal number. Always.
Why does that matter? Rounding.
Normal rounding means 0..4 down, 5..9 up. So it does matter if the result is
either
0.049999999999
.... or0.0500000000
... You may know that it means 5 cent, but the the computer does not know that and rounds0.4999
... down (wrong) and0.5000
... up (right).Given that the result of floating point computations always contain small error terms, the decision is pure luck. It gets hopeless if you want decimal round-to-even handling with binary numbers.
Unconvinced? You insist that in your account system everything is perfectly ok?
Assets and liabilities equal? Ok, then take each of the given formatted numbers of each entry, parse them and sum them with an independent decimal system!
Compare that with the formatted sum. Oops, there is something wrong, isn't it?
It doesn't help against this error. Because all people automatically assume that the computer sums right, and practically no one checks independently.
这张照片的答案:
这是另一种情况:来自北安普顿的男子得到了一封信,表明如果他不支付零美元和零美分,他的房子就会被没收!
This photo answers:
This is another situation: man from Northampton got a letter stating his home would be seized if he didn't pay up zero dollars and zero cents!
首先你应该阅读每个计算机科学家都应该了解浮点算术。 那么你应该真正考虑使用某种类型的 定点/任意精度数字 包(例如,Java BigNum 或Python 小数模块)。 否则,你将陷入痛苦的世界。 然后弄清楚使用本机 SQL 十进制类型是否足够。
浮点和双精度存在(编辑)以公开快速的 x87 浮点协处理器,现在非常漂亮已经过时了。 如果您关心计算的准确性和/或不能完全弥补它们的局限性,请不要使用它们。
First you should read What Every Computer Scientist Should Know About Floating Point Arithmetic. Then you should really consider using some type of fixed point / arbitrary-precision number package (e.g., Java BigNum or Python decimal module). Otherwise, you'll be in for a world of hurt. Then figure out if using the native SQL decimal type is enough.
Floats and doubles exist(ed) to expose the fast x87 floating-point coprocessor that is now pretty much obsolete. Don't use them if you care about the accuracy of the computations and/or don't fully compensate for their limitations.
正如附加警告一样,SQL Server 和 .NET 框架使用不同的默认舍入算法。 确保检查 Math.Round() 中的 MidPointRounding 参数。 .NET 框架默认使用银行家舍入,SQL Server 使用对称算法舍入。 请在此处查看维基百科文章。
Just as an additional warning, SQL Server and the .NET framework use a different default algorithm for rounding. Make sure you check out the MidPointRounding parameter in Math.Round(). .NET framework uses bankers' rounding by default and SQL Server uses Symmetric Algorithmic Rounding. Check out the Wikipedia article here.
这里有一些背景……
没有数字系统可以准确地处理所有实数。 所有这些都有其局限性,其中包括标准 IEEE 浮点和有符号十进制。 IEEE 浮点使用的每一位都更准确,但这在这里并不重要。
财务数据基于几个世纪以来的纸笔实践以及相关惯例。 它们相当准确,但更重要的是,它们是可重复的。 两名使用不同数字和费率的会计师应该得出相同的数字。 任何差异的空间都是欺诈的空间。
因此,对于财务计算,正确的答案是与擅长算术的注册会计师给出相同答案的答案。 这是十进制算术,而不是 IEEE 浮点数。
A bit of background here....
No number system can handle all real numbers accurately. All have their limitations, and this includes both the standard IEEE floating point and signed decimal. The IEEE floating point is more accurate per bit used, but that doesn't matter here.
Financial numbers are based on centuries of paper-and-pen practice, with associated conventions. They are reasonably accurate, but, more importantly, they're reproducible. Two accountants working with various numbers and rates should come up with the same number. Any room for discrepancy is room for fraud.
Therefore, for financial calculations, the right answer is whatever gives the same answer as a CPA who's good at arithmetic. This is decimal arithmetic, not IEEE floating point.
问问你的会计师吧! 他们会因为你使用浮动而对你皱眉。 就像大卫·辛格所说的那样,使用如果您不关心准确性,则仅浮动。 尽管在金钱方面我总是反对。
在会计软件中不接受浮点数。 使用具有四位小数的小数。
Ask your accountants! They will frown upon you for using float. Like David Singer said, use float only if you don't care for accuracy. Although I would always be against it when it comes to money.
In accounting software is not acceptable a float. Use decimal with four decimal points.