float(double)快速转换int的⽅法
⾃⼰写⼀个软件渲染器的时候,⽆意中发现float转换int⾮常耗时,于是查阅⽂章,这才有了这个命题,以前不清楚还有这么个机制。⽹上看了很多⽂章,搜索到了⼀个数字6755399441055744,这个是double快速转换int的⼀个magic number。⾄于原理我⼀知半解,主要看效果。经测试,这个函数的效率⽐c++直接float转int⾼很多,记录下来以便备忘。
//
// 将64位浮点数转换为32位整数
// ⼩数部分将四舍五⼊到偶数
//
//⽤于double的magic number是1.5*2^52=6755399441055744.0trunc函数和int
//对于double来说,相应的magic number就是1.5*2^36
//处理float的速度⽐汇编低1/3
/
/编译器优化情况下,⽐⼿写汇编快
inline int32_t f_toint(double x)
{
x += 6755399441055744.0;
return *(int32_t*)&x;
}
//四舍五⼊,处理的数据范围是-2^22 ~ 2^22-1, -4194304.0 ~ 4194303.0
inline int32_t f_toint32(float x)
{
//取得符号位,设置掩码
uint32_t n = ((*(uint32_t*)&x) & 0x80000000) ? 0xFFC00000 : 0;
x += 12582912.0f;
return ((*(uint32_t*)&x) & 0x3FFFFF) | n;
}
f_toint32的原型是csdn论坛⼀个⽹友说的回复:
inline long magic_f2l( float x )
{
static long mask[] = { 0x0, 0xffc00000 };
static float trunc[] = { -.5f, .5f };
int s = ( *(unsigned long*)&x )>>31;
x += trunc[s];
x += 12582912.f;
return(*(long*)&x) & 0x3fffff | mask[s];
}
这个⾥⾯⽤到了12582912这个魔法数字,和double转换那个是⼀样的,都可以⽤这个函数得到:
double float_magic_number(int bits)
{
return 1.5 * STD::pow(2.0, double(bits));
}
但magic_f2l()这个函数效率并没有double版本的那么好,原因估计是运算运算太多了,于是我精简了⼀下:
inline long magic_f2l( float x )
{
static long mask[] = { 0x0, 0xffc00000 };
static float trunc[] = { -.5f, .5f };
int s = ( *(unsigned long*)&x )>>31;//这⾥得到float的符号位,如果是负数,最后结果就和0xFFC00000进⾏or运算。这⾥有个位移操作
x += trunc[s];//这⾥有个加法和数组取值操作
x += 12582912.f;
return(*(long*)&x) & 0x3fffff | mask[s];//数组取值
}
经简化,完成如下函数,这个效率和double版本不相上下,稍微慢⼀丢丢(⼏个时钟周期),仍然把c++默认转换摔在后⾯。
inline int32_t f_toint32(float x)
{
//取得符号位,设置掩码
uint32_t n = ((*(uint32_t*)&x) & 0x80000000) ? 0xFFC00000 : 0;//⼀个三元操作符,直接储存掩码
x += 12582912.0f;//魔法数字加法
return ((*(uint32_t*)&x) & 0x3FFFFF) | n;//直接or运算
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论