我浏览了最近发布的Doom 3 BFG 源代码,当我发现一些似乎没有任何意义的东西时。Doom 3 在idMath类中封装了数学函数。有些函数只是从 转发到相应的函数math.h
,但有些是重新实现(例如idMath::exp16()),我认为它们比它们的对应函数具有更高的性能math.h
(可能以牺牲精度为代价)。
然而,令我困惑的是他们实现该float idMath::Sqrt(float x)
功能的方式:
ID_INLINE float idMath::InvSqrt( float x ) {
return ( x > FLT_SMALLEST_NON_DENORMAL ) ? sqrtf( 1.0f / x ) : INFINITY;
}
ID_INLINE float idMath::Sqrt( float x ) {
return ( x >= 0.0f ) ? x * InvSqrt( x ) : 0.0f;
}
这似乎执行了两个不必要的浮点运算:首先是除法,然后是乘法。
有趣的是,最初的 Doom 3 源代码也以这种方式实现了平方根函数,但反平方根使用的是快速反平方根算法。
ID_INLINE float idMath::InvSqrt( float x ) {
dword a = ((union _flint*)(&x))->i;
union _flint seed;
assert( initialized );
double y = x * 0.5f;
seed.i = (( ( (3*EXP_BIAS-1) - ( (a >> EXP_POS) & 0xFF) ) >> 1)<<EXP_POS) | iSqrt[(a >> (EXP_POS-LOOKUP_BITS)) & LOOKUP_MASK];
double r = seed.f;
r = r * ( 1.5f - r * r * y );
r = r * ( 1.5f - r * r * y );
return (float) r;
}
ID_INLINE float idMath::Sqrt( float x ) {
return x * InvSqrt( x );
}
您认为在计算Sqrt(x)
中是否有任何优势,就x * InvSqrt(x)
好像在InvSqrt(x)
内部只调用math.h
's fsqrt(1.f/x)
?我是否可能在这里遗漏了一些关于非规范化浮点数的重要信息,或者这只是 id 软件方面的草率?