C - Serialization of the floating point numbers (floats, doubles)

后端 未结 10 1018
攒了一身酷
攒了一身酷 2020-11-28 11:18

How to convert a floating point number into a sequence of bytes so that it can be persisted in a file? Such algorithm must be fast and highly portable. It must allow also th

相关标签:
10条回答
  • 2020-11-28 12:00

    This might give you a good start - it packs a floating point value into an int and long long pair, which you can then serialise in the usual way.

    #define FRAC_MAX 9223372036854775807LL /* 2**63 - 1 */
    
    struct dbl_packed
    {
        int exp;
        long long frac;
    };
    
    void pack(double x, struct dbl_packed *r)
    {
        double xf = fabs(frexp(x, &r->exp)) - 0.5;
    
        if (xf < 0.0)
        {
            r->frac = 0;
            return;
        }
    
        r->frac = 1 + (long long)(xf * 2.0 * (FRAC_MAX - 1));
    
        if (x < 0.0)
            r->frac = -r->frac;
    }
    
    double unpack(const struct dbl_packed *p)
    {
        double xf, x;
    
        if (p->frac == 0)
            return 0.0;
    
        xf = ((double)(llabs(p->frac) - 1) / (FRAC_MAX - 1)) / 2.0;
    
        x = ldexp(xf + 0.5, p->exp);
    
        if (p->frac < 0)
            x = -x;
    
        return x;
    }
    
    0 讨论(0)
  • 2020-11-28 12:02

    You could always convert to IEEE-754 format in a fixed byte order (either little endian or big endian). For most machines, that would require either nothing at all or a simple byte swap to serialize and deserialize. A machine that doesn't support IEEE-754 natively will need a converter written, but doing that with ldexp and frexp (stanard C library functions)and bit shuffling is not too tough.

    0 讨论(0)
  • 2020-11-28 12:03

    This version has excess of only one byte per one floating point value to indicate the endianness. But I think, it is still not very portable however.

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <ctype.h>
    
    #define LITEND      'L'
    #define BIGEND      'B'
    
    typedef short               INT16;
    typedef int                 INT32;
    typedef double              vec1_t;
    
     typedef struct {
        FILE            *fp;
    } WFILE, RFILE;
    
    #define w_byte(c, p)    putc((c), (p)->fp)
    #define r_byte(p)       getc((p)->fp)
    
    static void w_vec1(vec1_t v1_Val, WFILE *p)
    {
        INT32   i;
        char    *pc_Val;
    
        pc_Val = (char *)&v1_Val;
    
        w_byte(LITEND, p);
        for (i = 0; i<sizeof(vec1_t); i++)
        {
            w_byte(pc_Val[i], p);
        }
    }
    
    
    static vec1_t r_vec1(RFILE *p)
    {
        INT32   i;
        vec1_t  v1_Val;
        char    c_Type,
                *pc_Val;
    
        pc_Val = (char *)&v1_Val;
    
        c_Type = r_byte(p);
        if (c_Type==LITEND)
        {
            for (i = 0; i<sizeof(vec1_t); i++)
            {
                pc_Val[i] = r_byte(p);
            }
        }
        return v1_Val;
    }
    
    int main(void)
    {
        WFILE   x_FileW,
                *px_FileW = &x_FileW;
        RFILE   x_FileR,
                *px_FileR = &x_FileR;
    
        vec1_t  v1_Val;
        INT32   l_Val;
        char    *pc_Val = (char *)&v1_Val;
        INT32   i;
    
        px_FileW->fp = fopen("test.bin", "w");
        v1_Val = 1234567890.0987654321;
        printf("v1_Val before write = %.20f \n", v1_Val);
        w_vec1(v1_Val, px_FileW);
        fclose(px_FileW->fp);
    
        px_FileR->fp = fopen("test.bin", "r");
        v1_Val = r_vec1(px_FileR);
        printf("v1_Val after read = %.20f \n", v1_Val);
        fclose(px_FileR->fp);
        return 0;
    }
    
    0 讨论(0)
  • 2020-11-28 12:15

    Assuming you're using mainstream compilers, floating point values in C and C++ obey the IEEE standard and when written in binary form to a file can be recovered in any other platform, provided that you write and read using the same byte endianess. So my suggestion is: pick an endianess of choice, and before writing or after reading, check if that endianess is the same as in the current platform; if not, just swap the bytes.

    0 讨论(0)
提交回复
热议问题