我试图让用户输入介于1.00000到0.00001之间的数字,而边缘不包含在浮点变量中。我可以假设用户在点后面没有输入超过5个数字。 现在,这是我写的:
printf("Enter required Leibniz gap.(Between 0.00001 to 1.00000)\n");
scanf("%f", &gap);
while ((gap < 0.00002) || (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”的事情。
答案 0 :(得分:3)
此处的问题是您使用的是float
变量(gap
),但您将其与double
常量(0.00002
)进行比较。常量为double
,因为除非另有说明,否则C中的浮点常量为double。
根本问题是,0.00002
或float
无法表示数字double
。 (它在二进制浮点中根本无法表示,因为它的二进制扩展是无限长的,就像decimal的十进制扩展一样。)因此,当您在程序中编写0.00002
时,C编译器会用{{1}替换它}值非常接近double
。同样,当0.00002
将数字scanf
读入0.00002
变量时,它会替换非常接近float
的{{1}}值。由于float
个数字的位数多于0.00002
,因此double
值比floats
值更接近double
。
当您比较具有不同精度的两个浮点值时,编译器会将精度较低的值转换为具有更高精度的完全相同的值。 (可表示为0.00002
的值集是可表示为float
的值集的超集,因此始终可以找到其值与值相同的double
一个float
。)这就是执行double
时会发生什么:float
被转换为相同值的gap < 0.00002
,并与双倍进行比较(接近于)gap
。由于这两个值实际上都略低于0.00002,并且double
更接近,0.00002
小于double
。
您可以通过多种方式解决此问题。首先,您可以通过将float
设为double
并将gap
格式更改为double
,或将scanf
与{{{1}进行比较,从而避免转化1}}:
%lf
但由于几个原因,这并不正确。首先,实际上不能保证C编译器完成的浮点转换与标准库(gap
)完成的转换相同,并且标准允许编译器使用“最接近的可表示值” ,或者以最接近的可表示值紧邻的较大或较小的可表示值,以实现定义的方式选择。“ (它没有详细说明哪个值float
产生,但建议它是最接近的可表示值。)碰巧,while (gap < 0.00002F || gap > 0.99999F) {
和scanf
(C编译器和标准在Linux上使用的库)都产生最接近的可表示值,但其他实现没有。
无论如何,根据您的错误消息,您希望值介于scanf
和gcc
之间。所以你的测试应该是:
glibc
(假设您将0.00001
保留为1.00000
。)
上述任何解决方案都可行。就个人而言,我要while (gap <= 0.00001F || gap >= 1.0000F) { ...
一个gap
以使比较更直观,并将比较更改为与float
和gap
进行比较。
顺便说一下,double
后缀表示“时间十到-5的幂”(0.00001
代表1.0000
)。你会看到很多;它是编写浮点常量的标准方法。
答案 1 :(得分:2)
float
s无法为每个可能的数字存储精确值(0-1之间的无限数字因此是不可能的)。将0.00002分配给浮点数将具有不同但非常接近的数字,因为您正在实现该实现。随着数量的增加,精度会下降。
因此,您无法直接比较两个紧密的浮点数并获得健康的结果。
有关浮点的更多信息,请参见on this Wikipedia page。
你可以做的是模拟定点数学。让int n = 100000;
在内部代表1.00000
(1000 - > 0.001等)并相应地进行计算或使用定点数学库。
答案 2 :(得分:2)
单精度浮点数的分数部分可以表示从-2到2-2 ^ -23的数字,并且具有最小量化步长为2 ^ -23的分数部分。因此,如果某个值无法使用此类步骤表示,则根据IEEE 754 rounding rules使用最接近的值表示:
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)×指数。
这就是为什么一种非常流行的方法是使用一些大于量化步长的ε值来比较两个浮点数。
答案 3 :(得分:0)
就像一些评论所说,由于浮点数的表示方式,你会看到这样的错误。 解决方法是将其转换为
gap + 1e-8 < 0.0002
这为您提供了一个小容差窗口,足以让您想要传递大多数情况,而且大多数情况下您不想失败