سؤال

كيفية تحويل رقم النقطة العائمة إلى سلسلة من البايتات بحيث يمكن استمراره في ملف؟ يجب أن تكون هذه الخوارزمية سريعة ومحمولة للغاية. يجب أن تسمح أيضا العملية المعاكسة ، هجر. سيكون من الرائع أن يكون هناك حاجة إلى فائض صغير جدًا من البتات لكل قيمة (مساحة ثابتة).

هل كانت مفيدة؟

المحلول

على افتراض أنك تستخدم المجمعين الرئيسيين ، يمكن استرداد قيم النقاط العائمة في C و C ++ لمعايير IEEE وعندما يتم كتابتها في شكل ثنائي إلى ملف في أي منصة أخرى ، شريطة أن تكتب وقراءة باستخدام نفس البايت Endianess. لذا فإن اقتراحي هو: اختر endians من الاختيار ، وقبل الكتابة أو بعد القراءة ، تحقق مما إذا كان هذا endianess هو نفسه كما في المنصة الحالية ؛ إذا لم يكن كذلك ، فقط تبديل البايتات.

نصائح أخرى

يمكنك دائمًا التحويل إلى تنسيق IEEE-754 بترتيب بايت ثابت (إما Little Endian أو Big Endian). بالنسبة لمعظم الآلات ، لن يتطلب ذلك أي شيء على الإطلاق أو مبادلة بايت بسيطة لإجراء تسلسل وتصاريح. سيحتاج جهاز لا يدعم IEEE-754 أصلاً إلى محول مكتوب ، ولكن القيام بذلك مع LDEXP و FREXP (وظائف مكتبة Stanard C) وخلط البت ليس صعبًا للغاية.

قد يمنحك هذا بداية جيدة - إنه يحزم قيمة نقطة عائمة في int و long long الزوج ، الذي يمكنك بعد ذلك التسلسل بالطريقة المعتادة.

#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;
}

ماذا تقصد ، "محمول"؟

من أجل قابلية النقل ، تذكر أن تبقي الأرقام ضمن الحدود المحددة في المعيار: استخدم رقمًا واحدًا خارج هذه الحدود ، ويذهب كل قابلية النطاق إلى أسفل.

double planck_time = 5.39124E-44; /* second */

5.2.4.2.2 خصائص الأنواع العائمةu003Cfloat.h>

[...]
10   The values given in the following list shall be replaced by constant
     expressions with implementation-defined values [...]
11   The values given in the following list shall be replaced by constant
     expressions with implementation-defined values [...]
12   The values given in the following list shall be replaced by constant
     expressions with implementation-defined (positive) values [...]
[...]

لاحظ ال محدد التنفيذ في كل هذه البنود.

سيكون التحويل إلى تمثيل ASCII هو أبسط ، ولكن إذا كنت بحاجة إلى التعامل مع عدد هائل من العوامات ، فعندئذ يجب أن تذهب ثنائيًا. ولكن يمكن أن تكون هذه مشكلة صعبة إذا كنت تهتم بقابلية النقل. يتم تمثيل أرقام النقاط العائمة بشكل مختلف في الأجهزة المختلفة.

إذا كنت لا ترغب في استخدام مكتبة معلبة ، فسيتعين على المسلسل/المسلسل الثنائي الخاص بك ببساطة "عقد" حول كل شيء يهبط وما يمثله.

إليك موقع ويب ممتع للمساعدة في ذلك: حلقة الوصل.

Sprintf ، fprintf؟ أنت لا تحصل على أكثر من ذلك.

ما هو مستوى قابلية النقل التي تحتاجها؟ إذا كان من المقرر قراءة الملف على جهاز كمبيوتر بنفس نظام التشغيل الذي تم إنشاؤه عليه ، فبإمكانك استخدام ملف ثنائي وحفظ نمط البتات واستعادتها فقط. خلاف ذلك كما قال Boytheo ، ASCII هو صديقك.

يحتوي هذا الإصدار على فائض من بايت واحد فقط لكل نقطة عائمة واحدة للإشارة إلى endianness. لكنني أعتقد أنه لا يزال غير محمول للغاية.

#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;
}

ها نحن ذا.

Portable IEEE 754 التسلسل / التسلسل الذي يجب أن يعمل بغض النظر عن تمثيل النقطة العائمة الداخلية للآلة.

https://github.com/malcolmmclean/ieee754

/*
* read a double from a stream in ieee754 format regardless of host
*  encoding.
*  fp - the stream
*  bigendian - set to if big bytes first, clear for little bytes
*              first
*
*/
double freadieee754(FILE *fp, int bigendian)
{
    unsigned char buff[8];
    int i;
    double fnorm = 0.0;
    unsigned char temp;
    int sign;
    int exponent;
    double bitval;
    int maski, mask;
    int expbits = 11;
    int significandbits = 52;
    int shift;
    double answer;

    /* read the data */
    for (i = 0; i < 8; i++)
        buff[i] = fgetc(fp);
    /* just reverse if not big-endian*/
    if (!bigendian)
    {
        for (i = 0; i < 4; i++)
        {
            temp = buff[i];
            buff[i] = buff[8 - i - 1];
            buff[8 - i - 1] = temp;
        }
    }
    sign = buff[0] & 0x80 ? -1 : 1;
    /* exponet in raw format*/
    exponent = ((buff[0] & 0x7F) << 4) | ((buff[1] & 0xF0) >> 4);

    /* read inthe mantissa. Top bit is 0.5, the successive bits half*/
    bitval = 0.5;
    maski = 1;
    mask = 0x08;
    for (i = 0; i < significandbits; i++)
    {
        if (buff[maski] & mask)
            fnorm += bitval;

        bitval /= 2.0;
        mask >>= 1;
        if (mask == 0)
        {
            mask = 0x80;
            maski++;
        }
    }
    /* handle zero specially */
    if (exponent == 0 && fnorm == 0)
        return 0.0;

    shift = exponent - ((1 << (expbits - 1)) - 1); /* exponent = shift + bias */
    /* nans have exp 1024 and non-zero mantissa */
    if (shift == 1024 && fnorm != 0)
        return sqrt(-1.0);
    /*infinity*/
    if (shift == 1024 && fnorm == 0)
    {

#ifdef INFINITY
        return sign == 1 ? INFINITY : -INFINITY;
#endif
        return  (sign * 1.0) / 0.0;
    }
    if (shift > -1023)
    {
        answer = ldexp(fnorm + 1.0, shift);
        return answer * sign;
    }
    else
    {
        /* denormalised numbers */
        if (fnorm == 0.0)
            return 0.0;
        shift = -1022;
        while (fnorm < 1.0)
        {
            fnorm *= 2;
            shift--;
        }
        answer = ldexp(fnorm, shift);
        return answer * sign;
    }
}


/*
* 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.
*/
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);
}

Fwrite () ، Fread ()؟ من المحتمل أن تريد ثنائيًا ، ولا يمكنك تعبئة البايتات أي أكثر إحكاما إلا إذا كنت تريد التضحية بالدقة التي ستفعلها في البرنامج ثم Fwrite () fread () على أي حال ؛ تعويم أ ؛ مزدوج ب ؛ a = (تعويم) ب ؛ Fwrite (& a ، 1 ، sizeof (a) ، fp) ؛

إذا كنت تحمل تنسيقات نقاط عائمة مختلفة حولها ، فقد لا يتم تحويلها بالمعنى الثنائي المستقيم ، لذلك قد تضطر استخدم ولكن على نطاق واسع بحيث يقلل من الجهد.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top