浮點數之間的比較,基本運算這些究竟是怎麼實現的

chuangdu發表於2024-08-29

科學計算當中會用到不少浮點數的操作,這些浮點數可能是16位,32位,64位,80位甚至是128位。開源專案SoftFloat提供了一個高效的浮點運算實現,可以在沒有硬體支援的情況下,高效模擬浮點數的各種操作。

那麼,浮點數之間的比較,基本運算這些究竟是怎麼實現的呢,可以拿32位浮點數作為例子。

這是32位浮點數加法的實現,首先宣告瞭一個結構體float32_t。

typedef struct { uint32_t v; } float32_t;

這提供了32位浮點數的底層位表示,同時還宣告瞭一個union。

union ui32_f32 { uint32_t ui; float32_t f; };

一方面儲存了浮點數的位表示,另一方面也可以轉換為32位無符號整型直接進行比較,這在後面的演算法當中會直接涉及。先看看加法。

float32_t f32_add( float32_t a, float32_t b )
{
    union ui32_f32 uA;
    uint_fast32_t uiA;
    union ui32_f32 uB;
    uint_fast32_t uiB;
#if ! defined INLINE_LEVEL || (INLINE_LEVEL < 1)
    float32_t (*magsFuncPtr)( uint_fast32_t, uint_fast32_t );
#endif

    uA.f = a;
    uiA = uA.ui;
    uB.f = b;
    uiB = uB.ui;
#if defined INLINE_LEVEL && (1 <= INLINE_LEVEL)
    if ( signF32UI( uiA ^ uiB ) ) {
        return softfloat_subMagsF32( uiA, uiB );
    } else {
        return softfloat_addMagsF32( uiA, uiB );
    }
#else
    magsFuncPtr =
        signF32UI( uiA ^ uiB ) ? softfloat_subMagsF32 : softfloat_addMagsF32;
    return (*magsFuncPtr)( uiA, uiB );
#endif

}

這裡uiA和uiB是儲存無符號整型的,signF32UI是提取符號位的。signF32UI(uiA ^ uiB)判斷符號位是否相同,如果相同則呼叫加法,如果符號位不相同則呼叫減法,因為沒有浮點數,所以只能透過整型去模擬,另外,union儲存浮點和整型有一個名詞,似乎叫型別雙關技術?不過這裡union儲存的只是位表示,並不是真的浮點數。

float32_t f32_sub( float32_t a, float32_t b )
{
    union ui32_f32 uA;
    uint_fast32_t uiA;
    union ui32_f32 uB;
    uint_fast32_t uiB;
#if ! defined INLINE_LEVEL || (INLINE_LEVEL < 1)
    float32_t (*magsFuncPtr)( uint_fast32_t, uint_fast32_t );
#endif

    uA.f = a;
    uiA = uA.ui;
    uB.f = b;
    uiB = uB.ui;
#if defined INLINE_LEVEL && (1 <= INLINE_LEVEL)
    if ( signF32UI( uiA ^ uiB ) ) {
        return softfloat_addMagsF32( uiA, uiB );
    } else {
        return softfloat_subMagsF32( uiA, uiB );
    }
#else
    magsFuncPtr =
        signF32UI( uiA ^ uiB ) ? softfloat_addMagsF32 : softfloat_subMagsF32;
    return (*magsFuncPtr)( uiA, uiB );
#endif

}

減法則是在判斷符號那裡反過來,其它一樣。這時候可以看看比較運算怎麼做。

bool f32_le( float32_t a, float32_t b )
{
    union ui32_f32 uA;
    uint_fast32_t uiA;
    union ui32_f32 uB;
    uint_fast32_t uiB;
    bool signA, signB;

    uA.f = a;
    uiA = uA.ui;
    uB.f = b;
    uiB = uB.ui;
    if ( isNaNF32UI( uiA ) || isNaNF32UI( uiB ) ) {
        softfloat_raiseFlags( softfloat_flag_invalid );
        return false;
    }
    signA = signF32UI( uiA );
    signB = signF32UI( uiB );
    return
        (signA != signB) ? signA || ! (uint32_t) ((uiA | uiB)<<1)
            : (uiA == uiB) || (signA ^ (uiA < uiB));

}

最後的表示式有點繞,一步一步拆分。首先符號不相等(一正一負)的話,如果A的符號是1,也就是負數,肯定比B小,否則走 || 後的分支。把A和B的最高位(符號位)剔除,判斷是否相同,也就是+0和-0的情況,這裡記得別漏了前面的!符號,因為判斷兩者是否都為0;如果A和B同號的話,如果都是正數則直接比較,如果都是負數,則前面的signA會對結果取反。

結語

最近處於校招階段,正在準備,有時間會分享自己的心得和體會,希望儘早上岸。

本部落格參考豆莢加速器。轉載請註明出處!

相關文章