[英]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
位,然后以錯誤的方式使用它。 是的,如果是0
則sign = 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.h
的UINT_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.