Search code examples
cnumbersfloating

getting exponent of a floating number in c


Sorry if this is already been asked, and I've seen other way of extracting the exponent of a floating point number, however this is what is given to me:

unsigned f2i(float f)
{
  union {
    unsigned i;
    float f;
  } x;
  x.i = 0;
  x.f = f;
  return x.i;
}

I'm having trouble understanding this union datatype, because shouldn't the return x.i at the end always make f2i return a 0?

Also, what application could this data type even be useful for? For example, say I have a function:

int getexponent(float f){
}

This function is supposed to get the exponent value of the floating point number with bias of 127. I've found many ways to make this possible, however how could I manipulate the f2i function to serve this purpose?

I appreciate any pointers!

Update!! Wow, years later and this just seem trivial. For those who may be interested, here is the function!

int getexponent(float f) {
    unsigned f2u(float f); 
	unsigned int ui = (f2u(f)>>23) & 0xff ;//shift over by 23 and compare to 0xff to get the exponent with the bias 
	int bias = 127;//initialized bias 
	if(ui == 0) return 1-bias; // special case 0
	else if(ui == 255) return 11111111; //special case infinity
	return ui - bias;
}


Solution

  • I'm having trouble understanding this union datatype

    The union data type is a way for a programmer to indicate that some variable can be one of a number of different types. The wording of the C11 standard is something like "a union contains at most one of its members". It is used for things like parameters that may be logically one thing or another. For example, an IP address might be an IPv4 address or an IPv6 address so you might define an address type as follows:

    struct IpAddress
    {
        bool isIPv6;
        union 
        {
            uint8_t v4[4];
            uint8_t v6[16];
        } bytes;
    }
    

    And you would use it like this:

    struct IpAddress address = // Something
    if (address.isIPv6)
    {
        doSomeV6ThingWith(address.bytes.v6);
    }
    else 
    {
        doSomeV4ThingWith(address.bytes.v4);
    }
    

    Historically, unions have also been used to get the bits of one type into an object of another type. This is because, in a union, the members all start at the same memory address. If I just do this:

    float f = 3.0;
    int i = f;
    

    The compiler will insert code to convert a float to an integer, so the exponent will be lost. However, in

    union 
    {
        unsigned int i;
        float f;
    } x;
    
    x.f = 3.0;
    int i = x.i;
    

    i now contains the exact bits that represent 3.0 in a float. Or at least you hope it does. There's nothing in the C standard that says float and unsigned int have to be the same size. There's also nothing in the C standard that mandates a particular representation for float (well, annex F says floats conform to IEC 60559 , but I don't know if that counts as part of the standard). So the above code is, at best, non portable.

    To get the exponent of a float the portable way is the frexpf() function defined in math.h

    how could I manipulate the f2i function to serve this purpose?

    Let's make the assumption that a float is stored in IEC 60559 format in 32 bits which Wkipedia thinks is the same as IEEE 754. Let's also assume that integers are stored in little endian format.

    union 
    {
        uint32_t i;
        float f;
    } x;
    
    x.f = someFloat;
    uint32_t bits = x.i;
    

    bits now contains the bit pattern of the floating point number. A single precision floating point number looks like this

    SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMM
    ^        ^                     ^
    bit 31   bit 22                bit 0
    

    Where S is the sign bit, E is an exponent bit, M is a mantissa bit.

    So having got your int32_t you just need to do some shifting and masking:

    uint32_t exponentWithBias = (bits >> 23) & 0xff;