【问题标题】:Using float / double as a loop variable使用 float / double 作为循环变量
【发布时间】:2015-02-06 09:12:56
【问题描述】:

在 x64 架构上运行,Visual Studio 2013 Windows 8.1

我正在尝试运行一个使用浮点数作为其循环变量的循环,问题是在增量操作期间十进制值正在丢失。

例如:

float incrementValue = 360 / 14;
for (float i = 0; i <= 360; i = i + incrementValue)
{
    cout << endl << " I " << i;
}

我需要 i >= 360 的值。但它停在 350 。 由于 360/14 =25.7142857 但看起来增量是 25 的步骤。 如果我对任何整数做同样的事情,它就可以正常工作,这个问题只适用于 xx.yyyy 形式的任何数字

我尝试查找有关此问题的各种网站,但找不到任何可以帮助我/回答我的问题的内容。

【问题讨论】:

  • 360 / 14 会将25.0 分配给您的变量。使用360.0 / 14.0 分配25.7142857
  • 这并不能回答这个问题,但是由于浮点精度问题,无论如何使用for循环从我看到的地方开始浮点增量是一个坏主意。我宁愿使用while
  • 使用浮点值作为循环增量没有任何问题,只要您知道几件事。首先,由于精度误差,迭代次数可能会有所不同,因此您的 +-1 可能会超出您的预期。其次,出于与上述相同的原因,直接测试浮点值是否相等是不可靠的,您应该测试该值是否在某个“可接受”范围内。
  • @Lionel There is nothing wrong with using floating point values as a loop increment 他们不同意 - securecoding.cert.org/confluence/display/seccode/…
  • @PaulMcKenzie 好吧,“没什么错”可能太强了。 :) 也许“可以考虑使用它”会更合适。

标签: c++ computer-science


【解决方案1】:

您在这里遇到三个问题(两个相关),所有这些问题都可能有所贡献。

首先,浮点比较通常是不明智的,因为虽然您可能认为您拥有360,但实际上它可能是359.999999942

第二个是不准确随着时间的推移而累积。每次您添加一个像0.99 这样您认为1 的数字时,错误就会累积。

使用您使用的值,这些错误可能会很小,但您还是应该注意它们。如果您开始处理大量数字,您会很快发现问题。


最后一个不相关的问题是360 / 14整数 除法,它会给你一个整数结果,25 而不是25.714285714

您可以通过确保其中一个值是浮点数来解决最后一个问题:

float incrementValue = float(360) / 14;

但这并不能解决前两个问题,它在某个时候咬你。

要解决这个问题,你最好还是坚持使用整数(对于这种简单的情况)并在最新的可能实例中转换为浮点数:

#include<iostream>
int main (void) {
    int incrementValue = 360;
    for (int i = 0; i <= 360 * 14; i = i + incrementValue)
        std::cout << " I " << float(i) / 14 << '\n';
    return 0;
}

给你:

 I 0
 I 25.7143
 I 51.4286
 I 77.1429
 I 102.857
 I 128.571
 I 154.286
 I 180
 I 205.714
 I 231.429
 I 257.143
 I 282.857
 I 308.571
 I 334.286
 I 360

【讨论】:

  • 但有点不是......虽然你的说法是正确的,但这里的情况是他正在做整数除法
  • 非常感谢。我只是想通了同样的事情。问题是我没有将值转换为 float 。 [ (float)360/(float)14 ] 。我做了这个改变,它现在工作正常
  • @rahul,请记住其他两个问题。虽然他们可能还没有得到你,但他们最终会:-) 只有 一个 的数字需要是浮点数,这样你就可以节省一些打字时间。
【解决方案2】:

360 / 14 会给你一个整数结果。试试360.0 / 14.0

【讨论】:

    【解决方案3】:

    很难得到 'i = 360',因为 'i = 360.0000001' 会通过测试,而准确地得到 '360' 会很棘手。计算机上的浮点数很难,而且充满了小问题。

    尽量使用整数会更好:-

    int num_steps = 14;
    
    for (int i = 0 ; i <= num_steps ; ++i)
    {
      float value = 360.0f * i / num_steps;
      // use value
    }
    

    在循环中使用 '

    【讨论】:

      【解决方案4】:

      从除法浮点类型中制作操作数:

       float incrementValue = 360.f / 14.f;
      

      【讨论】:

        【解决方案5】:
          float incrementValue = (float)360 / 14;
          for (float i = 0; i <= 360; i = i + incrementValue)
          {
              cout << endl << " I " << i;
          }
        

        【讨论】:

          【解决方案6】:

          如果您知道迭代次数,那么使用浮点数来控制循环并不是一个好主意,因为浮点数并不精确。

          事实上,使用浮点数来控制任何循环都是一个坏主意,不使用公差(取决于机器精度)来考虑潜在的舍入误差。

          在这种情况下,您知道迭代次数,因此这样的循环就可以解决问题。

          int num_steps = 14
          float factor = 360.0/num_steps;
          
          for (int i = 0; i < num_steps; ++i)
          {
              std::cout << "\nI" << (i*factor);
          }
          

          【讨论】:

            【解决方案7】:

            当我真的想在循环中使用浮点数时,我会更改上限以确保不会因连续浮点错误而出现错误。

            float x_min = 0.;
            float x_max = 360.;
            int N_steps = 14;
            float x_step = (x_max - x_min) / N_steps;
            
            for (float x = x_min; x <= x_max + x_step / 2; x += x_step)
            {
                // do stuff
            }
            

            虽然这可以确保我每一步都正确,但如果有大量的步骤,精度问题就会累积起来。在这种情况下,使用整数循环变量并每次计算相应的浮点数。然后我只计算了一次浮点错误,而不是 14 次(或一百万次)。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2019-09-27
              • 2022-12-11
              • 2017-08-13
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-04-13
              相关资源
              最近更新 更多