float(double)快速轉換int的方法


自己寫一個軟件渲染器的時候,無意中發現float轉換int非常耗時,於是查閱文章,這才有了這個命題,以前不清楚還有這么個機制。網上看了很多文章,搜索到了一個數字6755399441055744,這個是double快速轉換int的一個magic number。至於原理我一知半解,主要看效果。經測試,這個函數的效率比c++直接float轉int高很多,記錄下來以便備忘。

//
// 將64位浮點數轉換為32位整數
// 小數部分將四舍五入到偶數
//
//用於double的magic number是1.5*2^52=6755399441055744.0
//對於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運算
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM