以前の質問では、予想外のときに丸くなったdouble
使用を印刷していました。完全な精度を使用して印刷するcout
にはどうすればよいですか?cout
double
16 に答える
精度を直接設定し、書式指定子std::cout
を使用できます。std::fixed
double d = 3.14159265358979;
cout.precision(17);
cout << "Pi: " << fixed << d << endl;
#include <limits>
float または double の最大精度を取得できます。
#include <limits>
typedef std::numeric_limits< double > dbl;
double d = 3.14159265358979;
cout.precision(dbl::max_digits10);
cout << "Pi: " << d << endl;
#include <iomanip>
std::cout << std::setprecision (15) << 3.14159265358979 << std::endl;
これが私が使用するものです:
std::cout << std::setprecision (std::numeric_limits<double>::digits10 + 1)
<< 3.14159265358979
<< std::endl;
基本的に、limits パッケージにはすべての組み込みタイプの特性があります。
浮動小数点数 (float/double/long double) の特性の 1 つは digits10 属性です。これは、基数 10 の浮動小数点数の精度 (正確な用語は忘れました) を定義します。
参照: http://www.cplusplus.com/reference/std/limits/numeric_limits.html
他の属性の詳細について。
std::format
C++20 では、これを行うために使用できます。
std::cout << std::format("{}", M_PI);
出力 (IEEE754 と仮定double
):
3.141592653589793
デフォルトの浮動小数点形式は、ラウンドトリップが保証された最短の 10 進数表現です。I/O マニピュレータと比較したこの方法の利点は、setprecision
不要な数字を出力しないことです。
それまでの間、ベースになっている {fmt} ライブラリを使用できます。std::format
{fmt} は、print
これをさらに簡単かつ効率的にする機能も提供します ( godbolt )。
fmt::print("{}", M_PI);
免責事項: 私は {fmt} と C++20 の作成者std::format
です。
iostreams の方法はちょっとぎこちないです。boost::lexical_cast
適切な精度を計算するため、使用することを好みます。そして、それも速いです。
#include <string>
#include <boost/lexical_cast.hpp>
using boost::lexical_cast;
using std::string;
double d = 3.14159265358979;
cout << "Pi: " << lexical_cast<string>(d) << endl;
出力:
円周率: 3.14159265358979
double を完全な精度で表示する方法は次のとおりです。
double d = 100.0000000000005;
int precision = std::numeric_limits<double>::max_digits10;
std::cout << std::setprecision(precision) << d << std::endl;
これは次のように表示されます。
100.0000000000005
max_digits10 は、すべての個別の double 値を一意に表すために必要な桁数です。max_digits10 は、小数点の前後の桁数を表します。
std::fixed で set_precision(max_digits10) を使用しないでください。
固定表記では、set_precision() は小数点以下の桁数のみを設定します。max_digits10は小数点の前後の桁数を表すため、これは正しくありません。
double d = 100.0000000000005;
int precision = std::numeric_limits<double>::max_digits10;
std::cout << std::fixed << std::setprecision(precision) << d << std::endl;
これは間違った結果を表示します:
100.00000000000049738
注: ヘッダー ファイルが必要です
#include <iomanip>
#include <limits>
double
完全な精度では、意図した値の最良の近似値を表示するのに十分な平均精度を想定していますが、基数 2 表現を使用して格納されていることを指摘する必要があります1.1
。実際の double の完全な精度 (ROUND OFF ERROR なし)を取得する唯一の方法は、バイナリ ビット (または 16 進数のニブル) を出力することです。
これを行う 1 つの方法は、 a を使用してを整数union
にタイプパンdouble
し、整数を出力することです。これは、整数は切り捨てや丸めの問題に悩まされないためです。(このような型パニングは C++ 標準ではサポートされていませんが、C ではサポートされています。ただし、ほとんどの C++ コンパイラはおそらく正しい値を出力します。g++ はこれをサポートしていると思います。)
union {
double d;
uint64_t u64;
} x;
x.d = 1.1;
std::cout << std::hex << x.u64;
これにより、double の 100% 正確な精度が得られます...人間は IEEE double 形式を読み取ることができないため、まったく読み取ることができません! ウィキペディアには、バイナリ ビットを解釈する方法に関する優れた記事があります。
新しいC++では、次のことができます
std::cout << std::hexfloat << 1.1;
printf("%.12f", M_PI);
%.12f は、精度が 12 桁の浮動小数点を意味します。
最も携帯性に...
#include <limits>
using std::numeric_limits;
...
cout.precision(numeric_limits<double>::digits10 + 1);
cout << d;
この質問では、double を無損失で文字列に変換する方法についての説明があります (Octave では、C++ で簡単に再現できます)。人間が読める短い float の説明と、16 進形式のロスレスな説明を用意することをお勧めします。例: pi -> 3.14{54442d18400921fb}。
これは、 だけでなく、任意の浮動小数点型で機能する関数であり、double
後で見つかった方法でストリームを戻します。残念ながら、スレッドとはうまくやり取りできませんが、それが iostream の性質です。ファイルの先頭に次のインクルードが必要です。
#include <limits>
#include <iostream>
関数は次のとおりです。頻繁に使用する場合は、ヘッダー ファイルで使用できます。
template <class T>
void printVal(std::ostream& os, T val)
{
auto oldFlags = os.flags();
auto oldPrecision = os.precision();
os.flags(oldFlags & ~std::ios_base::floatfield);
os.precision(std::numeric_limits<T>::digits10);
os << val;
os.flags(oldFlags);
os.precision(oldPrecision);
}
次のように使用します。
double d = foo();
float f = bar();
printVal(std::cout, d);
printVal(std::cout, f);
通常の挿入演算子を使用できるようにしたい場合は、次の<<
追加のラッパー コードを使用できます。
template <class T>
struct PrintValWrapper { T val; };
template <class T>
std::ostream& operator<<(std::ostream& os, PrintValWrapper<T> pvw) {
printVal(os, pvw.val);
return os;
}
template <class T>
PrintValWrapper<T> printIt(T val) {
return PrintValWrapper<T>{val};
}
これで、次のように使用できます。
double d = foo();
float f = bar();
std::cout << "The values are: " << printIt(d) << ", " << printIt(f) << '\n';
ostream::precision(int) を使用
cout.precision( numeric_limits<double>::digits10 + 1);
cout << M_PI << ", " << M_E << endl;
譲ります
3.141592653589793, 2.718281828459045
「+1」と言わなければならない理由はわかりませんが、得られる余分な数字は正しいです。