C++ C/C++;:1.00000<;=1.0f=假

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

有人能解释为什么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/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(作为其非负数)
  • 指数->小于0.1的第一个值是2^-4。因此指数应该是-4+127=123
  • 尾数->为此,我们检查指数为0.1的次数,然后尝试将分数转换为尾数。0.1/(2^-4)=0.1/0.0625=1.6。考虑到尾数为1.m,我们的尾数应为0.6。因此,让我们将其转换为二进制:
  • 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...