JustBlossom JustBlossom - 10 days ago 11
C Question

C Bit-Level Int to Float Conversion Unexpected Output

Background:

I am playing around with bit-level coding (this is not homework - just curious). I found a lot of good material online and in a book called Hacker's Delight, but I am having trouble with one of the online problems.

It asks to convert an integer to a float. I used the following links as reference to work through the problem:

How to manually (bitwise) perform (float)x?

How to convert an unsigned int to a float?

http://locklessinc.com/articles/i2f/

Problem and Question:

I thought I understood the process well enough (I tried to document the process in the comments), but when I test it, I don't understand the output.

Test Cases:

float_i2f(2) returns 1073741824

float_i2f(3) returns 1077936128


I expected to see something like 2.0000 and 3.0000.

Did I mess up the conversion somewhere? I thought maybe this was a memory address, so I was thinking maybe I missed something in the conversion step needed to access the actual number? Or maybe I am printing it incorrectly? I am printing my output like this:

printf("Float_i2f ( %d ): ", 3);
printf("%u", float_i2f(3));
printf("\n");


But I thought that printing method was fine for unsigned values in C (I'm used to programming in Java).

Thanks for any advice.

Code:


/*
* float_i2f - Return bit-level equivalent of expression (float) x
* Result is returned as unsigned int, but
* it is to be interpreted as the bit-level representation of a
* single-precision floating point values.
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*/
unsigned float_i2f(int x) {
if (x == 0){
return 0;
}

//save the sign bit for later and get the asolute value of x
//the absolute value is needed to shift bits to put them
//into the appropriate position for the float
unsigned int signBit = 0;
unsigned int absVal = (unsigned int)x;

if (x < 0){
signBit = 0x80000000;
absVal = (unsigned int)-x;
}

//Calculate the exponent
// Shift the input left until the high order bit is set to form the mantissa.
// Form the floating exponent by subtracting the number of shifts from 158.
unsigned int exponent = 158; //158 possibly because of place in byte range

while ((absVal & 0x80000000) == 0){//this checks for 0 or 1. when it reaches 1, the loop breaks
exponent--;
absVal <<= 1;
}

//find the mantissa (bit shift to the right)
unsigned int mantissa = absVal >> 8;

//place the exponent bits in the right place
exponent = exponent << 23;

//get the mantissa
mantissa = mantissa & 0x7fffff;

//return the reconstructed float
return signBit | exponent | mantissa;
}

Answer

Continuing from the comment. Your code is correct, and you are simply looking at the equivalent unsigned integer made up by the bits in your IEEE-754 single-precision floating point number. The IEEE-754 single-precision number format (made up of the sign, extended exponent, and mantissa), can be interpreted as a float, or those same bits can be interpreted as an unsigned integer (just the number that is made up by the 32-bits). You are outputting the unsigned equivalent for the floating point number.

You can confirm with a simple union. For example:

#include <stdio.h>
#include <stdint.h>

typedef union {
    uint32_t u;
    float f;
} u2f;

int main (void) {

    u2f tmp = { .f = 2.0 };
    printf ("\n u : %u\n f : %f\n", tmp.u, tmp.f);

    return 0;
}

Example Usage/Output

$ ./bin/unionuf

 u : 1073741824
 f : 2.000000

Let me know if you have any further questions. It's good to see that your study resulted in the correct floating point conversion. (also note the second comment regarding truncation/rounding)