为什么我看到一个双变量初始化为某个值,如21.4为21.399999618530273?

yes*_*aaj 46 floating-point precision

double r = 11.631;
double theta = 21.4;
Run Code Online (Sandbox Code Playgroud)

在调试器中,这些显示为11.63100000000000021.399999618530273.

我怎么能避免这个?

Kon*_*lph 57

这些准确性问题是由于浮点数的内部表示,并且您无法避免它.

顺便说一句,在运行时打印这些值通常仍会产生正确的结果,至少使用现代C++编译器.对于大多数操作而言,这不是一个问题.

  • 黑暗 - 事实并非如此.可表示值的空间在0附近更密集,并且当你走向无穷远时更加稀疏(例如,对于32位双精度,使用IEEE浮点标准无法精确表示2 ^ 24 + 1) (4认同)

Jef*_*ood 38

我喜欢Joel的解释,它解决了Excel 2007中类似的二进制浮点精度问题:

看看最后那里有很多0110 0110 0110?这是因为0.1具有二进制没有确切的表示 ......这是一个循环二进制数.这有点像1/3没有十进制表示.1/3是0.33333333,你必须永远写3.如果你失去耐心,你会得到一些不准确的东西.

所以你可以想象一下,如果你试图做3*1/3,并且你没有时间永远写3,你会得到的结果是0.99999999,而不是1,人们会生气你错了.

  • 如果你试图做3*1/3,你可以将三乘以一,得三.然后你将三乘三分,没有人应该生气.我假设乔尔打算说3*(1/3). (7认同)
  • @Nosredna这取决于你使用的语言是否具有更高的运算符优先级为`*`或`/`. (2认同)

Mar*_*ram 11

如果你有一个像这样的值:

double theta = 21.4;
Run Code Online (Sandbox Code Playgroud)

而且你想做:

if (theta == 21.4)
{
}
Run Code Online (Sandbox Code Playgroud)

你必须要有点聪明,你需要检查theta的值是否真的接近21.4,但不一定是那个值.

if (fabs(theta - 21.4) <= 1e-6)
{
}
Run Code Online (Sandbox Code Playgroud)


Jon*_*eet 7

这部分是针对特定平台的 - 我们不知道您正在使用什么平台.

这也部分是知道你真正想要看到的东西.调试器正在向您展示 - 无论如何 - 存储在变量中的精确值.在我关于.NET中二进制浮点数的文章中,有一个C#类可以让你看到存储在double中的绝对精确数字.目前在线版本尚未正常工作 - 我会尝试将其中一个放在另一个网站上.

鉴于调试器看到"实际"值,它必须对要显示的内容进行判断调用 - 它可以显示舍入到几个小数位的值,或更精确的值.有些调试器在阅读开发人员的头脑方面比其他调试器做得更好,但这是二进制浮点数的基本问题.

  • Jon,这个问题最初被标记为C++/VC6,所以我们实际上*知道*平台,然后有人认定这些信息并不重要并且编辑了标签. (2认同)

Pet*_*one 5

decimal如果想要在精度极限下保持稳定性,请使用定点类型.有开销,如果您希望转换为浮点,则必须显式转换.如果你确实转换为浮点数,你将重新引入似乎困扰你的不稳定性.

或者,您可以克服它并学习使用浮点运算的有限精度.例如,您可以使用舍入来使值收敛,或者您可以使用epsilon比较来描述容差."Epsilon"是您设置的常量,用于定义容差.例如,如果两个值在0.0001之间,则可以选择将它们视为相等.

我发现你可以使用运算符重载来使epsilon比较透明.那会非常酷.


对于尾数指数表示,必须计算EPSILON以保持在可表示的精度内.对于数字N,Epsilon = N/10E + 14

System.Double.Epsilon是该Double类型的最小可表示正值.它对我们来说太小了.阅读微软关于平等测试的建议