14

浮動小数点数をバイトのシーケンスに変換して、ファイルに永続化できるようにするにはどうすればよいですか?このようなアルゴリズムは、高速で移植性が高い必要があります。逆の操作である逆シリアル化も許可する必要があります。値あたりのビット数(永続スペース)がごくわずかに超過するだけでよい場合は便利です。

4

10 に答える 10

13

主流のコンパイラを使用していると仮定すると、CおよびC ++の浮動小数点値はIEEE標準に準拠しており、同じバイトエンディアンを使用して書き込みと読み取りを行う場合は、バイナリ形式でファイルに書き込むと、他のプラットフォームで復元できます。したがって、私の提案は次のとおりです。選択したエンディアンを選択し、書き込む前または読み取った後、そのエンディアンが現在のプラットフォームと同じであるかどうかを確認します。そうでない場合は、バイトを交換するだけです。

于 2009-11-23T22:10:06.150 に答える
3

常に固定バイトオーダー(リトルエンディアンまたはビッグエンディアン)でIEEE-754形式に変換できます。ほとんどのマシンでは、シリアル化と逆シリアル化するために、何も必要としないか、単純なバイトスワップが必要になります。IEEE-754をネイティブにサポートしていないマシンでは、コンバーターを作成する必要がありますが、ldexpand frexp(標準Cライブラリ関数)とビットシャッフルを使用してそれを行うのはそれほど難しくありません。

于 2009-11-23T22:29:41.650 に答える
3

これにより、良いスタートを切ることができます。浮動小数点値を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;
}
于 2009-11-24T00:06:16.100 に答える
2

「ポータブル」とはどういう意味ですか?

移植性のために、番号を標準で定義された制限内に保つことを忘れないでください。これらの制限外の単一の番号を使用すると、すべての移植性が低下します。

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

5.2.4.2.2フローティングタイプの特性<float.h>

[...]
10次​​のリストに示されている値は、定数に置き換えられます。
     実装定義の値を持つ式[...]
11次のリストに示されている値は、定数に置き換えられます。
     実装定義の値を持つ式[...]
12次のリストに示されている値は、定数に置き換えられます。
     実装で定義された(正の)値を持つ式[...]
[...]

これらすべての句で定義されている実装に注意してください。

于 2009-11-23T22:10:20.020 に答える
1

ASCII表現への変換が最も簡単ですが、膨大な数のfloatを処理する必要がある場合は、もちろんバイナリにする必要があります。ただし、移植性を気にする場合、これは難しい問題になる可能性があります。浮動小数点数は、マシンごとに異なる方法で表されます。

既定のライブラリを使用したくない場合は、float-binaryシリアライザー/デシリアライザーは、各ビットがどこに到達し、それが何を表すかについて「契約」を結ぶ必要があります。

これを支援する楽しいウェブサイトがあります:リンク

于 2009-11-23T21:55:08.950 に答える
0

sprintf、fprintf?それ以上のポータブル性はありません。

于 2009-11-23T21:34:25.580 に答える
0

どのレベルの移植性が必要ですか?ファイルが生成されたのと同じOSを搭載したコンピューターでファイルを読み取る場合は、バイナリファイルを使用して、ビットパターンを保存および復元するだけで機能するはずです。そうでなければ、boytheoが言ったように、ASCIIはあなたの友達です。

于 2009-11-23T21:50:21.057 に答える
0

このバージョンでは、エンディアンを示すために、1つの浮動小数点値ごとに1バイトしか超過していません。しかし、私はそれがまだあまりポータブルではないと思います。

#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;
}
于 2009-11-24T10:50:01.673 に答える
0

どうぞ。

マシンの内部浮動小数点表現に関係なく機能するポータブルIEEE754シリアル化/逆シリアル化。

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);
}
于 2016-08-27T05:44:27.750 に答える
-1

fwrite()、fread()?おそらくバイナリが必要になるでしょう。プログラムで行う精度を犠牲にして、とにかくfwrite()fread()を実行したい場合を除いて、バイトをこれ以上密にパックすることはできません。フロートa; ダブルb; a =(float)b; fwrite(&a、1、sizeof(a)、fp);

さまざまな浮動小数点形式を使用している場合、それらは単精度の2進数の意味で変換されない可能性があるため、ビットを分解して計算を実行する必要がある場合があります。これに加えて、IEEE754は次のような恐ろしい標準です。使用しますが、広く普及しているため、労力を最小限に抑えることができます。

于 2009-11-26T02:12:53.393 に答える