C++ C/C++;:1.00000<;=1.0f=假
有人能解释为什么1.000000正如评论中正确指出的那样,C++ C/C++;:1.00000<;=1.0f=假,c++,for-loop,floating-accuracy,C++,For Loop,Floating Accuracy,有人能解释为什么1.000000正如评论中正确指出的那样,t的值实际上与您在下一行中定义的1.00000不同 使用std::setprecision(20)以更高精度打印t将显示其实际值:1.000000 1192092895508 避免此类问题的常用方法不是与1比较,而是与1+epsilon比较,其中epsilon是一个非常小的数字,可能比浮点精度大一到两个数量级 因此,您可以将for循环条件写为 for(t = 0; t <= 1.000001f; t += step) 原因是1.0
t
的值实际上与您在下一行中定义的1.00000
不同
使用std::setprecision(20)
以更高精度打印t将显示其实际值:1.000000 1192092895508
避免此类问题的常用方法不是与1
比较,而是与1+epsilon
比较,其中epsilon是一个非常小的数字,可能比浮点精度大一到两个数量级
因此,您可以将for循环条件写为
for(t = 0; t <= 1.000001f; t += step)
原因是1.0/10.0=0.1不能用二进制精确表示,正如1.0/3.0=0.333..不能用小数精确表示一样。 如果我们使用
float step = 1.0f / 8;
例如,结果与预期一致
为了避免这些问题,使用MICEY的答案中所示的小偏移量。
< P> C++中的浮点类型(以及大多数其他语言)是使用以下4个组件使用可用字节(例如,8或8)的方法实现的:让我们看看它的32位(4字节)类型,它通常是C++中的浮点。< /P> 符号只是一个简单的比特,它是1或0,其中0表示其正,1表示其负。如果不考虑所有存在的标准化,也可以说0->负,1->正
指数可以使用8位。与我们的日常生活相反,此指数不适用于基数10,而是基数2。这意味着1作为指数并不对应于10,而是对应于2,指数2表示4(=2^2),而不是100(=10^2) 另一个重要的部分是,对于浮点变量,我们可能也希望有负指数,比如2^-1等于0.5,2^-2等于0.25等等。因此,我们定义了一个偏差值,从指数中减去该偏差值,并得到实际值。在这种情况下,我们选择了8位127,这意味着0的指数给出了2^-127和指数255的t表示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->1.100101到基数2->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。因为for循环变量不是真正的1.0,而是0.9999999或类似的。标准浮点问题。因为你陷入了与浮点运算相关的一个非常常见的陷阱。因为神秘的
float
运算。阅读《年轻学徒:你的问题》n是无效的。当您的程序正确地输出时,t是的,我做了另一个没有for循环的示例,但只是增加了值,得到了相同的结果。我也意识到了这一点。我认为为比较添加一个微小偏移量的方法优于舍入,这不仅是出于性能原因,也是因为舍入不适用于for循环非常大的数字,或者与非整数的比较。但是,我的答案很好。“MICEY,我认为它取决于……是的,大数不能被舍入,但是如果要测试相等性,舍入是有用的。用ε,必须用(x>=y -e & & x)来测试相等性,当舍入时,需要考虑是否需要代码>
,ceil()
或floor()
,在亚历山大的情况下,这些都不能真正完成工作。你需要floor(t+epsilon)
@mic\e是的,我想你是对的。我会改变我的答案,谢谢。0.1f
实际上比0.1
大一点点,因为最后一个数字是向上舍入的,而不是向下舍入的。不过这可能取决于你的FPU。你当然是
float step = 1.0f / 8;
m = 10011001100110011001...