1. ホーム
  2. c

[解決済み] 浮動小数点値の精度を維持するためのPrintf幅指定子

2022-06-23 11:48:17

質問

このような printf の必要な数に自動的にフォーマットして出力する浮動小数点数指定子に適用できる幅指定子はありますか? 有効数字 文字列をスキャンして戻すときに、元の浮動小数点値が取得されるように、必要な数の有効数字に出力を自動的にフォーマットするような浮動小数点指定子を適用できますか?

例えば、私が float という精度で 2 小数点以下の桁数。

float foobar = 0.9375;
printf("%.2f", foobar);    // prints out 0.94

出力されたものをスキャンすると 0.94 をスキャンした場合、標準に準拠した保証はありませんが、オリジナルの 0.9375 浮動小数点値を取り戻せるという標準準拠の保証はありません (この例では、おそらく取り戻せません)。

私は printf に自動的に浮動小数点値を必要な数の 有効数字 に渡された元の値にスキャンバックできるようにします。 printf .

のマクロのいくつかを使うことができました。 float.h から の最大幅を導出します。 に渡す printf に渡す最大幅を導きますが、自動的に必要な数の 有効数字 -- あるいは、少なくとも最大幅で印刷するような指定はありますか?

どのように解決するのですか?

Jens Gustedt氏の16進数による解決策をお勧めします:%aを使用します。

OPは「最大限の精度で(または少なくとも最上位の10進数まで)印刷する」ことを望んでいます。

簡単な例では、7分の1を印刷するというように。

#include <float.h>
int Digs = DECIMAL_DIG;
double OneSeventh = 1.0/7.0;
printf("%.*e\n", Digs, OneSeventh);
// 1.428571428571428492127e-01


しかし、もっと掘り下げてみましょう.

数学的には、答えは "0.142857 142857 ..." ですが、私たちは有限精度の浮動小数点数を使っています。 次のように仮定してみましょう。 IEEE754倍精度バイナリ . そこで OneSeventh = 1.0/7.0 の結果は以下のようになります。 また、前後の表現可能な double 浮動小数点数です。

OneSeventh before = 0.1428571428571428 214571170656199683435261249542236328125
OneSeventh        = 0.1428571428571428 49212692681248881854116916656494140625
OneSeventh after  = 0.1428571428571428 769682682968777953647077083587646484375

を印刷する 正確 の10進数表現を表示します。 double は用途が限られます。

C言語には2種類のマクロがあり <float.h> を用意し、私たちを助けてくれます。

1セット目は、数 重要な の桁数です。 元の浮動小数点が得られます。 C の仕様で示されている 最小 の値と サンプル C11コンパイラです。

FLT_DECIMAL_DIG   6,  9 (float)                           (C11)
DBL_DECIMAL_DIG  10, 17 (double)                          (C11)
LDBL_DECIMAL_DIG 10, 21 (long double)                     (C11)
DECIMAL_DIG      10, 21 (widest supported floating type)  (C99)

2つ目のセットは 重要な の桁数で、文字列を浮動小数点にスキャンして、FPを印刷しても、同じ文字列表現を保持することができます。 C 仕様で示されている 最小 の値と サンプル C11コンパイラ。 C99 より前のものが利用可能だと思います。

FLT_DIG   6, 6 (float)
DBL_DIG  10, 15 (double)
LDBL_DIG 10, 18 (long double)

最初のマクロのセットは、OPの目標である 重要な 桁の数字です。 しかし、その マクロ は常に使えるとは限りません。

#ifdef DBL_DECIMAL_DIG
  #define OP_DBL_Digs (DBL_DECIMAL_DIG)
#else  
  #ifdef DECIMAL_DIG
    #define OP_DBL_Digs (DECIMAL_DIG)
  #else  
    #define OP_DBL_Digs (DBL_DIG + 3)
  #endif
#endif

"+3"が前回の回答の核心でした。 その中心は、往復変換文字列-FP-文字列(セット#2マクロありC89)がわかっている場合、FP-文字列-FP(セット#1マクロありC89以降)の桁数はどのように決定するのでしょうか? 一般的には、3を足すという結果になりました。

さて、いくつの 重要な を表示するかが決定され <float.h> .

Nを表示するには 重要な 小数点以下の数字を表示するために、様々なフォーマットを使用することができます。

とは "%e" 精度 フィールドは の後に の後の桁数であり、小数点以下の桁数です。 そのため - 1 が順当なところです。 注:この -1 は、最初の int Digs = DECIMAL_DIG;

printf("%.*e\n", OP_DBL_Digs - 1, OneSeventh);
// 1.4285714285714285e-01

とは "%f" 精度 フィールドは の後に の小数点以下の桁数です。 のような数値の場合 OneSeventh/1000000.0 のような場合、1つは OP_DBL_Digs + 6 をすべて見るには 重要な の数字を表示します。

printf("%.*f\n", OP_DBL_Digs    , OneSeventh);
// 0.14285714285714285
printf("%.*f\n", OP_DBL_Digs + 6, OneSeventh/1000000.0);
// 0.00000014285714285714285

注意:多くの人は "%f" . これは小数点以下6桁を表示します。6桁は表示のデフォルトであり、数値の精度ではありません。