Portable serialisation of IEEE754 floating-point values

后端 未结 3 1835
悲&欢浪女
悲&欢浪女 2020-12-17 17:51

I\'ve recently been working on a system that needs to store and load large quantities of data, including single-precision floating-point values. I decided to standardise on

3条回答
  •  萌比男神i
    2020-12-17 17:56

    Here's a portable IEEE 754 write routine. It will write a double in IEEE 754 format, regardless of the floating point representation on the host machine.

    /*
    * write a double to a stream in ieee754 format regardless of host
    *  encoding.
    *  x - number to write
    *  fp - the stream
    *  bigendian - set to write big bytes first, elee write litle bytes
    *              first
    *  Returns: 0 or EOF on error
    *  Notes: different NaN types and negative zero not preserved.
    *         if the number is too big to represent it will become infinity
    *         if it is too small to represent it will become zero.
    */
    static int fwriteieee754(double x, FILE *fp, int bigendian)
    {
        int shift;
        unsigned long sign, exp, hibits, hilong, lowlong;
        double fnorm, significand;
        int expbits = 11;
        int significandbits = 52;
    
        /* zero (can't handle signed zero) */
        if (x == 0)
        {
            hilong = 0;
            lowlong = 0;
            goto writedata;
        }
        /* infinity */
        if (x > DBL_MAX)
        {
            hilong = 1024 + ((1 << (expbits - 1)) - 1);
            hilong <<= (31 - expbits);
            lowlong = 0;
            goto writedata;
        }
        /* -infinity */
        if (x < -DBL_MAX)
        {
            hilong = 1024 + ((1 << (expbits - 1)) - 1);
            hilong <<= (31 - expbits);
            hilong |= (1 << 31);
            lowlong = 0;
            goto writedata;
        }
        /* NaN - dodgy because many compilers optimise out this test, but
        *there is no portable isnan() */
        if (x != x)
        {
            hilong = 1024 + ((1 << (expbits - 1)) - 1);
            hilong <<= (31 - expbits);
            lowlong = 1234;
            goto writedata;
        }
    
        /* get the sign */
        if (x < 0) { sign = 1; fnorm = -x; }
        else { sign = 0; fnorm = x; }
    
        /* get the normalized form of f and track the exponent */
        shift = 0;
        while (fnorm >= 2.0) { fnorm /= 2.0; shift++; }
        while (fnorm < 1.0) { fnorm *= 2.0; shift--; }
    
        /* check for denormalized numbers */
        if (shift < -1022)
        {
            while (shift < -1022) { fnorm /= 2.0; shift++; }
            shift = -1023;
        }
        /* out of range. Set to infinity */
        else if (shift > 1023)
        {
            hilong = 1024 + ((1 << (expbits - 1)) - 1);
            hilong <<= (31 - expbits);
            hilong |= (sign << 31);
            lowlong = 0;
            goto writedata;
        }
        else
            fnorm = fnorm - 1.0; /* take the significant bit off mantissa */
    
        /* calculate the integer form of the significand */
        /* hold it in a  double for now */
    
        significand = fnorm * ((1LL << significandbits) + 0.5f);
    
    
        /* get the biased exponent */
        exp = shift + ((1 << (expbits - 1)) - 1); /* shift + bias */
    
        /* put the data into two longs (for convenience) */
        hibits = (long)(significand / 4294967296);
        hilong = (sign << 31) | (exp << (31 - expbits)) | hibits;
        x = significand - hibits * 4294967296;
        lowlong = (unsigned long)(significand - hibits * 4294967296);
    
    writedata:
        /* write the bytes out to the stream */
        if (bigendian)
        {
            fputc((hilong >> 24) & 0xFF, fp);
            fputc((hilong >> 16) & 0xFF, fp);
            fputc((hilong >> 8) & 0xFF, fp);
            fputc(hilong & 0xFF, fp);
    
            fputc((lowlong >> 24) & 0xFF, fp);
            fputc((lowlong >> 16) & 0xFF, fp);
            fputc((lowlong >> 8) & 0xFF, fp);
            fputc(lowlong & 0xFF, fp);
        }
        else
        {
            fputc(lowlong & 0xFF, fp);
            fputc((lowlong >> 8) & 0xFF, fp);
            fputc((lowlong >> 16) & 0xFF, fp);
            fputc((lowlong >> 24) & 0xFF, fp);
    
            fputc(hilong & 0xFF, fp);
            fputc((hilong >> 8) & 0xFF, fp);
            fputc((hilong >> 16) & 0xFF, fp);
            fputc((hilong >> 24) & 0xFF, fp);
        }
        return ferror(fp);
    }
    

提交回复
热议问题