4

double に変換された文字列に対していくつかの操作を実行した後、double の精度に問題があります。

#include <iostream>   
#include <sstream>
#include <math.h>

using namespace std;

// conversion function
void convert(const char * a, const int i, double &out)
{

   double val;

   istringstream in(a);
   in >> val;

   cout << "char a -- " << a << endl;
   cout << "val ----- " << val << endl;

   val *= i;

   cout << "modified val --- " << val << endl;
   cout << "FMOD ----- " << fmod(val, 1) << endl;

   out = val;

   return 0;

}

これは、文字列として入力されたすべての数値に当てはまるわけではないため、エラーは一定ではありません。一部の数値にのみ影響します (34.38 は定数のようです)。

現時点で、a = 34.38 および i=100 を渡すと、次のように返されます。

char a -- 34.38
Val ----- 34.38
modified val --- 3438
FMOD ----- 4.54747e-13

精度が低いため、Valをfloatに変更すると機能しますが、doubleが必要です。

これは、sstream の代わりに atof、sscanf、strtod を使用した場合にも再現されます。

C++ で、文字列を double に正しく変換し、実際に正確な値を返す最良の方法は何ですか?

ありがとう。

4

2 に答える 2

11

これは、ここでの非常に多くの質問のほぼ正確な複製です。基本的に、バイナリ浮動小数点で 34.38 を正確に表現することはできないため、34 + 19/50 は 34 + k/n として表されます。ここで、n は 2 のべき乗です。因数として 50 を持つ正確な 2 の累乗は存在しないため、可能な k の正確な値はありません。

出力精度を設定すると、最適な double 表現が正確ではないことがわかります。

cout << fixed << setprecision ( 20 );

与える

char a -- 34.38
val ----- 34.38000000000000255795
modified val --- 3438.00000000000045474735
FMOD ----- 0.00000000000045474735

したがって、あなたの質問への回答として、文字列を double に変換する最良の方法を既に使用しています (ただし、レキシカル キャストをブーストすると 2 行または 3 行が 1 行にまとめられるため、独自の関数を作成する手間が省ける場合があります)。結果は double で使用される表現によるものであり、2 進浮動小数点に基づく任意の有限表現に適用されます。

浮動小数点数を使用すると、乗算はたまたま切り上げではなく切り捨てられるため、たまたま正確な結果が得られます。これは、依存できる動作ではありません。

于 2010-07-08T10:57:07.860 に答える
2

ここでの「問題」は、単純に 34.38 を倍精度浮動小数点で正確に表現できないことです。10 進値を浮動小数点で正確に表すことができない理由を説明しているこの記事を読む必要があります。

"34.38 * 100" を 16 進数で調べると (たとえば、MATLAB の "format hex" に従って)、次のように表示されます。

40aadc0000000001

最後の桁に注意してください。

于 2010-07-08T10:57:31.027 に答える