【问题标题】:Floating points in C++ (float and double)C++ 中的浮点数(float 和 double)
【发布时间】:2021-03-11 10:08:35
【问题描述】:

我知道我们不应该在循环中使用浮点数。但是有人可以向我解释一下当我们有一个循环并且我们将一个小数添加到一个大数直到我们达到允许循环终止的某个值时会发生什么? 我想这可能会导致潜在的错误。但除此之外呢?

单精度 (float) 和双精度 (double) 浮点数会是什么样子?我想在 double 类型中会出现更多的舍入错误。谁能给我一个例子(C ++中最好的),因为我不知道如何开始......

如果您能给我一些提示,我将不胜感激。谢谢!

【问题讨论】:

  • "我知道我们不应该在循环中使用浮点数。" .那是一种误解。例如,在牛顿的求根算法中,您循环直到错误足够小,并且该错误将是一个浮点数。

标签: c++ loops floating-point double rounding-error


【解决方案1】:

在使用 IEEE-754 算术和 float 的“单一”(二进制 32)格式的 C++ 实现中,此代码打印“count = 3”:

int count = 0;
for (float f = 0; f < .3f; f += .1f)
    ++count;
std::cout << "count = " << count << ".\n";

但此代码打印“count = 4”:

int count = 0;
for (float f = 0; f < .33f; f += .11f)
    ++count;
std::cout << "count = " << count << ".\n";

在第一个示例中,源文本 .1f 被转换为 0.100000001490116119384765625,这是 float 中可表示的值,接近于 0.1。源文本 .3f 转换为 0.300000011920928955078125,float 值最接近 0.3。将 .1f 的转换值添加到 f 会产生 0.100000001490116119384765625,然后是 0.20000000298023223876953125,然后是 0.3000000119209289550765325,此时循环为 0.100000000765333。

在第二个示例中,.11f 转换为 0.10999999940395355224609375,.33f 转换为 0.3300000131130218505859375。在这种情况下,将 .11f 的转换值与 f 相加会产生 0.10999999940395355224609375,然后是 0.2199999988079071044921875,然后是 0.329999983310699462890625。注意,由于四舍五入,这个加3次.11f的结果是0.329999983310699462890625,小于.33f(0.3300000131130218505859375),所以f &lt; .33f为真,循环继续下一次迭代。

这类似于以两位十进制格式添加 ⅓,循环边界为三分之三(即 1)。如果我们有 for (f = 0; f &lt; 1; f += ⅓),则源文本中的 必须转换为 .33(两位十进制数)。然后f 将逐步通过 0.33、0.66 和 0.99。循环直到达到 1.32 才会停止。二进制浮点运算中也会出现相同的舍入问题。

当循环中添加的数量相对于大数字来说是一个小数字时,这些舍入问题就更大了。首先,加法会更多,所以舍入误差也会更多,而且可能会累积。其次,由于较大的数字需要较大的指数才能以浮点格式对其进行缩放,因此它们的绝对精度低于较小的数字。这意味着四舍五入必须相对于添加的小数字更大。所以舍入误差的幅度更大。

那么,即使循环最终终止,由于累积误差,每次迭代中f 的值也可能与期望值相差甚远。如果 f 用于循环内的计算,则计算可能未使用所需的值,并可能产生不正确的结果。

【讨论】:

    【解决方案2】:

    随着值的增加,两个浮点值之间的差异也会增加。有一点i+1 会产生相同的值。

    考虑这段代码:

    #include <iostream>
    
    int main()
    {   
        float i = 0;
        while (i != i + 1) i++;
        std::cout << i << std::endl;
        return 0;
    }
    

    while (i != i + 1) 应该是一个无限循环,但对于浮点变量,它不是。 上面的代码在https://godbolt.org/z/7xf8n8上打印1.67772e+07

    所以,for (float f = 0; f &lt; 2e7; f++) 是一个无限循环。

    你可以自己用double试试,价值更大。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-11
      相关资源
      最近更新 更多