cpp_dec_float を科学表記法で末尾のゼロなしで出力します
-
21-12-2019 - |
質問
使っています 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()
?
解決
これは大変なことになりました。
短い話は次のとおりです。cpp_dec_float にはそのような機能はありません。さらに悪いことに、cpp_dec_float は設定された有効桁数を追跡しないため、分数を出力するために必要な長さを見つける「安価な」方法がありません。
アイデア:
一部の国境の場合 (例:123.000000000000001) 小数部の逆数の log10 + 整数部の log10 を取ることができます。これは一般的に適用できるものではありません。
実装の詳細を使用したい場合は、バックエンド配列で「最後に居住した」要素を見つけて計算することができます。ただし、これはかなり複雑です(変更が必要です)
cpp_dec_float.hpp
そして多くのテストが必要です)。最後に、現在の実装が
.str()
明らかに作ります ゼロ 効率的になるための努力。まったく。
したがって、全体として、私は次のような提案をします。どちらか
に切り替えます
gmp
バックエンド(余裕があれば)。注記- これは10進数の浮動小数点表現ではありません
- これには追加のライブラリ (libgmp) をリンクする必要があります
gmp_float
する 任意の精度がありますが、 そして- その
str()
実装 する 仮数部のゼロの重要性を考慮する
見てください ライブ・オン・コリル
#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
それ以上のアクションは必要ありません。それができない場合は、出力を後処理して末尾のゼロを削除します。
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); }
見てください ライブ・オン・コリル また
他のヒント
cpp_dec_float::str()
メソッドで出力する必要がある桁数を明示的に述べることができます。
std::cout << std::scientific << test_num.str(75) << std::endl;
// output: 0.707106781186547524400844362104849039284835937688474036588339869
.