1

我试图让用户输入一个介于 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”的东西。

4

4 回答 4

3

这里的问题是您使用了一个float变量 ( gap),但您正在将它与一个double常量 ( 0.00002) 进行比较。该常量是double因为 C 中的浮点常量是双精度的,除非另有说明。

一个潜在的问题是该数字在或0.00002中均不可表示。(它根本不能用二进制浮点表示,因为它的二进制扩展是无限长的,就像 ⅓ 的十进制扩展一样。)所以当你在程序中编写时,C 编译器会用一个非常接近. 类似地,当将数字读入变量时,它会替换一个非常接近的值。由于数字的位数多于,因此该值比该值更接近。floatdouble0.00002double0.00002scanf0.00002floatfloat0.00002doublefloatsdouble0.00002float

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

您可以通过多种方式解决此问题。首先,您可以通过制作gapadouble并将scanf格式更改为%lf或与gapa 进行比较来避免转换float

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

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

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

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

(假设您保留gapfloat.)

上述任何解决方案都将起作用。就个人而言,我会做gapadouble以使比较更直观,并将比较更改为与0.00001and进行比较1.0000

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

于 2013-11-09T01:58:30.013 回答
2

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

所以你不能直接比较两个接近的浮点数并得到健康的结果。

更多关于浮点的信息可以在这个维基百科页面上找到。

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

于 2013-11-09T01:03:42.857 回答
2

单精度浮点数的小数部分可以表示从 -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⁻⁵,第二个变体等于 1.999999948×10⁻⁵(只是为了比较 - 如果我们选择 5497560,我们得到 2.000000677×10⁻⁵)。因此可以选择第二个变量作为结果,其值不等于 0.00002。
浮点数的总精度也取决于指数值(取值从 -128 到 127):它可以通过小数部分量化步长和指数值的乘积来计算。如果总精度为 0.00002,则为 (2^-23)×(2^-15) = 3.6×(10^-12)。这意味着如果我们向 0.00002 添加一个小于该值一半的值,那么 0.00002 将保持不变。一般来说,这意味着有意义的浮点数是从1×exponent到2×(10^-23)×exponent。
这就是为什么一种非常流行的方法是使用大于量化步长的某个 epsilon 值来比较两个浮点数。

于 2013-11-09T01:03:50.493 回答
0

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

gap + 1e-8 < 0.0002 

这为您提供了一个足够小的容忍窗口,足以让您希望通过的大多数情况以及您不想失败的大多数情况

于 2013-11-09T01:06:07.807 回答