cfloating-pointdata-conversionmantissa

Converting given mantissa, exponent, and sign to float?


I am given the mantissa, exponent, and sign and I have to convert it into the corresponding float. I am using 22 bits for mantissa, 9 bits for exponent, and 1 bit for the sign.

I conceptually know how to convert them into a float, first adjusting the exponent back to its place, then converting the resulting number back into a float, but I'm having trouble implementing this in C. I saw this thread, but I couldn't understand the code, and I'm not sure the answer is even right. Can anyone point me in the right direction? I need to code it in C

Edit: I've made some progress by first converting the mantissa into binary, then adjusting the decimal point of the binary, then converting the decimal-point binary back into the actual float. I based my conversion functions off these two GeekforGeek pages (one, two) But it seems like doing all these binary conversions is doing it the long and hard way. The link above apparently does it in very little steps by using the >> operators, but I don't understand exactly how that results in a float.


Solution

  • Here is a program with comments explaining the decoding:

    #include <inttypes.h>
    #include <math.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    
    //  Define constants describing the floating-point encoding.
    enum
    {
        SignificandBits = 22,   //  Number of bits in signficand field.
        ExponentBits    =  9,   //  Number of bits in exponent field.
    
        ExponentMaximum = (1 << ExponentBits) - 1,
        ExponentBias    = (1 << ExponentBits-1) - 1,
    };
    
    
    /*  Given the contents of the sign, exponent, and significand fields that
        encode a floating-point number following IEEE-754 patterns for binary
        floating-point, return the encoded number.
    
        "double" is used for the return type as not all values represented by the
        sample format (9 exponent bits, 22 significand bits) will fit in a "float"
        when it is the commonly used IEEE-754 binary32 format.
    */
    double DecodeCustomFloat(
        unsigned SignField, uint32_t ExponentField, uint32_t SignificandField)
    {
        /*  We are given a significand field as an integer, but it is used as the
            value of a binary numeral consisting of “.” followed by the significand
            bits.  That value equals the integer divided by 2 to the power of the
            number of significand bits.  Define a constant with that value to be
            used for converting the significand field to represented value.
        */
        static const double SignificandRatio = (uint32_t) 1 << SignificandBits;
    
        /*  Decode the sign field:
    
                If the sign bit is 0, the sign is +, for which we use +1.
                If the sign bit is 1, the sign is -, for which we use -1.
        */
        double Sign = SignField ? -1. : +1.;
    
        //  Dispatch to handle the different categories of exponent field.
        switch (ExponentField)
        {
            /*  When the exponent field is all ones, the value represented is a
                NaN or infinity:
    
                    If the significand field is zero, it is an infinity.
                    Otherwise, it is a NaN.  In either case, the sign should be
                    preserved.
    
                Note this is a simple demonstration implementation that does not
                preserve the bits in the significand field of a NaN -- we just
                return the generic NAN without attempting to set its significand
                bits.
            */
            case ExponentMaximum:
            {
                return Sign * (SignificandField ? NAN : INFINITY);
            }
    
            /*  When the exponent field is not all zeros or all ones, the value
                represented is a normal number:
    
                    The exponent represented is ExponentField - ExponentBias, and
                    the significand represented is the value given by the binary
                    numeral “1.” followed by the significand bits.
            */
            default:
            {
                int    Exponent = ExponentField - ExponentBias;
                double Significand = 1 + SignificandField / SignificandRatio;
                return Sign * ldexp(Significand, Exponent);
            }
    
            /*  When the exponent field is zero, the value represented is subnormal:
    
                    The exponent represented is 1 - ExponentBias, and the
                    significand represented is the value given by the binary
                    numeral “0.” followed by the significand bits.
            */
            case 0:
            {
                int    Exponent = 1 - ExponentBias;
                double Significand = 0 + SignificandField / SignificandRatio;
                return Sign * ldexp(Significand, Exponent);
            }
        }
    }
    
    
    /*  Test that a given set of fields decodes to the expected value and
        print the fields and the decoded value.
    */
    static void Demonstrate(
        unsigned SignField, uint32_t SignificandField, uint32_t ExponentField,
        double Expected)
    {
        double Observed
            = DecodeCustomFloat(SignField, SignificandField, ExponentField);
    
        if (! (Observed == Expected) && ! (isnan(Observed) && isnan(Expected)))
        {
            fprintf(stderr,
                "Error, expected (%u, %" PRIu32 ", %" PRIu32 ") to represent "
                "%g (hexadecimal %a) but got %g (hexadecimal %a).\n",
                SignField, SignificandField, ExponentField,
                Expected, Expected,
                Observed, Observed);
            exit(EXIT_FAILURE);
        }
    
        printf(
            "(%u, %" PRIu32 ", %" PRIu32 ") represents %g (hexadecimal %a).\n",
            SignField, SignificandField, ExponentField, Observed, Observed);
    }
    
    
    int main(void)
    {
        Demonstrate(0, 0, 0, +0.);
        Demonstrate(1, 0, 0, -0.);
        Demonstrate(0, 255, 0, +1.);
        Demonstrate(1, 255, 0, -1.);
        Demonstrate(0, 511, 0, +INFINITY);
        Demonstrate(1, 511, 0, -INFINITY);
        Demonstrate(0, 511, 1, +NAN);
        Demonstrate(1, 511, 1, -NAN);
        Demonstrate(0, 0, 1, +0x1p-276);
        Demonstrate(1, 0, 1, -0x1p-276);
        Demonstrate(0, 255, 1, +1. + 0x1p-22);
        Demonstrate(1, 255, 1, -1. - 0x1p-22);
        Demonstrate(0, 1, 0, +0x1p-254);
        Demonstrate(1, 1, 0, -0x1p-254);
        Demonstrate(0, 510, 0x3fffff, +0x1p256 - 0x1p233);
        Demonstrate(1, 510, 0x3fffff, -0x1p256 + 0x1p233);
    }
    

    Some notes: