12

在《游戏引擎架构》一书中:“……假设我们使用浮点变量来跟踪游戏的绝对时间(以秒为单位)。在时钟变量的大小变得如此之大以至于增加 1/30 之前,我们可以运行游戏多长时间秒到它不再改变它的价值?答案大约是 12.9 天。为什么是 12.9 天,如何计算?

4

3 回答 3

9

当浮点计算的结果不能准确表示时,它会四舍五入到最接近的值。所以你想找到最小值x,使得增量f = 1/30 小于x和下一个最大浮点数之间的宽度h的一半,这意味着x+f将四舍五入到x

由于同一binade中的所有元素的间隙相同,因此我们知道x必须是其 binade 中的最小元素,即 2 的幂。

因此,如果x = 2 k,则h = 2 k-23因为浮点数具有 24 位有效数。所以我们需要找到最小的整数k使得

2 k-23 /2 > 1/30

这意味着k > 19.09,因此k = 20,并且x = 2 20 = 1048576(秒)。

请注意,x / (60 × 60 × 24) = 12.14(天),这比您的答案建议的要少一点,但凭经验检查:在 Julia

julia> x = 2f0^20
1.048576f6

julia> f = 1f0/30f0
0.033333335f0

julia> x+f == x
true

julia> p = prevfloat(x)
1.04857594f6

julia> p+f == p
false

更新:好的,那么 12.9 是从哪里来的?12.14 是在游戏时间,而不是实际时间:由于浮点涉及的舍入误差(尤其是接近尾声,当舍入误差实际上相对于f相当大时),这些会产生分歧。据我所知,没有办法直接计算它,但迭代 32 位浮点数实际上相当快。

同样,在 Julia 中:

julia> function timestuff(f)
           t = 0
           x = 0f0
           while true
               t += 1
               xp = x
               x += f
               if x == xp
                   return (t,x)
               end
           end
       end
timestuff (generic function with 1 method)

julia> t,x = timestuff(1f0/30f0)
(24986956,1.048576f6)

x与我们之前计算的结果相匹配,并且t是以 30 秒为单位的时钟时间。转换为天数:

julia> t/(30*60*60*24)
9.640029320987654

这是更远的地方。所以我不知道12.9是从哪里来的......

更新 2:我的猜测是 12.9 来自计算

y = 4 × f / ε = 1118481.125(秒)

其中 ε 是标准机器 epsilon(1 和下一个最大浮点数之间的差距)。将此缩放为天数为 12.945。这提供了x的上限,但它不是上面解释的正确答案。

于 2016-05-25T21:05:00.660 回答
2
#include <iostream>
#include <iomanip>

/*
https://en.wikipedia.org/wiki/Machine_epsilon#How_to_determine_machine_epsilon
*/

typedef union
{
    int32_t i32;
    float   f32;
} fi32_t;

float float_epsilon(float nbr)
{
    fi32_t flt;
    flt.f32 = nbr;
    flt.i32++;
    return (flt.f32 - nbr);
}

int main()
{
    // How to calculate 32-bit floating-point epsilon?

    const float one {1.}, ten_mills {10e6};
    std::cout << "epsilon for number " << one << " is:\n"
        << std::fixed << std::setprecision(25)
        << float_epsilon(one)
        << std::defaultfloat << "\n\n";

    std::cout << "epsilon for number " << ten_mills << " is:\n"
        << std::fixed << std::setprecision(25)
        << float_epsilon(ten_mills)
        << std::defaultfloat << "\n\n";


    // In book Game Engine Architecture : "..., let’s say we use a
    // floating-point variable to track absolute game time in seconds.
    // How long can we run our game before the magnitude of our clock
    // variable gets so large that adding 1/30th of a second to it no
    // longer changes its value? The answer is roughly 12.9 days."
    // Why 12.9 days, how to calculate it ?

    const float one_30th {1.f/30}, day_sec {60*60*24};
    float time_sec {}, time_sec_old {};

    while ((time_sec += one_30th) > time_sec_old)
    {
        time_sec_old = time_sec;
    }

    std::cout << "We can run our game for "
        << std::fixed << std::setprecision(5)
        << (time_sec / day_sec)
        << std::defaultfloat << " days.\n";


    return EXIT_SUCCESS;
}

这输出

epsilon for number 1 is:
0.0000001192092895507812500

epsilon for number 10000000 is:
1.0000000000000000000000000

We can run our game for 12.13630 days.
于 2017-10-23T11:53:54.977 回答
1

这是由于浮点表示中的可表达区域。看看我大学的这个讲座

随着指数变大,实际表示的值之间的真实数字线上的跳跃增加;当指数低时,表示密度高。举个例子,用有限数量的位值成像十进制数。给定 1.0001e1 和 1.0002e1 两个值之间的差值为 0.0001。但如果指数增加 1.0001-10 1.0002-10,则两者之差为 0.000100135。显然,随着指数的增加,这会变得更大。在您谈论的情况下,跳跃可能变得如此之大,增加不会促进最低有效位的四舍五入增加

有趣的是,对于表示的限制,较大的浮点类型的准确性更差!仅仅是因为当有更多位可用于指数时,尾数中位模式的增加会在数轴上跳得更远;与双倍的情况一样,浮动

于 2016-05-25T19:20:14.153 回答