如何计算 32 位浮点数 epsilon?
How to calculate 32-bit floating-point epsilon?
在书 游戏引擎架构 中:“...,假设我们使用浮点变量来跟踪以秒为单位的绝对游戏时间。我们可以 运行 我们的游戏在我们的时钟变量的大小变得如此之大以至于增加 1/30 秒不再改变它的值之前?答案是大约 12.9 天。”
为什么是12.9天,怎么算?
这是由于浮点表示中的可表达区域。
从我的大学查看 this lecture。
随着指数变大,实际表示的值之间的真实数字线上的跳跃增加;当指数低时,表示密度高。举个例子,用有限个位值来成像十进制数。给定 1.0001e1 和 1.0002e1,这两个值之间的差异是 0.0001。但如果指数增加 1.0001-10 1.0002-10 两者之间的差异是 0.000100135。显然,随着指数的增加,它会变大。在你说的情况下,有可能跳跃变得如此之大,增加不促进最低有效位的舍入增加
有趣的是,对于表示的限制,较大的 float 类型的准确性更差!仅仅是因为当更多位可用于指数时,尾数中位模式的增加在数字线上跳得更远;与 double 一样,在 float
之上
当无法精确表示浮点运算的结果时,会四舍五入到最接近的值。所以你想找到最小值 x 使得增量 f = 1/30 小于宽度 h 的一半 在 x 和下一个最大的浮点数之间,这意味着 x+f 将四舍五入到 x.
由于相同binade中所有元素的间隙相同,我们知道x一定是其binade中最小的元素,这是一个幂共 2
所以如果x = 2k, 那么h = 2k-23 因为 float 有一个 24 位有效数字。所以我们需要找到最小的整数 k 使得
2k-23/2 > 1/30
这意味着 k > 19.09,因此 k = 20,并且 x = 2 20 = 1048576(秒)。
请注意 x / (60 × 60 × 24) = 12.14(天),这比您的答案建议的少一点,但根据经验检查:在茱莉亚
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(秒)
其中 ε 是标准 machine epsilon(1 和下一个最大浮点数之间的差距)。将其缩放为天数为 12.945。这提供了 x 的上限,但它不是上面解释的正确答案。
#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.
在书 游戏引擎架构 中:“...,假设我们使用浮点变量来跟踪以秒为单位的绝对游戏时间。我们可以 运行 我们的游戏在我们的时钟变量的大小变得如此之大以至于增加 1/30 秒不再改变它的值之前?答案是大约 12.9 天。” 为什么是12.9天,怎么算?
这是由于浮点表示中的可表达区域。 从我的大学查看 this lecture。
随着指数变大,实际表示的值之间的真实数字线上的跳跃增加;当指数低时,表示密度高。举个例子,用有限个位值来成像十进制数。给定 1.0001e1 和 1.0002e1,这两个值之间的差异是 0.0001。但如果指数增加 1.0001-10 1.0002-10 两者之间的差异是 0.000100135。显然,随着指数的增加,它会变大。在你说的情况下,有可能跳跃变得如此之大,增加不促进最低有效位的舍入增加
有趣的是,对于表示的限制,较大的 float 类型的准确性更差!仅仅是因为当更多位可用于指数时,尾数中位模式的增加在数字线上跳得更远;与 double 一样,在 float
之上当无法精确表示浮点运算的结果时,会四舍五入到最接近的值。所以你想找到最小值 x 使得增量 f = 1/30 小于宽度 h 的一半 在 x 和下一个最大的浮点数之间,这意味着 x+f 将四舍五入到 x.
由于相同binade中所有元素的间隙相同,我们知道x一定是其binade中最小的元素,这是一个幂共 2
所以如果x = 2k, 那么h = 2k-23 因为 float 有一个 24 位有效数字。所以我们需要找到最小的整数 k 使得
2k-23/2 > 1/30
这意味着 k > 19.09,因此 k = 20,并且 x = 2 20 = 1048576(秒)。
请注意 x / (60 × 60 × 24) = 12.14(天),这比您的答案建议的少一点,但根据经验检查:在茱莉亚
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(秒)
其中 ε 是标准 machine epsilon(1 和下一个最大浮点数之间的差距)。将其缩放为天数为 12.945。这提供了 x 的上限,但它不是上面解释的正确答案。
#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.