I am trying to convert hex values stored as int and convert them to floatting point numbers using the IEEE 32 bit rules. I am specifically struggling with getting the right values for the mantissa and exponent. The hex is stored from in a file in hex. I want to have four significant figures to it. Below is my code.
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;
}
I suspect you mean for the ^
in
mantissa2 = m1 * (2 ^ -1) + m2*(2 ^ -2) + m3*(2 ^ -3) + m4*(2 ^ -4);
to mean "to the power of". There is no such operator in C or C++. The ^
operator is the bit-wise XOR operator.
Considering your CPU follows the IEEE standard, you can also use union
. Something like this
union
{
int num;
float fnum;
} my_union;
Then store the integer values into my_union.num
and read them as float by getting my_union.fnum
.
We needed to convert IEEE-754 single and double precision numbers (using 32bit and 64bit encoding). We were using a C compiler (Vector CANoe/Canalyzer CAPL Script) with a restricted set of functions and ended up developing the function below (it can easily be tested using any on-line C compiler):
#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);
}
There are a number of very basic errors in your code.
The most visible is repeatedly using ^
for "power of". ^
is the XOR-operator, and for "power" you must use the function pow(base, exponent)
in math.h
.
Next, "I want to have four significant figures" (presumably for the mantissa), but you only extract four bits. Four bits can encode only 0..15
, which is about a digit-and-a-half. To get four significant digits, you'd need at least log(10,000)/log(2) ≈ 13.288, or at least 14 bits (but preferably 17, so you get one full extra digit to get better rounding).
You extract the wrong bit for sign
, and then you use it the wrong way. Yes, if it is 0
then sign = 1
and if 1
then sign = -1
, but you use it in the final calculation as
value = (-1^sign) * ...
(again with a ^
, although even pow
does not make any sense here). You ought to have used sign * ..
straight away.
exponent
was declared an unsigned int
, but that fails for negative values. It needs to be signed
for pow(2, exponent)
(corrected from your (2 ^ exponent)
).
On the positive side, (1+mantissa2)
is indeed correct.
With all of those points taken together, and ignoring the fact that you actually ask for only 4 significant digits, I get the following code. Note that I rearranged the initial bit shifting and extracting for convenience – I shift mantissa
to the left, rather than the right, so I can test against 0
in its calculation.
(Ah, I missed this!) Using sign
straight away does not work because it was declared as an unsigned int
. Therefore, where you think you give it the value -1
, it actually gets the value 4294967295
(more precise: the value of UINT_MAX
from limits.h
).
The easiest way to get rid of this is not multiplying by sign
but only test it, and negate value
if it is set.
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;
}
With the above, you get correct results for values such as 0x3e4ccccd
(0.2000000030) and 0x40490FDB
(3.1415927410).
All said and done, if your input is already in IEEE-754 format (albeit in hex), then a simple cast ought to be enough.
Just do the following (but of course make sure you have the right endianness when reading bytes into the integer in the first place):
float int_bits_to_float(int32_t ieee754_bits) {
float flt;
*((int*) &flt) = ieee754_bits;
return flt;
}
Works for me... this of course assumes that float
has 32 bits, and is in IEEE754 format, on your architecture (which is almost always the case).
As well as being much simpler, this also avoids any rounding/precision errors.
float value = reinterpret_cast<float&>(myNumba)
If you still want to inspect the parts separately, use the library function std::frexp
afterwards. Of if you don't like the type punning, at least use std::ldexp
to apply the exponent rather than your explicit maths, which is vulnerable to rounding/precision errors and overflow.
An alternate to both of these is to use a union type, as described in this answer.
User contributions licensed under CC BY-SA 3.0