浮点变量不符合条件(C)

我试图让用户输入介于1.00000到0.00001之间的数字,而边缘不包含在浮点变量中。 我可以假设用户在点后面没有输入超过5个数字。 现在,这是我写的:

printf("Enter required Leibniz gap.(Between 0.00001 to 1.00000)\n"); scanf("%f", &gap); while ((gap  0.99999)) { printf("Enter required Leibniz gap.(Between 0.00001 to 1.00000)\n"); scanf("%f", &gap); } 

现在,当我输入尽可能小的数字时:0.00002陷入了while循环。 当我运行调试器时,我看到0.00002与float变量中的值一起存储:1.99999995e-005任何人都可以为我澄清我做错了什么? 为什么0.00002不满足条件? 什么是“1.99999995e-005”的事情。

float s不能为每个可能的数字存储精确值(0-1之间的无限数字因此是不可能的)。 将0.00002分配给浮点数将具有不同但非常接近的数字,因为您正在实现的实现。 精度随着数量的增加而减少。

因此,您无法直接比较两个紧密浮动并获得健康的结果。

有关浮点的更多信息,请访问此维基百科页面 。

你能做的是模拟定点数学。 有一个int n = 100000; 在内部表示1.00000 (1000 – > 0.001等)并相应地进行计算或使用定点数学库。

这里的问题是你使用float变量( gap ),但是你将它与double常数( 0.00002 )进行比较。 常量是double因为除非另有说明,否则C中的浮点常量是double

一个潜在的问题是数字0.00002无法表示floatdouble 。 (它在二进制浮点中根本不可表示,因为它的二进制扩展是无限长的,就像decimal的十进制扩展一样。)因此,当你在程序中写入0.00002时,C编译0.00002一个非常接近0.00002double值替换它。 。 类似地,当scanf将数字0.00002读入float变量时,它会替换非常接近0.00002float值。 由于double数字的位数多于floats ,因此double值比float值更接近0.00002

比较具有不同精度的两个浮点值时,编译器会将精度较低的值转换为具有更高精度的完全相同的值。 (可表示为double的值集是可表示为float的值集的超集,因此总是可以找到一个double其值与float的值相同。)这就是当gap < 0.00002时发生的情况。执行: gap转换为相同值的double ,并与double(接近) 0.00002 。 由于这两个值实际上都小于0.00002,并且double更接近,因此float小于double

您可以通过几种方式解决此问题。 首先,您可以通过将gap变为double并将scanf格式更改为%lf ,或者将gapfloat相比较来避免转换:

 while (gap < 0.00002F || gap > 0.99999F) { 

但出于几个原因,这并不是真的正确。 首先,实际上无法保证C编译器完成的浮点转换与标准库( scanf )完成的转换相同,并且标准允许编译器使用“最接近的可表示值,或更大或者与最接近的可表示值紧邻的较小可表示值,以实现定义的方式选择。“ (它没有详细说明scanf产生哪个值,但建议它是最接近的可表示值。)碰巧, gccglibc (Linux上使用的C编译器和标准库)都产生最接近的可表示值,但其他实现则没有。

无论如何,根据您的错误消息,您希望值介于0.000011.00000之间。 所以你的测试应该是这样的:

 while (gap <= 0.00001F || gap >= 1.0000F) { ... 

(假设您将gap保持为float 。)

任何上述解决方案都可行。 就个人而言,为了使比较更加直观,我会将gap double ,并将比较改为0.000011.0000

顺便说一句, E-05后缀的意思是“时间十到-5的幂”( E代表Exponent )。 你会看到很多; 它是编写浮点常量的标准方法。

单精度浮点数的分数部分可以表示从-2到2-2 ^ -23的数字,并且具有最小量化步长为2 ^ -23的分数部分。 因此,如果某个值无法用这样的步骤表示,则根据IEEE 754舍入规则用最接近的值表示:

 0.00002*32768 = 0.655360043 // floating point exponent is chosen. 0.655360043/(2^-23) = 5497558.5 // is not an integer multiplier // of quantization step, so the 5497558*(2^-23) = 0.655359983 // nearest value is chosen 5497559*(2^-23) = 0.655360103 // from these two variants 

第一个变量等于1.999969797×10 -6(十进制格式),第二个等于1.999999948×10 -6(只是为了比较 – 如果我们选择5497560,我们得到2.000000677×10 -6)。 因此,可以选择第二个变量作为结果,其值不等于0.00002。
浮点数的总精度也取决于指数值(取-128到127的值):它可以通过分数部分量化步长和指数值的乘法来计算。 在0.00002的情况下,总精度为(2 ^ -23)×(2 ^ -15)= 3.6×(10 ^ -12)。 这意味着如果我们向0.00002添加一个小于该值一半的值,则0.00002保持不变。 一般来说,这意味着有意义的浮点数的数量是从1×指数到2×(10 ^ -23)×指数。
这就是为什么一种非常流行的方法是使用一些大于量化步长的epsilon值来比较两个浮点数。

就像一些评论所说,由于浮点数的表示方式,你会看到这样的错误。 对此的解决方案是将其转换为

 gap + 1e-8 < 0.0002 

这为您提供了一个小容差窗口,足以让您想要传递的大多数情况,而且大多数情况下您不想失败