Frage

Ich benutze cpp_dec_float für willkürliche Präzision, und es ist großartig, aber ich habe Probleme herauszufinden, wie alle signifikanten Ziffern gedruckt werden.

Zum Beispiel mit diesem Code zum Einrichten

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

mp_type test_num("7.0710678118654752440084436210484903928483593768847403658833986900e-01");

und wenn ich einfach mit drucke

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

Das Ergebnis ist 7.071068e-01, das ist also raus.

Wenn ich es pleite mache

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

Ich bekomme 7.0710678118654752440084436210484903928483593768847403658833986900000000000000000000000000000000000000e-01.Ich bin froh, die Präzision nicht zu verlieren, aber es ist nicht sehr platzsparend.

Gibt es eine Möglichkeit, die nachgestellten Nullen mit vorhandenen Tools zu entfernen, ohne an Präzision zu verlieren?Wenn nicht, wie können die nachgestellten Nullen aus der resultierenden Zeichenfolge entfernt werden?

Wenn vorhandene Tools verwendet werden können, um meine Absicht zu erfüllen, wie kann das dann geschehen? cpp_dec_float in wissenschaftlicher Notation ausgegeben werden, ohne dass die Genauigkeit verloren geht und nachgestellte Nullen aus einer Zeichenfolge entfernt werden?Ich kann nur das finden Stream-Beispiele.

Näher

Dank Mockinterface bin ich viel näher dran.

Ich habe den Code wie folgt geändert:

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;

Potenziell unbegrenzte Länge haben;Dies wird jedoch gedruckt:

7.0710678118654752440084436210484903928480e-01

Das ist zwar nah dran, wirkt aber seltsam.Im Quelle mockinterface hat mich so freundlich darauf hingewiesen, dass ich diese Zeilen gefunden habe

if(number_of_digits == 0)
    number_of_digits = cpp_dec_float_total_digits10;

Das legt meiner Meinung nach nahe, dass alle signifikanten Ziffern berücksichtigt werden sollten und aufgrund der unbegrenzten Länge grundsätzlich das ausgegeben wird, was eingegeben wurde.

Ich habe das überprüft Quelle für cpp_dec_float_total_digits10, und ich kann nicht genau bestimmen, was es ist;Allerdings habe ich diesen Codeabschnitt gefunden, der es zu definieren scheint.

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);

Kann die Anzahl der signifikanten Ziffern bestimmt und als erstes Argument für verwendet werden? boost::multiprecision::cpp_dec_float::str()?

War es hilfreich?

Lösung

Das erwies sich als schwierige Angelegenheit.

Die Kurzgeschichte ist:In cpp_dec_float gibt es keine solche Funktionalität.Was noch schlimmer ist: cpp_dec_float verfolgt nicht die Anzahl der festgelegten signifikanten Ziffern, sodass es keine „billige“ Möglichkeit gibt, die zum Drucken des Bruchs erforderliche Länge zu ermitteln.

Ideen:

  • Für einige Grenzfälle (z.B.123.000000000000001) man könnte den log10 des Kehrwerts des Bruchteils + log10 des ganzzahligen Teils nehmen.Dies ist überhaupt nicht allgemein anwendbar.

  • Wenn Sie Implementierungsdetails verwenden möchten, suchen Sie möglicherweise das „zuletzt bewohnte“ Element im Backend-Array und führen Sie die Berechnung durch.Dies ist jedoch ziemlich kompliziert (muss geändert werden). cpp_dec_float.hpp und viele Tests).

  • Schließlich habe ich festgestellt, dass die aktuelle Implementierung für .str() klar macht null Anstrengung, effizient zu sein.Überhaupt.

Alles in allem habe ich also die folgenden Vorschläge.Entweder

  1. wechseln Sie zum gmp Backend (wenn Sie es sich leisten können).Notiz

    • Dies ist keine dezimale Gleitkommadarstellung AFAICT
    • Hierzu muss eine zusätzliche Bibliothek (libgmp) verknüpft werden
    • gmp_float tut haben jedoch willkürliche Präzision, Und
    • es ist str() Implementierung tut Berücksichtigen Sie die Bedeutung von Nullen in der Mantisse

    Sehen Sie es Lebe auf 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';
    }
    

    Drucke 7.071067811865475244008443621048490392848359376884740365883398690e-01 ohne dass weitere Maßnahmen erforderlich sind.

  2. Wenn das keine Option ist, würde ich die Ausgabe einfach nachbearbeiten und die nachgestellten Nullen entfernen:

    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);
    }
    

Sehen Sie es Lebe auf Coliru wieder

Andere Tipps

Sie können die Anzahl der auszugebenden Ziffern explizit angeben cpp_dec_float::str() Methode:

std::cout << std::scientific << test_num.str(75) << std::endl;
// output: 0.707106781186547524400844362104849039284835937688474036588339869
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top