我知道这已经被一次又一次地讨论过,但我似乎无法得到一个一步一步的双重划分的最简单的例子,导致C#中预期的,不完整的结果 - 所以我是想知道是否有一些编译器标志或其他奇怪的东西,我没想到。考虑这个例子:
double v1 = 0.7;
double v2 = 0.025;
double result = v1 / v2;
当我在最后一行之后断开并在VS调试器中检查它时,“result”的值是27.999999999999996。我知道我可以通过更改为“十进制”来解决它,但在周围程序的情况下这是不可能的。像这样的两个低精度双倍不能分成28的正确值是不奇怪的? Math.Round是唯一真正解决结果的解决方案吗?
答案 0 :(得分:17)
像这样的两个低精度双打不能分成28的正确值这并不奇怪吗?
不,不是真的。在double
类型中,0.7和0.025都不能精确表示。涉及的确切值是:
0.6999999999999999555910790149937383830547332763671875
0.025000000000000001387778780781445675529539585113525390625
现在你觉得这个部门没有准确地给出28分吗?垃圾进去,垃圾出来......
正如您所说,正确表示十进制数字的正确结果是使用decimal
。如果你的程序的其余部分使用了错误的类型,那只意味着你需要解决更高的问题:获得错误答案的成本,或者改变整个程序的成本。
答案 1 :(得分:4)
它与double
数字的“简单”或“小”无关。严格地说,0.7
或0.025
都不能完全存储在计算机内存中,因此如果您精确度较高,则对其进行计算可能会提供有趣的结果。
是的,请使用decimal
或圆形。
答案 2 :(得分:4)
如果您正在处理float
或double
,精确度始终是个问题。
它是计算机科学中的一个已知问题,每种编程语言都受其影响。为了最大限度地减少这些与舍入主要相关的错误,Numerical Analysis的完整字段专用于它。
例如,让我们采取以下代码。
你会期待什么?
您希望答案为1
,但事实并非如此,您将获得0.9999907
。
float v = .001f;
float sum = 0;
for (int i = 0; i < 1000; i++ )
{
sum += v;
}
答案 3 :(得分:4)
通过类比来解释:
想象一下你在基地3工作。在基数3中,0.1是(十进制)1/3,或0.333333333'。
因此,您可以在基数3中精确表示1/3(十进制),但在尝试以十进制表示时会出现舍入错误。
嗯,你可以用一些十进制数得到完全相同的东西:它们可以用十进制精确表示,但它们不能用二进制精确表示;因此,你会得到它们的舍入错误。
答案 4 :(得分:2)
对第一个问题的简短回答:不,这并不奇怪。浮点数是实数的离散近似值,这意味着当您进行算术运算时,舍入误差将传播和缩放。
Theres'是一个完整的数学领域,称为数值分析,主要处理如何在使用这种近似值时最小化误差。
答案 5 :(得分:2)
这是通常的浮点不精确。并非每个数字都可以表示为double,并且这些次要表示不准确性会加起来。这也是你不应该将双打与精确数字进行比较的原因。我刚刚对其进行了测试,result.ToString()
显示28
(可能会在double.ToString()
中进行某种舍入?)。 result == 28
已返回false
。并(int)result
返回27
。所以你只需要期待那样的不精确。