2010-01-06 96 views
5

有沒有人有方便的將代碼片段轉換爲直接下級(或高級)float,,而不改變或假設任何有關FPU當前舍入模式將雙精度浮點數轉換爲浮點而不依賴FPU舍入模式

注意:這個約束可能意味着根本不使用FPU。我期望在這些條件下最簡單的方法就是讀取64位長的雙精度數據位並使用它。

你可以假設你選擇了簡單的存儲方式,而且這一雙,可通過下面的聯盟d領域:

union double_bits 
{ 
    long i; 
    double d; 
}; 

我會盡力做我自己,但我敢肯定我會爲非規範化或負數引入難以察覺的錯誤。

+0

上glibc的系統,你會發現一個頭文件ieee754.h,它定義了浮點類型和位域結構,工會,這樣你就可以用尾數和指數更容易,不好意思工作,但我不能給你真正的碼。 – quinmars 2010-01-06 11:23:45

回答

3

我認爲下面的作品,但我會先說明我的假設:

  • 浮點數以IEEE-754格式存儲在您的實施中,
  • 沒有溢出,
  • 您有nextafterf()可用(它在C99中指定)。

此外,最有可能的是,這種方法效率不高。

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

int main(int argc, char *argv[]) 
{ 
    /* Change to non-zero for superior, otherwise inferior */ 
    int superior = 0; 

    /* double value to convert */ 
    double d = 0.1; 

    float f; 
    double tmp = d; 

    if (argc > 1) 
     d = strtod(argv[1], NULL); 

    /* First, get an approximation of the double value */ 
    f = d; 

    /* Now, convert that back to double */ 
    tmp = f; 

    /* Print the numbers. %a is C99 */ 
    printf("Double: %.20f (%a)\n", d, d); 
    printf("Float: %.20f (%a)\n", f, f); 
    printf("tmp: %.20f (%a)\n", tmp, tmp); 

    if (superior) { 
     /* If we wanted superior, and got a smaller value, 
      get the next value */ 
     if (tmp < d) 
      f = nextafterf(f, INFINITY); 
    } else { 
     if (tmp > d) 
      f = nextafterf(f, -INFINITY); 
    } 
    printf("converted: %.20f (%a)\n", f, f); 

    return 0; 
} 

在我的機器,它打印:

Double: 0.10000000000000000555 (0x1.999999999999ap-4) 
Float: 0.10000000149011611938 (0x1.99999ap-4) 
tmp: 0.10000000149011611938 (0x1.99999ap-4) 
converted: 0.09999999403953552246 (0x1.999998p-4) 

的想法是,我的double值轉換爲float值—這可能比這取決於雙值小於或大於舍入模式。當轉換回double時,我們可以檢查它是小於還是大於原始值。然後,如果float的值不是正確的方向,我們查看下一個float號碼的轉換號碼在原始號碼的方向。

+0

非常感謝你的代碼。我慢慢地確信這是最容易出錯的解決方案。感謝您指出'nextafterf',這比在/ float中減少float的位好得多,就好像它是int一樣。爲了減輕f + 1等於f的風險,我可以寫'nextafterf(f,INFINITY)'嗎? – 2010-01-07 08:46:54

+0

我剛剛閱讀手冊頁,C標準草案,並試用了它,看起來像'INFINITY'應該可以工作。 – 2010-01-07 08:54:39

+0

好的,我編輯了我的帖子。感謝您的評論。 – 2010-01-07 08:56:58

3

爲了更準確地不僅僅是重新結合尾數和做好這項工作指數位的檢查了這一點:

http://www.mathworks.com/matlabcentral/fileexchange/23173

問候

+0

謝謝。這裏的'doubles2halfp'函數像我擔心的那樣複雜,但至少它已經有一半的常量是對的,所以這是一個很好的起點。 – 2010-01-06 10:14:13

+0

我會使用給定的代碼作爲參考,並重寫一個更簡單的方法,使用&>>,或者,然後檢查非常小和非常大的數字。從http://babbage.cs.qc.edu/IEEE-754/Decimal查看移位計數和位位置。html – stacker 2010-01-06 10:30:27

2

我在這裏發佈了代碼:https://stackoverflow.com/q/19644895/364818並在下面複製它以方便您。

// d is IEEE double, but double is not natively supported. 
    static float ConvertDoubleToFloat(void* d) 
    { 
     unsigned long long x; 
     float f; // assumed to be IEEE float 
     unsigned long long sign ; 
     unsigned long long exponent; 
     unsigned long long mantissa; 

     memcpy(&x,d,8); 

     // IEEE binary64 format (unsupported) 
     sign  = (x >> 63) & 1; // 1 
     exponent = ((x >> 52) & 0x7FF); // 11 
     mantissa = (x >> 0) & 0x000FFFFFFFFFFFFFULL; // 52 
     exponent -= 1023; 

     // IEEE binary32 format (supported) 
     exponent += 127; // rebase 
     exponent &= 0xFF; 
     mantissa >>= (52-23); // left justify 

     x = mantissa | (exponent << 23) | (sign << 31); 
     memcpy(&f,&x,4); 

     return f; 
    } 
+0

謝謝。 'exponent&= 0xFF'這一行表示當返回'±FLT_MAX'或'±inf'時,會返回一個具有奇指數的'float'(反常結果也是關閉的)。 – 2013-10-28 21:13:50