C - 浮動小数点数のシリアル化(浮動小数点数、倍精度)
-
21-09-2019 - |
質問
どのようにそれがファイルに永続化することができますので、バイトシーケンスに浮動小数点数に変換するには?このようなアルゴリズムは、高速かつ携帯性に優れている必要があります。また、逆の操作、直列化復元を許可する必要があります。値(永続的なスペース)あたりのビットだけに非常に小さな過剰が必要とされる場合、それはいいだろう。
解決
IEEE標準に従うCおよびC ++での浮動小数点値を、あなたが主流のコンパイラを使用していると仮定すると、あなたが書くことを提供し、他のプラットフォームで回収し、同じバイトエンディアンを使用して読み取ることができるファイルにバイナリ形式で記述されたとき。ある私の提案はそう:選択のエンディアンを選択し、書き込む前または読んだ後、そのエンディアンが現在のプラットフォームの場合と同じであるかどうかをチェック。ない場合は、単にバイトをスワップします。
他のヒント
あなたは常に一定のバイト順(リトルエンディアンまたはビッグエンディアンのどちらか)にIEEE-754形式に変換することができます。ほとんどのマシンでは、それがすべてでは何もまたはシリアル化および逆シリアル化の単純なバイトスワップのいずれかを必要としないでしょう。 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特性
[...] 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の?あなたはそれよりも多くの移植を得ることはありません。
ポータビリティのどのレベルあなたは必要ですか?ファイルは、それが上に生成されたのと同じOSを備えたコンピュータ上で読み取ることがある場合は、バイナリファイルを使用して、ちょうどビットパターンの保存と復元以上が動作するはずです。 boytheoが言ったようにそれ以外の場合は、ASCIIはあなたの友達です。
このバージョンでは、エンディアンを示すために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;
}
ここで行くます。
ポータブル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()を犠牲にしたくない限り、任意のタイトバイト詰めることはできません。フロート;ダブルB; =(FLOAT)B。 fwriteの( - 、1、はsizeof()、FP);
あなたは離れてビットを選択し、数学を実行する必要がありますので、彼らは、これがその力に、ストレート・バイナリ意味で変換されないことがあなたの周りにはさまざまな浮動小数点形式を持っている場合はプラスこれなどIEEE 754であります恐ろしい使用する標準が、それは努力を最小限にするように、広範ます。