为什么我会看到一个双variables初始化为21.4,如21.399999618530273?
double r = 11.631; double theta = 21.4;
在debugging器中,这些显示为11.631000000000000
和21.399999618530273
。
我怎样才能避免这一点?
这些精度问题是由浮点数的内部表示造成的,因此避免这种情况的可能性不大。
顺便说一下,在运行时打印这些值通常仍会导致正确的结果,至less使用现代C ++编译器。 对于大多数操作,这不是一个问题。
我喜欢Joel的解释 ,它解决了Excel 2007中类似的二进制浮点精度问题:
看看最后有多less0110 0110 0110? 这是因为0.1 在二进制文件中没有确切的表示forms ……这是一个重复的二进制数字。 这就好像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) { }
这部分是针对特定平台的 – 我们不知道您使用的是什么平台。
这也是知道你真正想看到什么的一个例子。 无论如何,debugging器都会向您显示存储在variables中的精确值。 在我的关于.NET中二进制浮点数的文章中 ,有一个C#类 ,它可以让你看到存储在double中的绝对精确的数字。 在线版本目前不工作 – 我会尝试把一个在另一个网站上。
假如debugging器看到“实际”值,那么就需要判断要显示的内容 – 它可以显示四舍五入到小数点后的数值,或更精确的值。 有些debugging人员在阅读开发人员的想法时比其他人做得更好,但这是二进制浮点数的一个基本问题。
如果要稳定在极限的精度,使用定点decimal
types。 有开销,如果你想转换为浮点,你必须显式强制转换。 如果你转换为浮点数,你将重新引入似乎打扰你的不稳定性。
或者你可以克服它,并学习浮点运算的有限精度。 例如,您可以使用舍入来使值趋于一致,也可以使用epsilon比较来描述容差。 “Epsilon”是您设定的常数,定义了一个容差。 例如,如果两个值在0.0001之内,你可以select两个值相等。
它发生在我身上,你可以使用运算符重载,使epsilon比较透明。 这将是非常酷的。
对于尾数 – 指数表示EPSILON必须被计算保持在可表示的精度内。 对于数字N,Epsilon = N / 10E + 14
System.Double.Epsilon
是Double
types的最小可表示正值。 这对于我们的目的来说太小了。 阅读微软关于平等testing的build议
我之前曾经遇到过这个问题( 在我的博客上 ),我认为这个“非理性”数字是不同的。
这里的“非理性”我只是指这样的事实,他们不能准确地表示。 真正的无理数(如π-π)根本无法准确表示。
大多数人都熟悉1/3不工作在十进制:0.3333333333333 …
奇怪的是,1.1不能在浮动工作。 人们希望十进制数值能够在浮点数上工作,因为他们如何看待它们:
1.1是11×10 ^ -1
实际上,他们在基地2
1.1是154811237190861 x 2 ^ -47
你无法避免它,你只需要习惯一些花车是“非理性的”,就像三分之一一样。
避免这种情况的一种方法是使用一个使用另一种表示十进制数的方法的库,例如BCD
在我看来,21.399999618530273是21.4的单精度 (float)表示。 看起来像debugging器正在从双重抛出浮在某处。
如果您正在使用Java并且需要准确性,请使用BigDecimal类进行浮点计算。 这是更慢但更安全。
你不能避免这一点,因为你正在使用固定数量的字节的浮点数。 实数与有限符号之间根本不存在同构。
但大多数时候你可以忽略它。 21.4 == 21.4仍然是真的,因为它仍然是相同的错误号码。 但21.4f == 21.4可能不是真的,因为float和double的错误是不同的。
如果你需要固定的精度,也许你应该尝试定点数。 甚至整数。 我例如经常使用int(1000 * x)来传递debugging寻呼机。
计算机算术的危险
如果困扰您,您可以自定义在debugging过程中显示某些值的方式。 小心使用它:-)
使用debugging器显示属性增强debugging
参考一般十进制算术
当比较花车时也要注意,看到这个答案更多的信息。
根据javadoc
如果至less有一个数值运算符的操作数是doubletypes,那么
操作是使用64位浮点运算来进行的,并且结果如下
数值运算符是doubletypes的值。 如果另一个操作数不是双精度,则是
首先扩大(§5.1.5)以通过数字提升(§5.6)键入double。
这是来源