4

私はcpp_dec_float任意の精度で使用していますが、それは素晴らしいのですが、すべての有効数字を出力する方法がわかりません。

たとえば、このコードを使用してセットアップします

using boost::multiprecision::cpp_dec_float;
typedef boost::multiprecision::number<cpp_dec_float<100>> mp_type;

mp_type test_num("7.0710678118654752440084436210484903928483593768847403658833986900e-01");

そして、単純に印刷すると

std::cout << std::scientific << test_num << std::endl;

結果は7.071068e-01です。

私が壊れた場合

std::cout << std::setprecision(std::numeric_limits<mp_type>::digits) << std::scientific << test_num << std::endl;

私は得る7.0710678118654752440084436210484903928483593768847403658833986900000000000000000000000000000000000000e-01。精度を落とさなくてよかったのですが、スペースの節約にはなりません。

既存のツールで精度を失うことなく末尾のゼロを削除する方法はありますか? そうでない場合、結果の文字列から末尾のゼロを削除するにはどうすればよいですか?

私の意図を満たすために既存のツールを使用できる場合、cpp_dec_float精度を失わず、文字列の末尾のゼロを削除せずに科学表記法で出力するにはどうすればよいでしょうか? ストリームの例しか見つかりません。

クローザー

モックインターフェイスのおかげで、私はずっと近くにいます。

コードを次のように変更しました。

using boost::multiprecision::cpp_dec_float;
typedef boost::multiprecision::number<cpp_dec_float<0>> mp_type;
mp_type test_num("7.0710678118654752440084436210484903928483593768847403658833986900e-01");
std::cout << test_num.str(0, std::ios_base::scientific) << std::endl;

潜在的に無制限の長さを持つこと。ただし、これは印刷されます。

7.0710678118654752440084436210484903928480e-01

これは近いですが奇妙に思えます。親切に指摘されたソースのモックインターフェイスで、これらの行を見つけました

if(number_of_digits == 0)
    number_of_digits = cpp_dec_float_total_digits10;

これは、すべての有効数字を考慮に入れる必要があることを示唆しており、基本的には長さが無制限であるため、入力されたものを出力します。

のソースを確認しましたcpp_dec_float_total_digits10が、それが何であるかを正確に判断できません。ただし、それを定義しているように見えるこのコードセクションを見つけました。

private:
   static const boost::int32_t cpp_dec_float_elem_digits10 = 8L;
   static const boost::int32_t cpp_dec_float_elem_mask     = 100000000L;

   BOOST_STATIC_ASSERT(0 == cpp_dec_float_max_exp10 % cpp_dec_float_elem_digits10);

   // There are three guard limbs.
   // 1) The first limb has 'play' from 1...8 decimal digits.
   // 2) The last limb also has 'play' from 1...8 decimal digits.
   // 3) One limb can get lost when justifying after multiply,
   //    as only half of the triangle is multiplied and a carry
   //    from below is missing.
   static const boost::int32_t cpp_dec_float_elem_number_request = static_cast<boost::int32_t>((cpp_dec_float_digits10 / cpp_dec_float_elem_digits10) + (((cpp_dec_float_digits10 % cpp_dec_float_elem_digits10) != 0) ? 1 : 0));

   // The number of elements needed (with a minimum of two) plus three added guard limbs.
   static const boost::int32_t cpp_dec_float_elem_number = static_cast<boost::int32_t>(((cpp_dec_float_elem_number_request < 2L) ? 2L : cpp_dec_float_elem_number_request) + 3L);

public:
   static const boost::int32_t cpp_dec_float_total_digits10 = static_cast<boost::int32_t>(cpp_dec_float_elem_number * cpp_dec_float_elem_digits10);

の最初の引数として有効桁数を決定して使用できますboost::multiprecision::cpp_dec_float::str()か?

4

2 に答える 2

2

これは大変なことになりました。

簡単に言うと、cpp_dec_float にはそのような機能はありません。さらに悪いことに、 cpp_dec_float は設定された有効桁数を追跡しないため、分数を出力するために必要な長さを見つける「安価な」方法はありません。

アイデア:

  • 一部の境界ケース (例: 123.000000000000001) では、小数部分の逆数 + 整数部分の log10 の log10 を取ることができます。これは一般的に適用することはできません。

  • 実装の詳細を使用したい場合は、バックエンド配列で「最後に居住した」要素を見つけて、計算を行うことができます。ただし、これはかなり複雑です (変更cpp_dec_float.hppと多くのテストが必要です)。

  • 最後に、 の現在の実装では、.str()明らかに効率化の努力がゼロであることを確認しました。まったく。

全体として、次の提案があります。また

  1. バックエンドに切り替えgmpます(余裕がある場合)。ノート

    • これは 10 進浮動小数点表現ではありません AFAICT
    • これには追加のライブラリ (libgmp) をリンクする必要があります
    • gmp_float ただし、任意の精度があります
    • そのstr()実装では、仮数のゼロの意味考慮されます

    Coliruでライブを見る

    #include <boost/multiprecision/number.hpp>
    #include <boost/multiprecision/gmp.hpp>
    #include <iostream>
    
    namespace mp = boost::multiprecision;
    
    int main()
    {
        typedef mp::number<mp::gmp_float<100>> mp_type;
        mp_type test_num("7.071067811865475244008443621048490392848359376884740365883398690000000000000000000e-01");
    
        std::cout << test_num.str(0, std::ios_base::scientific) << '\n';
    }
    

    7.071067811865475244008443621048490392848359376884740365883398690e-01追加のアクションを必要とせずに印刷します。

  2. それができない場合は、出力を後処理して、末尾のゼロを削除します。

    template <typename T>
    std::string to_pretty_string(T const& v)
    {
        std::string s = v.str(0, std::ios_base::scientific);
        assert(s.length()>3); // min: 0.e
        switch (s[0])
        { // normalized scientific always has #.####### form of mantissa
            case '-':
            case '+': assert(s[2] == '.'); break;
            default:  assert(s[1] == '.'); break;
        }
    
        auto exp = s.find('e');
        if (std::string::npos != exp && exp > 0)
        {
            for(size_t pos = exp-1; pos; --pos)
            {
                if (s[pos] != '0')
                {
                    // remove run of 0s if applicable
                    s.erase(pos+1, exp-pos-1); 
                    break;
                }
            }
        }
        return std::move(s);
    }
    

Coliruでライブをもう一度見る

于 2014-04-12T23:08:36.860 に答える
1

cpp_dec_float::str()メソッドを使用して、出力する必要がある桁数を明示的に指定できます。

std::cout << std::scientific << test_num.str(75) << std::endl;
// output: 0.707106781186547524400844362104849039284835937688474036588339869
于 2014-04-12T05:18:24.183 に答える