2015-04-28 19 views
1
float input = whatever; 
long output = (long)(0.5f + input); 

這對使用編譯器提供的浮點附加支持庫在MSP430上的應用程序是低效的。將有符號單精度浮點四捨五入到最近整數的有效方法是什麼?

我在想,這種特殊類型的「最接近的整數」舍入可能會有一個聰明的「竅門」,避免純浮點加法,或許是直接「點綴」浮點表示,但我還沒有找到這樣的。任何人都可以提出這樣一個把IEEE 754 32位浮點數捨去的技巧嗎?

+0

所以你正在尋找'long float_to_long_round_nearest(float)'函數的實現,對嗎?在您的平臺上「長」包含多少位?轉換能夠正確處理哪些「float」操作數範圍?應該如何處理領帶案件:聯繫一致,還是聯繫起來?似乎期望的轉換過程不應該涉及浮點運算,因爲這些是在這個平臺上模擬的?我認爲它的編程語言是C? – njuffa

+1

軟件浮點運行緩慢。解決緩慢軟件浮點問題的唯一方法是使用硬件浮點。 –

+0

使用直接在位級表示上操作的位操作方法將IEEE-754「float」轉換爲「long」當然是可能的,但是有一個精確的規範會很有幫助。今天晚些時候,我應該可以爲此製作一些C代碼。 – njuffa

回答

3

按位操作轉換非常簡單,下面的C代碼演示了它。根據關於MSP430上數據類型的評論,代碼假定int包含16位,並且long 32位。

我們需要一種手段,儘可能有效地將float的位模式轉換爲unsigned long。此實現爲此使用union,您的平臺可能具有更高效的機器特定方式,例如,一種內在的。在最壞的情況下,使用memcpy()複製字節。

只有幾種情況可以區分。我們可以檢查float輸入的指數字段以區分它們。如果參數太大或NaN,轉換失敗。一種慣例是在這種情況下返回最小的負整數操作數。如果輸入小於0.5,結果爲零。在消除這些特殊情況之後,我們留下那些需要一點點計算才能轉換的輸入。

對於足夠大的參數,float始終是一個整數,在這種情況下,我們只需要將尾數模式轉換爲正確的位位置。如果輸入太小而不是整數,我們將轉換爲32.32定點格式。四捨五入然後基於最重要的分數位,並且在平局的情況下,也是最不重要的整數位,因爲關係必須舍入爲偶數。

如果領帶箱子都應該總是輪遠離零,在代碼舍入邏輯簡化爲

r = r + (t >= 0x80000000UL); 

下面是實現上面討論的方法,將具有一個測試框架,測試此沿着float_to_long_round_nearest()徹底實施。

#include <stdio.h> 
#include <stdlib.h> 
#include <math.h> 

long float_to_long_round_nearest (float a) 
{ 
    volatile union { 
     float f; 
     unsigned long i; 
    } cvt; 
    unsigned long r, ia, t, expo; 

    cvt.f = a; 
    ia = cvt.i; 
    expo = (ia >> 23) & 0xff; 
    if (expo > 157) {  /* magnitude too large (>= 2**31) or NaN */ 
     r = 0x80000000UL; 
    } else if (expo < 126) { /* magnitude too small (< 0.5) */ 
     r = 0x00000000UL; 
    } else { 
     int shift = expo - 150; 
     t = (ia & 0x007fffffUL) | 0x00800000UL; 
     if (expo >= 150) { /* argument is an integer, shift left */ 
      r = t << shift; 
     } else { 
      r = t >> (-shift); 
      t = t << (32 + shift); 
      /* round to nearest or even */ 
      r = r + ((t > 0x80000000UL) | ((t == 0x80000000UL) & (r & 1))); 
     } 
     if ((long)ia < 0) { /* negate result if argument negative */ 
      r = -(long)r; 
     } 
    } 
    return (long)r; 
} 

long reference (float a) 
{ 
    return (long)rintf (a); 
} 

int main (void) 
{ 
    volatile union { 
     float f; 
     unsigned long i; 
    } arg; 
    long res, ref; 

    arg.i = 0x00000000UL; 
    do { 
     res = float_to_long_round_nearest (arg.f); 
     ref = reference (arg.f); 
     if (res != ref) { 
      printf ("arg=%08lx % 15.8e res=%08lx ref=%08lx\n", 
        arg.i, arg.f, res, ref); 
      return EXIT_FAILURE; 
     } 
     arg.i++; 
    } while (arg.i); 
    return EXIT_SUCCESS; 
} 
+0

在MSP430上,'int' /'long'具有16/32位。但是,支持''類型。 –

+0

@CL感謝您的澄清,我會相應地修復我的代碼。 – njuffa

+0

這是一流的!這正是我所希望的,並且與我的應用程序使用標準C庫「+ 0.5f」相比,這是一個重大改進。謝謝! – Puffin