有人可以解释为什么1.000000< = 1.0f是假的吗?
代码:
#include <iostream>
#include <stdio.h>
using namespace std;
int main(int argc, char **argv)
{
float step = 1.0f / 10;
float t;
for(t = 0; t <= 1.0f; t += step)
{
printf("t = %f\n", t);
cout << "t = " << t << "\n";
cout << "(t <= 1.0f) = " << (t <= 1.0f) << "\n";
}
printf("t = %f\n", t );
cout << "t = " << t << "\n";
cout << "(t <= 1.0f) = " << (t <= 1.0f) << "\n";
cout << "\n(1.000000 <= 1.0f) = " << (1.000000 <= 1.0f) << "\n";
}
结果:
t = 0.000000
t = 0
(t <= 1.0f) = 1
t = 0.100000
t = 0.1
(t <= 1.0f) = 1
t = 0.200000
t = 0.2
(t <= 1.0f) = 1
t = 0.300000
t = 0.3
(t <= 1.0f) = 1
t = 0.400000
t = 0.4
(t <= 1.0f) = 1
t = 0.500000
t = 0.5
(t <= 1.0f) = 1
t = 0.600000
t = 0.6
(t <= 1.0f) = 1
t = 0.700000
t = 0.7
(t <= 1.0f) = 1
t = 0.800000
t = 0.8
(t <= 1.0f) = 1
t = 0.900000
t = 0.9
(t <= 1.0f) = 1
t = 1.000000
t = 1
(t <= 1.0f) = 0
(1.000000 <= 1.0f) = 1
答案 0 :(得分:7)
正如评论中正确指出的那样,t
的值实际上与您在下面的行中定义的1.00000
不同。
使用std::setprecision(20)
以更高的精度打印t将显示其实际值:1.0000001192092895508
。
避免这类问题的常见方法是不与1
进行比较,而是与1 + epsilon
进行比较,其中epsilon是一个非常小的数字,可能比你的漂浮大一或两个数量级点精度。
所以你会把你的for循环条件写成
for(t = 0; t <= 1.000001f; t += step)
请注意,在您的情况下,epsilon应至少比最大浮点错误大十倍,因为浮点数会增加十倍。
正如Muepe和Alain所指出的,t != 1.0f
的原因是1/10
无法用二进制浮点数精确表示。
答案 1 :(得分:5)
C ++(以及大多数其他语言)中的浮点类型是使用一种方法实现的,该方法使用以下3个组件的可用字节(例如4或8):
让我们看看它是否为32位(4字节)类型,这通常是你在C ++中为float所拥有的。
符号只是一个简单的位置1或0,其中0表示正数,1表示其负数。如果你离开现有的每一个标准化,你也可以说0 - &gt;否定的,1 - &gt;正。
指数可以使用8位。与我们的日常生活相反,这个指数不是用于基数10而是基数2.这意味着1作为指数不对应于10但是对应于2,而指数2表示4(= 2 ^ 2)而不是100(= 10 ^ 2)。
另一个重要的部分是,对于浮点变量,我们也可能希望有负指数,如2 ^ -1 beeing 0.5,2 ^ -2表示0.25,依此类推。因此,我们定义了一个偏离值,该偏差值从指数中减去并产生实际值。在具有8位的情况下,我们选择127意味着指数0给出2 ^ -127并且指数255意味着2 ^ 128。但是,这种情况存在异常。通常,指数的两个值用于标记NaN和无穷大。因此,实指数为0到253,范围为2 ^ -127到2 ^ 126。
尾数显然现在填满剩余的23位。如果我们将尾数看作0和1的系列,你可以想象它的值就像1.m,其中m是那些位的系列,但是不是10的幂,而是2的幂 。所以1.1将是1 * 2 ^ 0 + 1 * 2 ^ -1 = 1 + 0.5 = 1.5。举个例子,我们来看看下面的尾数(非常短的):
m = 100101 - &gt; 1.100101至base 2 - &gt; 1 * 2 ^ 0 + 1 * 2 ^ -1 + 0 * 2 ^ -2 + 0 * 2 ^ -3 + 1 * 2 ^ -4 + 0 * 2 ^ -5 + 1 * 2 ^ -6 = 1 * 1 + 1 * 0.5 + 1 * 1/16 + 1 * 1/64 = 1.578125
然后使用以下公式计算浮点数的最终结果:
e * 1.m *(标志?-1:1)
你的循环到底出了什么问题:你的步数是0.1!对于基数为2的浮点数,0.1是一个非常糟糕的数字,让我们来看看为什么:
0.6 = 1 * 2^-1 + 0.1 -> m = 1
0.1 = 0 * 2^-2 + 0.1 -> m = 10
0.1 = 0 * 2^-3 + 0.1 -> m = 100
0.1 = 1 * 2^-4 + 0.0375 -> m = 1001
0.0375 = 1 * 2^-5 + 0.00625 -> m = 10011
0.00625 = 0 * 2^-6 + 0.00625 -> m = 100110
0.00625 = 0 * 2^-7 + 0.00625 -> m = 1001100
0.00625 = 1 * 2^-8 + 0.00234375 -> m = 10011001
我们可以继续这样,直到我们有23个尾数位,但我可以告诉你,你得到:
m = 10011001100110011001...
因此,二进制浮点环境中的0.1与基本10系统中的1/3相同。它是一个周期性的无限数。由于浮点数的空间是有限的,所以它只需要切割的第23位,因此0.1是一个小于0.1的微小位,因为浮点数中不存在数字的所有无限部分,而在23位之后是一个0,但它会四舍五入为1.
答案 2 :(得分:2)
原因是1.0 / 10.0 = 0.1不能完全用二进制表示,正如1.0 / 3.0 = 0.333 ..不能用十进制精确表示。 如果我们使用
float step = 1.0f / 8;
例如,,结果与预期一致。
要避免此类问题,请使用mic_e的答案中所示的小偏移量。