浮点变量不符合条件(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
无法表示float
或double
。 (它在二进制浮点中根本不可表示,因为它的二进制扩展是无限长的,就像decimal的十进制扩展一样。)因此,当你在程序中写入0.00002
时,C编译0.00002
一个非常接近0.00002
的double
值替换它。 。 类似地,当scanf
将数字0.00002
读入float
变量时,它会替换非常接近0.00002
的float
值。 由于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
,或者将gap
与float
相比较来避免转换:
while (gap < 0.00002F || gap > 0.99999F) {
但出于几个原因,这并不是真的正确。 首先,实际上无法保证C编译器完成的浮点转换与标准库( scanf
)完成的转换相同,并且标准允许编译器使用“最接近的可表示值,或更大或者与最接近的可表示值紧邻的较小可表示值,以实现定义的方式选择。“ (它没有详细说明scanf
产生哪个值,但建议它是最接近的可表示值。)碰巧, gcc
和glibc
(Linux上使用的C编译器和标准库)都产生最接近的可表示值,但其他实现则没有。
无论如何,根据您的错误消息,您希望值介于0.00001
和1.00000
之间。 所以你的测试应该是这样的:
while (gap <= 0.00001F || gap >= 1.0000F) { ...
(假设您将gap
保持为float
。)
任何上述解决方案都可行。 就个人而言,为了使比较更加直观,我会将gap
double
,并将比较改为0.00001
和1.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
这为您提供了一个小容差窗口,足以让您想要传递的大多数情况,而且大多数情况下您不想失败