为什么我看到一个双变量初始化为某个值,如21.4为21.399999618530273?
-
05-07-2019 - |
题
double r = 11.631;
double theta = 21.4;
在调试器中,它们显示为 11.631000000000000
和 21.399999618530273
。
我该如何避免这种情况?
其他提示
我喜欢 Joel的解释,它涉及类似的二进制浮动Excel 2007中的精度问题:
看看最后那里有很多0110 0110 0110?那是因为 0.1 在二进制中没有精确的表示 ...它是一个重复的二进制数。这有点像1/3没有十进制表示。 1/3是0.33333333,你必须永远写3。如果你失去耐心,就会得到不准确的东西。
所以你可以想象,如果你试图做3 * 1/3,如果你没有时间永远写3,你会得到的结果是0.99999999,而不是1,人们会因为你的错而生气。
如果您的值如下:
double theta = 21.4;
你想做的事:
if (theta == 21.4)
{
}
你必须有点聪明,你需要检查theta的值是否真的接近21.4,但不一定是那个值。
if (fabs(theta - 21.4) <= 1e-6)
{
}
这部分是针对特定平台的 - 我们不知道您正在使用哪个平台。
部分原因还在于了解您实际需要看到的内容。调试器正在向您展示 - 无论如何 - 存储在变量中的精确值。在我的有关.NET中二进制浮点数的文章中,有一个 C#class ,可让您查看存储在double中的绝对完全号码。目前在线版本尚未正常工作 - 我会尝试将其中一个放在另一个网站上。
鉴于调试器看到“实际”的值,它必须对显示的内容进行判断调用 - 它可以显示舍入到几个小数位的值,或更精确的值。有些调试器在阅读开发人员的脑海中比其他调试器做得更好,但这是二进制浮点数的基本问题。
如果希望在精度限制下保持稳定,请使用定点 decimal
类型。有开销,如果您希望转换为浮点,则必须显式转换。如果你确实转换为浮点数,你将重新引入似乎困扰你的不稳定性。
或者,您可以克服它并学习使用浮点运算的有限精度。例如,您可以使用舍入来使值收敛,或者您可以使用epsilon比较来描述容差。 &QUOT;小量&QUOT;是您设置的常量,用于定义容差。例如,如果两个值在0.0001之间,则可以选择将它们视为相等。
我发现你可以使用运算符重载来使epsilon比较透明。那会很酷。
对于尾数指数表示,必须计算EPSILON以保持在可表示的精度范围内。对于数字N,Epsilon = N / 10E + 14
System.Double.Epsilon
是 Double
类型的最小可表示正值。为了我们的目的,它太小。阅读微软关于平等测试的建议
我之前遇到过这个问题( on我的博客) - 我认为惊喜往往是“非理性”数字不同。
这里的'无理性'我只是指他们无法用这种格式准确表示的事实。真正的无理数(如&#960; - pi)根本无法准确表示。
大多数人都熟悉1/3无法使用十进制:0.3333333333333 ...
奇怪的是1.1在浮点数中不起作用。人们期望十进制值在浮点数中起作用,因为他们如何看待它们:
1.1是11 x 10 ^ -1
实际上他们在基地2时
1.1是154811237190861 x 2 ^ -47
你无法避免它,你只需要习惯一些浮动是“非理性的”,就像1/3一样。
可以避免这种情况的一种方法是使用一个使用替代方法来表示十进制数的库,例如 BCD
在我看来,21.399999618530273是21.4的单精度(浮点)表示。看起来调试器正在从某个地方向下转换为浮动。
如果您使用的是Java并且需要准确性,请使用BigDecimal类进行浮点计算。它更慢但更安全。
由于您使用的是具有固定字节数的浮点数,因此无法避免这种情况。实数与其有限的符号之间根本没有同构现象。
但大多数时候你可以简单地忽略它。 21.4 == 21.4仍然是真的,因为它仍然是具有相同错误的相同数字。但是21.4f == 21.4可能不正确,因为float和double的错误是不同的。
如果你需要固定的精度,也许你应该尝试定点数。甚至是整数。例如,我经常使用int(1000 * x)传递给调试寻呼机。
如果它困扰您,您可以自定义调试期间某些值的显示方式。小心使用: - )
根据javadoc
&quot;如果数值运算符的至少一个操作数是double类型,那么
使用64位浮点运算执行运算,以及
的结果
数字运算符是double类型的值。如果另一个操作数不是double,则为
首先加宽(&#167; 5.1.5)以通过数字提升键入double(&#167; 5.6)。&quot;