web-dev-qa-db-ja.com

double値の正しい小数点以下桁数を「計算」する方法は?

doubleの精度を維持するのに助けが必要です。リテラルをdoubleに割り当てると、実際の値は切り捨てられました。

_int main() {
    double x = 7.40200133400;
    std::cout << x << "\n";
}
_

上記のコードスニペットの場合、出力は_7.402_でした
このタイプの切り捨てを防ぐ方法はありますか?または、doubleの浮動小数点数を正確に計算する方法はありますか?たとえば、number_of_decimal(x)は11を返します。実行時に入力が不明であるため、setprecision()を使用できないためです。


私は質問を次のように変更する必要があると思います:浮動小数点を切り捨てずにdoubleを文字列に変換する方法つまり.

_#include <iostream>
#include <string>
#include <sstream>

template<typename T>
std::string type_to_string( T data ) {
    std::ostringstream o;
    o << data;
    return o.str();
}

int main() {
    double x = 7.40200;
    std::cout << type_to_string( x ) << "\n";
}
_

予想される出力は7.40200ですが、実際の結果は7.402でした。それでは、この問題をどのように回避できますか?何か案が?

26
Chan

floatdoubleは内部的にバイナリで保存されているため、リテラル7.40200133400は実際には数字7.40200133400000037653398976544849574565887451171875を表します

...それで、どれだけの精度が本当に必要ですか? :-)

#include <iomanip>    
int main()
{
    double x = 7.40200133400;
    std::cout << std::setprecision(51) << x << "\n";
}

はい、このプログラムは実際に7.40200133400000037653398976544849574565887451171875を印刷します!

29
fredoverflow

setiosflags(ios::fixed)setprecision(x)を使用する必要があります。

たとえば、cout << setiosflags(ios::fixed) << setprecision(4) << myNumber << endl;

また、#include <iomanip.h>

18
Maxpm
std::cout << std::setprecision(8) << x;

setprecisionは永続的であり、印刷する次のすべてのフロートは、別の値に変更するまでその精度で印刷されることに注意してください。それが問題であり、それを回避したい場合は、プロキシstringstreamオブジェクトを使用できます。

std::stringstream s;
s << std::setprecision(8) << x;
std::cout << s.str();

Iostreamのフォーマットの詳細については、cppreferenceの Input/output manipulators セクションをご覧ください。

9
Kos

Boost.Format を使用したソリューション:

#include <boost/format.hpp>
#include <iostream>

int main() {
    double x = 7.40200133400;
    std::cout << boost::format("%1$.16f") % x << "\n";
}

これは7.4020013340000004を出力します。

お役に立てれば!

5

私が思いついたこれに対する唯一の答えは、これを正しく行う方法がないことです(小数点以下の桁数を計算するなど)!これの主な理由は、数値の表現が期待どおりではない可能性があることです。たとえば、128.82は十分に無害なように見えますが、実際の表現は128.8199999999です。

3
Nim

ダブルはhave小数点以下の桁を持ちません。バイナリの場所があります。また、2進数と10進数は通約不可能です(log2(10)は整数ではないため)。

あなたが求めているものは存在しません。

2
user207421

回答編集への応答:それを行う方法はありません。 doubleに値を割り当てるとすぐに、後続のゼロは失われます(コンパイラ/コンピューターにとって、0.402、0.4020、および0.40200は同じ番号です)。指定したように末尾のゼロを保持する唯一の方法は、値を文字列として保存することです(または、気になる桁数を追跡して正確にその長さにフォーマットするトリックを実行します)。

2
Mark B

似たようなリクエストをしてみましょう。整数を001で初期化した後、先頭にゼロを付けて印刷したいと思うでしょう。そのフォーマット情報は単に保存されることはありませんでした。

倍精度浮動小数点ストレージの詳細については、IEEE 754標準を参照してください。

1
fned

質問の2番目の部分、値の指定から出力結果までの浮動小数点値の末尾のゼロを保持する方法については、解決策がありません。浮動小数点値は元の値の仕様を保持しません。この無意味な部分はSOモデレーターによって追加されたようです。

7.40200133400のすべての有効数字、つまり7.402001334のような出力を表示する方法として解釈する質問の最初の元の部分に関しては、出力結果から末尾のゼロのみを削除できます。 double値の信頼できる数字:

#include <assert.h>         // assert
#include <limits>           // std::(numeric_limits)
#include <string>           // std::(string)
#include <sstream>          // std::(ostringstream)

namespace my{
    // Visual C++2017 doesn't support comma-separated list for `using`:
    using std::fixed; using std::numeric_limits; using std::string;
    using std::ostringstream;

    auto max_fractional_digits_for_positive( double value )
        -> int
    {
        int result = numeric_limits<double>::digits10 - 1;
        while( value < 1 ) { ++result; value *= 10; }
        return result;
    }

    auto string_from_positive( double const value )
        -> string
    {
        ostringstream stream;
        stream << fixed;
        stream.precision( max_fractional_digits_for_positive( value ) );
        stream << value;
        string result = stream.str();
        while( result.back() == '0' )
        {
            result.resize( result.size() - 1 );
        }
        return result;
    }

    auto string_from( double const value )
        -> string
    {
        return (0?""
            : value == 0?   "0"
            : value < 0?    "-" + string_from_positive( -value )
            :               string_from_positive( value )
            );
    }
}

#include<iostream>
auto main()
    -> int
{
    using std::cout;
    cout << my::string_from( 7.40200133400 ) << "\n";
    cout << my::string_from( 0.00000000000740200133400 ) << "\n";
    cout << my::string_from( 128.82 ) << "\n";
}

出力:

 7.402001334 
 0.000000000007402001334 
 128.81999999999999 

最後の結果のように、9の長いシーケンスを回避するために、丸めのロジックを追加することを検討できます。