3

私は次のようにdouble値を文字列に変換しています:

std::string conv(double x) {
    char buf[30];
    sprintf(buf, "%.20g", x);
    return buf;
}

バッファサイズを30にハードコーディングしましたが、これがすべての場合に十分な大きさであるかどうかはわかりません。

  • 必要な最大バッファーサイズを確認するにはどうすればよいですか?
  • 32ビットから64ビットに切り替えると、精度が高くなりますか(したがって、バッファーを増やす必要がありますか)?

PS:使用できない、ostringstreamまたはboost::lexical_castパフォーマンス上の理由(これを参照)

4

5 に答える 5

3

printf("%.20g", 1.79769e+308);1.7976900000000000632e+308、末尾の\0を含めて27バイトです。念のため、64または128を選択します。

(スタック上にあり、すぐにリリースされるため、組み込み以外のアプリケーションで問題が発生することなく、2048バイトの大きなバッファーを使用することもできます)

また、プログラムのボトルネックはlexical_cast..ですか?あなたがしていることをすることは私には非常にばかげているようです

于 2009-12-19T20:46:13.733 に答える
3

sprintf宛先を指定して呼び出してNULLも何も起こらないことを覚えているようです。ただし、「書き込んだ」文字数は返します。私が正しければ(そしてそのソースが見つからないようです)、次のことができます:

// find the length of the string
int len = sprintf(NULL, fmt, var1, var2,...);
// allocate the necessary memory.
char *output = malloc(sizeof(char) * (len + 1)); // yes I know that sizeof(char) is defined as 1 but this seems nicer.
// now sprintf it after checking for errors
sprintf(output, fmt, var1, var2,...);

もう 1 つのオプションはsnprintf、出力の長さを制限するために使用することです。

#define MAX 20 /* or whatever length you want */
char output[MAX];
snprintf(output, MAX, fmt, var1, var2,...);

snprintfバッファのサイズを引数として取り、出力文字列がそのサイズを超えることを許可しません。

于 2009-12-19T20:58:42.957 に答える
3

バッファ サイズを 30 にハードコードしましたが、これがすべてのケースに十分な大きさかどうかはわかりません。

です。%.20g は、仮数部に 20 桁を指定します。小数点に 1 を追加します。(可能な) 符号の場合は 1、"e+308" または "e-308" の場合は 5、最悪の場合の指数。null を終了する場合は 1。

20 + 1 + 1 + 5 + 1 = 28。

32 ビットから 64 ビットに切り替えると、精度が高くなります (したがって、バッファを増やす必要があります)。

いいえ。

double は、両方のアーキテクチャで同じサイズです。変数を long double として宣言すると、指数 "e+4092" にさらに 1 桁ある可能性がありますが、それでも 30 文字のバッファーに収まります。ただし、X86 のみ、および古いプロセッサのみです。

long double は、486 FPU のネイティブ形式であった浮動小数点値の廃止された 80 ビット形式です。その FPU アーキテクチャはうまくスケーリングできず、それ以来、考えられる最大の浮動小数点値が 64 ビット double である SSE スタイルの命令を支持して破棄されました。

これは、出力の仮数部を 20 桁に制限し続ける限り、30 文字のバッファーで常に十分であると言う長い方法です。

于 2009-12-19T22:00:00.693 に答える
0

doubleこれは、任意のシステムで最大値と最小値に必要な桁数を出力するプログラムです。

#include <float.h>
#include <stdio.h>

int main(void)
{
    double m = DBL_MAX;
    double n = DBL_MIN;
    int i;
    i = printf("%.20g\n", m);
    printf("%d\n", i);
    i = printf("%.20g\n", n);
    printf("%d\n", i);
    return 0;
}

私にとって、それは印刷します:

1.7976931348623157081e+308
27
2.2250738585072013831e-308
27

27には改行が含まれていますが0、文字列の終端は含まれていないため、このシステムでは27で十分です。の場合、私のシステムlong doubleでは、答えはそれぞれ27と28のようLDBL_MAXですLDBL_MIN

マニュアルページ(私の場合sprintfはこれについて次のように述べてい%gます:

double引数は、スタイルfまたはe(またはG変換の場合はFまたはE)に変換されます。精度は有効桁数を指定します。精度が不足している場合は、6桁が与えられます。精度がゼロの場合、1として扱われます。変換からの指数が-4未満、または精度以上の場合、スタイルeが使用されます。末尾のゼロは、結果の小数部分から削除されます。小数点は、その後に少なくとも1桁の数字が続く場合にのみ表示されます。

同様の表現がC標準にあります。

したがって、上記のプログラムからの出力を配列サイズとして使用すれば安全だと思います。

于 2009-12-19T20:52:59.083 に答える
0

snprintfPOSIX または C99 をサポートするプラットフォームを使用している場合は、 を使用して、必要なバッファーのサイズを計算できるはずです。snprintf渡すバッファのサイズを示すパラメータを取ります。文字列のサイズがそのバッファーのサイズを超える場合、バッファーに収まるように出力を切り捨て、出力全体を収めるのに必要なスペースの量を返します。この出力を使用して、正確なサイズのバッファーを割り当てることができます。必要なバッファーのサイズを計算するだけの場合は、バッファーとして NULL を渡し、サイズ 0 を渡して、必要なスペースの量を計算できます。

int size = snprintf(NULL, 0, "%.20g", x);
char *buf = malloc(size + 1); // Need the + 1 for a terminating null character
snprintf(buf, size + 1, "%.20g", x);

free(buf)メモリリークを避けるために、使用後は忘れないでください。

これの問題は、C99 をまだサポートしていない Visual Studio では機能しないことです。のようなものsnprintfがありますが、渡されたバッファが小さすぎると、必要なサイズが返さ-1れず、代わりに返されますが、これはまったく役に立ちません(長さNULLがあってもバッファとして受け入れられません0)。

切り捨てを気にしない場合はsnprintf、固定サイズのバッファーを使用するだけで、オーバーフローしないことが保証されます。

char buf[30];
snprintf(buf, sizeof(buf), "%.20g", x);

プラットフォームのドキュメントを確認してくださいsnprintf。特に、一部のプラットフォームでは、文字列が切り捨てられた場合に文字列の末尾に終端の null を追加しない場合があるため、自分で行う必要がある場合があります。

于 2009-12-19T21:23:46.827 に答える