C# 数学给出错误结果!
我理解这个问题背后的原理,但想到这种情况在我的应用程序中发生并且我需要找到解决方案,这让我很头痛。
double Value = 141.1;
double Discount = 25.0;
double disc = Value * Discount / 100; // disc = 35.275
Value -= disc; // Value = 105.824999999999999
Value = Functions.Round(Value, 2); // Value = 105.82
我使用双精度数来表示相当小的数字。不知何故,在计算 141.1 - 35.275 时,结果的二进制表示给出的数字仅为 0.0000000000001。不幸的是,由于我随后对这个数字进行四舍五入,因此给出了错误的答案。
我读过有关使用小数而不是双精度的内容,但我无法用小数替换双精度的每个实例。有没有更简单的方法来解决这个问题?
I understand the principle behind this problem but it's giving me a headache to think that this is going on throughout my application and I need to find as solution.
double Value = 141.1;
double Discount = 25.0;
double disc = Value * Discount / 100; // disc = 35.275
Value -= disc; // Value = 105.824999999999999
Value = Functions.Round(Value, 2); // Value = 105.82
I'm using doubles to represent quite small numbers. Somehow in the calculation 141.1 - 35.275 the binary representation of the result gives a number which is just 0.0000000000001 out. Unfortunately, since I am then rounding this number, this gives the wrong answer.
I've read about using Decimals instead of Doubles but I can't replace every instance of a Double with a Decimal. Is there some easier way to get around this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
如果您正在寻找自然十进制值的精确表示,则需要将所有位置的
double
替换为decimal
。您只是使用了错误的数据类型。如果您一直在整数中使用short
,然后发现您需要处理比支持的更大的值,您会怎么做?这是同样的交易。但是,您确实应该尝试了解正在发生的事情……例如,为什么 Value 不完全等于 141.1。
文章:
If you're looking for exact representations of values which are naturally decimal, you will need to replace
double
withdecimal
everywhere. You're simply using the wrong datatype. If you'd been usingshort
everywhere for integers and then found out that you needed to cope with larger values than that supports, what would you do? It's the same deal.However, you should really try to understand what's going on to start with... why Value doesn't equal exactly 141.1, for example.
I have two articles on this:
您应该使用
十进制
——这就是它的用途。浮点运算的行为?这就是它的作用。它的精度有限。并非所有数字都可以精确表示。事实上,实值数有无数个,但只能表示有限个数。对于此应用程序,
decimal
的关键在于它使用基数 10 表示 -double
使用基数 2。You should use
decimal
– that's what it's for.The behaviour of floating point arithmetic? That's just what it does. It has limited finite precision. Not all numbers are exactly representable. In fact, there are an infinite number of real valued numbers, and only a finite number can be representable. The key to
decimal
, for this application, is that it uses a base 10 representation –double
uses base 2.您可以使用自己编写的一些函数,而不是使用 Round 来对数字进行舍入,该函数在舍入时使用一个小 epsilon 以允许错误。这就是你想要的答案。
你不想要的答案,但无论如何我要给出的是,如果你想要精度,并且由于你正在处理金钱,从你的例子来看你可能会这样做,你不应该使用二进制浮点数学。二进制浮点本质上是不准确的,并且某些数字无法正确表示。使用 Decimal(以 10 为基数进行浮点运算)在任何地方都是一种更好的方法,并且可以避免您在双打方面犯下代价高昂的错误。
Instead of using Round to round the number, you could use some function you write yourself which uses a small epsilon when rounding to allow for the error. That's the answer you want.
The answer you don't want, but I'm going to give anyway, is that if you want precision, and since you're dealing with money judging by your example you probably do, you should not be using binary floating point maths. Binary floating point is inherently inaccurate and some numbers just can't be represented correctly. Using Decimal, which does base-10 floating point, would be a much better approach everywhere and will avoid you making costly mistakes with your doubles.
在花了早上的大部分时间尝试将“双精度”的每个实例替换为“十进制”并意识到我正在打一场失败的战斗之后,我再次审视了我的 Round 函数。这对于那些无法实现正确解决方案的人可能很有用:
首先将值转换为小数,然后调用 Math.Round,这将返回“正确”值。
After spending most of the morning trying to replace every instance of a 'double' to 'decimal' and realising I was fighting a losing battle, I had another look at my Round function. This may be useful to those who can't implement the proper solution:
By first casting the value to a decimal, and then calling Math.Round, this will return the 'correct' value.