簡體   English   中英

如何在C ++中將十六進制轉換為IEEE 754 32位浮點

[英]How to convert Hex to IEEE 754 32 bit float in C++

我正在嘗試轉換存儲為int的十六進制值,並使用IEEE 32位規則將其轉換為浮點數。 我特別在努力獲取尾數和指數的正確值。 十六進制存儲在十六進制文件中。 我想要四個重要的數字。 下面是我的代碼。

float floatizeMe(unsigned int myNumba ) {
    //// myNumba comes in as 32 bits or 8 byte  

    unsigned int  sign = (myNumba & 0x007fffff) >>31;
    unsigned int  exponent = ((myNumba & 0x7f800000) >> 23)- 0x7F;
    unsigned int  mantissa = (myNumba & 0x007fffff) ;
    float  value = 0;
    float mantissa2; 

    cout << endl<< "mantissa is : " << dec << mantissa << endl;

    unsigned    int m1 = mantissa & 0x00400000 >> 23;
    unsigned    int m2 = mantissa & 0x00200000 >> 22;
    unsigned    int m3 = mantissa & 0x00080000 >> 21;
    unsigned    int m4 = mantissa & 0x00040000 >> 20;

    mantissa2 = m1 * (2 ^ -1) + m2*(2 ^ -2) + m3*(2 ^ -3) + m4*(2 ^ -4);

    cout << "\nsign is: " << dec << sign << endl;
    cout << "exponent is : " << dec << exponent << endl;
    cout << "mantissa  2 is : " << dec << mantissa2 << endl;

    // if above this number it is negative 
    if ( sign  == 1)
        sign = -1; 

    // if above this number it is positive 
    else {
        sign = 1;
    }

    value = (-1^sign) * (1+mantissa2) * (2 ^ exponent);
    cout << dec << "Float value is: " << value << "\n\n\n";

    return value;
}




  int main()
{   
    ifstream myfile("input.txt");
    if (myfile.is_open())
    {
        unsigned int a, b,b1; // Hex 
        float c, d, e; // Dec
        int choice; 

        unsigned int ex1 = 0;
        unsigned int ex2 = 1;
        myfile >> std::hex;
        myfile >> a >> b ;
        floatizeMe(a);
myfile.close();
return 0;

}

我懷疑你是說^

mantissa2 = m1 * (2 ^ -1) + m2*(2 ^ -2) + m3*(2 ^ -3) + m4*(2 ^ -4);

意思是“至……的力量”。 在C或C ++中沒有這樣的運算符。 ^運算符是按位XOR運算符。

我們需要轉換IEEE-754單精度和雙精度數字(使用32位和64位編碼)。 我們使用的是帶有有限功能集的C編譯器(Vector CANoe / Canalyzer CAPL腳本),最終開發了以下功能(可以使用任何在線C編譯器輕松對其進行測試):

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

double ConvertNumberToFloat(unsigned long number, int isDoublePrecision)
{
    int mantissaShift = isDoublePrecision ? 52 : 23;
    unsigned long exponentMask = isDoublePrecision ? 0x7FF0000000000000 : 0x7f800000;
    int bias = isDoublePrecision ? 1023 : 127;
    int signShift = isDoublePrecision ? 63 : 31;

    int sign = (number >> signShift) & 0x01;
    int exponent = ((number & exponentMask) >> mantissaShift) - bias;

    int power = -1;
    double total = 0.0;
    for ( int i = 0; i < mantissaShift; i++ )
    {
        int calc = (number >> (mantissaShift-i-1)) & 0x01;
        total += calc * pow(2.0, power);
        power--;
    }
    double value = (sign ? -1 : 1) * pow(2.0, exponent) * (total + 1.0);

    return value;
}

int main()
{
    // Single Precision 
    unsigned int singleValue = 0x40490FDB; // 3.141592...
    float singlePrecision = (float)ConvertNumberToFloat(singleValue, 0);
    printf("IEEE754 Single (from 32bit 0x%08X): %.7f\n",singleValue,singlePrecision);

    // Double Precision
    unsigned long doubleValue = 0x400921FB54442D18; // 3.141592653589793... 
    double doublePrecision = ConvertNumberToFloat(doubleValue, 1);
    printf("IEEE754 Double (from 64bit 0x%016lX): %.16f\n",doubleValue,doublePrecision);
}

考慮到您的CPU遵循IEEE標准,您也可以使用union 像這樣

  union
  {
    int num;
    float fnum;
  } my_union;

然后將整數值存儲到my_union.num並通過獲取my_union.fnum它們讀取為float。

您的代碼中存在許多非常基本的錯誤。

最明顯的是反復使用^作為“冪”。 ^是XOR運算符,對於“ power”,必須在math.h使用函數pow(base, exponent)

接下來,“我想有四個有效數字”(大概是尾數),但是您只提取了四個 四位只能編碼0..15 ,大約是一個半數字。 要獲得四個有效數字,您至少需要log(10,000)/ log(2)≈13.288,或至少14位(但最好是17位,因此您需要多一位完整的數字才能獲得更好的舍入)。

您提取錯誤的sign位,然后以錯誤的方式使用它。 是的,如果是0sign = 1 ,如果1然后sign = -1 ,但你在最后的計算為使用

value = (-1^sign) * ...

(再次用^ ,盡管pow在這里沒有任何意義)。 您應該立即使用sign * ..

exponent被聲明為unsigned int ,但是對於負值失敗。 需要為pow(2, exponent) signed (已從您的(2 ^ exponent)更正)。

從正面看, (1+mantissa2)確實是正確的。

將所有這些要點結合在一起,並且忽略了您實際上只要求4個有效數字這一事實,我得到了以下代碼。 請注意,為方便起見,我重新排列了初始的位移和提取–我將mantissa 向左移動 ,而不是向右移動,因此可以在計算中針對0進行測試。

(啊,我錯過了!)立即使用sign無效,因為它被聲明為unsigned int 因此,在您認為給它一個值-1 ,它實際上得到的值是4294967295 (更精確的是:來自limits.hUINT_MAX的值)。

消除此問題的最簡單方法是不乘以sign而僅對其進行測試,並在設置了value時取反。

float floatizeMe (unsigned int myNumba )
{
    //// myNumba comes in as 32 bits or 8 byte  

    unsigned int  sign = myNumba >>31;
    signed int  exponent = ((myNumba >> 23) & 0xff) - 0x7F;
    unsigned int  mantissa = myNumba << 9;
    float  value = 0;
    float mantissa2;

    cout << endl << "input is : " << hex << myNumba << endl;
    cout << endl << "mantissa is : " << hex << mantissa << endl;

    value = 0.5f;
    mantissa2 = 0.0f;
    while (mantissa)
    {
        if (mantissa & 0x80000000)
            mantissa2 += value;
        mantissa <<= 1;
        value *= 0.5f;
    }

    cout << "\nsign is: " << sign << endl;
    cout << "exponent is : " << hex << exponent << endl;
    cout << "mantissa 2 is : " << mantissa2 << endl;

    /* REMOVE:
       if above this number it is negative 
    if ( sign  == 1)
        sign = -1; 

    // if above this number it is positive 
    else {
        sign = 1;
    } */

    /* value = sign * (1.0f + mantissa2) * (pow (2, exponent)); */
    value = (1.0f + mantissa2) * (pow (2, exponent));
    if (sign) value = -value;
    cout << dec << "Float value is: " << value << "\n\n\n";

    return value;
}

通過上述操作,您將獲得正確的結果,例如0x3e4ccccd (0.2000000030)和0x40490FDB (3.1415927410)。

總而言之,如果您的輸入已經是IEEE-754格式(盡管是十六進制),那么簡單的轉換就足夠了。

只需執行以下操作(但首先確保將字節讀入整數時,您具有正確的字節序):

float int_bits_to_float(int32_t ieee754_bits) {
    float flt;
    *((int*) &flt) = ieee754_bits;
    return flt;
}

對我有用...當然,這假定float在您的體系結構上為32位,並且為IEEE754格式(幾乎總是這樣)。

除了簡單得多之外,這還避免了任何舍入/精度錯誤。

float value = reinterpret_cast<float&>(myNumba)

如果仍要單獨檢查零件,請在以后使用庫函數std::frexp 如果您不喜歡std::ldexp類型,請至少使用std::ldexp來應用指數而不是顯式數學,因為它很容易出現舍入/精度錯誤和溢出。

這兩種方法的替代方法是使用聯合類型,如本答案所述

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM