【问题标题】:(Objective-C++) Testing float value causes infinite loop(Objective-C++) 测试浮点值导致无限循环
【发布时间】:2019-08-05 13:05:45
【问题描述】:

我正在尝试从头开始编写一个 Objective-C(++) 应用程序,但完全不知道为什么在 while 循环中测试 float 似乎会导致无限循环。

首先,文件:test.mm

#include <stdio.h>
#include <mach/mach_time.h>

int main(int argc, const char* argv[])
{
    #pragma unused(argc)
    #pragma unused(argv)

    // --- LOOP ---
    float timer = 2.0f;
    float debugMarker = 2.0f;

    uint64_t lastLoopStart = mach_absolute_time();
    mach_timebase_info_data_t timebase;
    mach_timebase_info(&timebase);

    while(timer > 0.0f)
    {
        uint64_t now = mach_absolute_time();
        uint64_t elapsed = now - lastLoopStart;
        uint64_t nanos = elapsed * timebase.numer / timebase.denom;
        float deltaTime = static_cast<float>(static_cast<double>(nanos) * 1.0E-9);
        timer -= deltaTime;
        lastLoopStart = now;

        // Including this line avoids the bug
        // timer -= 0.1f;

        // This does not cause the bug
        // if(0.0f < timer)

        // This causes the bug
        // if(debugMarker > 0.0f)

        // This causes the bug
        if(debugMarker >= timer)
        {
            printf("timer: %f\n", static_cast<double>(timer));

            debugMarker -= 1.0f;
        }
    }

    printf("DONE\n");

    return (0);
}

编译:clang -g -Weverything test.mm

运行此代码生成的程序会导致循环输出一次定时器值,然后出现无限循环。 使用if(debugMarker &gt; 0.0f) 会导致它打印两次计时器值。

我完全不知道这里会发生什么。 任何帮助将不胜感激!

【问题讨论】:

  • 浮点精度不足以从 2 秒中减去纳秒。您的代码什么也不做,尤其是当您不打印任何内容时。所以它花了 11-30 纳秒(取决于你的设备)。

标签: objective-c macos clang


【解决方案1】:

你真的必须/有任何理由使用浮点数吗?

我的建议是忘记浮点数,真的,使用它没有任何优势,而且精度也不高,甚至对于 sin/cos 函数也不行。

为了获得最佳实践,我建议始终选择 double 作为浮点类型,无论是从 C 到 Swift 的任何语言。

 


您的代码没问题!!我将其转换为 double,删除了强制转换并调整了一些变量值。

我将 printf 行注释掉,编译并执行。

它在 2.011 秒内循环并退出。

> $ time ./a.out
DONE
./a.out  1.97s user 0.02s system 98% cpu 2.011 total

然后我激活了prinf函数,再次执行。

循环 2.014 秒,打印超过 500 万行,回归计数。

(partial listing... total was 5168689 printed lines)
timer: 0.000060
timer: 0.000057
timer: 0.000054
timer: 0.000051
timer: 0.000048
timer: 0.000045
timer: 0.000041
timer: 0.000039
timer: 0.000036
timer: 0.000033
timer: 0.000029
timer: 0.000026
timer: 0.000023
timer: 0.000020
timer: 0.000017
timer: 0.000014
timer: 0.000011
timer: 0.000008
timer: 0.000005
timer: 0.000002
timer: -0.000001
DONE
./a.out  0.24s user 0.25s system 23% cpu 2.014 total

在这里,转换为使用双精度:

#include <stdio.h>
#include <mach/mach_time.h>

int main(int argc, const char* argv[])
{
    #pragma unused(argc)
    #pragma unused(argv)

    // --- LOOP ---
    double timer = 2;
    double debugMarker = 2;

    uint64_t lastLoopStart = mach_absolute_time();
    mach_timebase_info_data_t timebase;
    mach_timebase_info(&timebase);

    while(timer > 0)
    {
        uint64_t now = mach_absolute_time();
        uint64_t elapsed = now - lastLoopStart;
        uint64_t nanos = elapsed * timebase.numer / timebase.denom;
        double deltaTime = nanos * 1.0E-9;
        timer -= deltaTime;
        lastLoopStart = now;

        // Including this line avoids the bug
        // timer -= 0.1f;

        // This does not cause the bug
        // if(0.0f < timer)

        // This causes the bug
        // if(debugMarker > 0)

        // This causes the bug
        //if(debugMarker < timer)
        //{

          printf("timer: %F\n", timer);

        //    debugMarker -= 1;
        //}
    }

    printf("DONE\n");

    return (0);
}

【讨论】:

  • 感谢 Prado,当使用 doubles 而不是 floats 时,它确实可以按预期工作,但是我仍然不清楚为什么一些不同的版本会失败,而有些会成功。我能做出的最好猜测是,当@gralex 提到的printf() 语句未被调用时,差异太小而无法从计时器值中减去任何内容。我强烈不同意double总是优于float的概念,因为使用float的性能提升总是是影响考虑。
  • 我理解你的问题。在使用 double 测试您的代码时,我在某些执行中得到了不同的结果,这比平均执行要慢得多。我认为这是由我的计算机多任务处理引起的,它可能会暂时减慢毫秒级别的某些进程。我们测量的是纳秒,比毫小得多。纳秒需要 9 个十进制数字,float on 有 7 个,所以它只有在减法等于或大于 3 个纳秒数字(>= 100ns)时才有效。在多任务减速期间它可以工作,float 处理它,但在正常使用时,它的 15 个十进制数字只能加倍
  • 这里有一个很好的例子,使用 double 和 float 计算方程 x2 - 4.0000000 x + 3.9999999 = 0 的根,其中 float 无法计算并返回 2.00000 和 2.00000,而 double 可以正确处理它并返回方程的正确值,即 2.00032 和 1.99968。在此处检查,@ 987654321@ - 就个人而言,几年前我完全放弃了使用浮点数,因为它的不精确性导致了太多的头痛。你可以使用浮点数,但要小心,永远不要忘记这个细节:它可能是错误的,会搞砸数学
猜你喜欢
  • 1970-01-01
  • 2016-02-22
  • 1970-01-01
  • 2016-01-16
  • 2015-12-27
  • 2015-01-11
  • 1970-01-01
  • 2020-08-27
  • 2021-05-07
相关资源
最近更新 更多