Quake3 快速开平方和开平方倒数计算优化
2016-02-23 08:46
399 查看
在Quake3中,卡马克使用了广为人知的魔数来优化,开平方和开平方倒数计算。而在Doom3中改为查表计算了。
[cpp] view
plain copy
/*
================
SquareRootFloat
================
*/
float SquareRootFloat(float number) {
long i;
float x, y;
const float f = 1.5F;
x = number * 0.5F;
y = number;
i = * ( long * ) &y;
i = 0x5f3759df - ( i >> 1 );
y = * ( float * ) &i;
y = y * ( f - ( x * y * y ) );
y = y * ( f - ( x * y * y ) );
return number * y;
}
[cpp] view
plain copy
/*
** float q_rsqrt( float number )
*/
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // what the fuck?
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
return y;
}
开平方和开平方倒数的算法是一样,都使用了魔数0x5f3759df,利用牛顿迭代法进行迭代。只是在上面的迭代中,开平方迭代了2次,开平方倒数迭代了1次。开平方最后结果,是开平方倒数在乘以原数值的结果。在GameDev.net上有人做过测试,该函数的相对误差约为0.177585%,速度比C标准库的sqrt提高超过20%。如果增加一次迭代过程,相对误差可以降低到e-004 的级数,但速度也会降到和sqrt差不多。
下面对这2个算法进行微量的优化。
利用union去除float类型的强转,和一些赋值操作
long类型改用int类型
把中间变量的赋值操作去除,替换到计算式中
[cpp] view
plain copy
static float InvSqrtf(float x)
{
union
{
float x;
int i;
} u;
u.x = x;
u.i = 0x5f3759df - (u.i >> 1);
return u.x * (1.5f - 0.5f * x * u.x * u.x);
}
static float Sqrtf(float x)
{
union
{
float x;
int i;
} u;
u.x = x;
u.i = 0x5f3759df - (u.i >> 1);
// InvSqrt(x) * x, equal Sqrt(x)
return u.x * (1.5f - 0.5f * x * u.x * u.x) * x;
}
如果,精度不够可以增加迭代次数,不过也会降低性能。经过测试精度还不错。
[cpp] view
plain copy
/*
================
SquareRootFloat
================
*/
float SquareRootFloat(float number) {
long i;
float x, y;
const float f = 1.5F;
x = number * 0.5F;
y = number;
i = * ( long * ) &y;
i = 0x5f3759df - ( i >> 1 );
y = * ( float * ) &i;
y = y * ( f - ( x * y * y ) );
y = y * ( f - ( x * y * y ) );
return number * y;
}
[cpp] view
plain copy
/*
** float q_rsqrt( float number )
*/
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // what the fuck?
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
return y;
}
开平方和开平方倒数的算法是一样,都使用了魔数0x5f3759df,利用牛顿迭代法进行迭代。只是在上面的迭代中,开平方迭代了2次,开平方倒数迭代了1次。开平方最后结果,是开平方倒数在乘以原数值的结果。在GameDev.net上有人做过测试,该函数的相对误差约为0.177585%,速度比C标准库的sqrt提高超过20%。如果增加一次迭代过程,相对误差可以降低到e-004 的级数,但速度也会降到和sqrt差不多。
下面对这2个算法进行微量的优化。
利用union去除float类型的强转,和一些赋值操作
long类型改用int类型
把中间变量的赋值操作去除,替换到计算式中
[cpp] view
plain copy
static float InvSqrtf(float x)
{
union
{
float x;
int i;
} u;
u.x = x;
u.i = 0x5f3759df - (u.i >> 1);
return u.x * (1.5f - 0.5f * x * u.x * u.x);
}
static float Sqrtf(float x)
{
union
{
float x;
int i;
} u;
u.x = x;
u.i = 0x5f3759df - (u.i >> 1);
// InvSqrt(x) * x, equal Sqrt(x)
return u.x * (1.5f - 0.5f * x * u.x * u.x) * x;
}
如果,精度不够可以增加迭代次数,不过也会降低性能。经过测试精度还不错。
相关文章推荐
- 从NOT NULL完整性约束的介绍看Concept的强大
- Z-index
- sql server 表和字段的数量限制
- iOS中常用简单封装
- Redis将Session 集中管理
- SparkContext的初始化(仲篇)——SparkUI、环境变量及调度
- 《形象讲解sudo命令简单原理及实践优化用户使用命令》
- 06
- Windows10默认网关不可用故障解决方法
- 如何准确判断请求是搜索引擎爬虫(蜘蛛)发出的请求?
- 用户行为轨迹 如何集成
- server 2003 访问FTP站点下载文件,提示当前的安全设置不允许从该位置下载文件
- linux 给joe jane Julie 发送邮件
- 附加包含目录 同一个解决方案下,使用不同工程的头文件
- GIS+=地理信息+行业+大数据——基于云环境流处理平台下的实时交通创新型app
- 计世资讯观点:2016数据库安全市场解读
- 内存缓存的使用Demo
- 利用位运算求一个月的天数
- Nginx搭建反向代理服务器过程详解
- 图片流验证后缀