我浏览了最近发布的Doom 3 BFG源代码,当我发现一些看起来毫无意义的东西时,它们与数学函数包装在idMath类中。其中一些函数只是转发到
然而,令我困惑的是他们如何实现
这似乎执行了两个不必要的浮点运算:首先是除法,然后是乘法。
有趣的是,原始Doom 3源代码也以这种方式实现了平方根函数,但倒数平方根使用了快速倒数平方根算法。
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 );
}
如果InvSqrt(x)
内部只是调用math.h
的fsqrt(1.f/x)
,那么将Sqrt(x)
计算为x * InvSqrt(x)
是否有优势?我在这里可能忽略了关于非规格化浮点数的重要内容,或者这只是id软件的粗心大意?
sqrtf
在处理非规格化浮点数时会比较慢? - user1773602Sqrt()
内部检查反规范化,并返回他们认为合适的内容。 - Robert Rüger