Question

j'utilise cpp_dec_float pour une précision arbitraire, et c'est génial, mais j'ai du mal à comprendre comment imprimer tous les chiffres significatifs.

Par exemple, avec ce code pour configurer

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

mp_type test_num("7.0710678118654752440084436210484903928483593768847403658833986900e-01");

et si j'imprime simplement avec

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

le résultat est 7.071068e-01, donc c'est fini.

Si je fais faillite avec

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

Je reçois 7.0710678118654752440084436210484903928483593768847403658833986900000000000000000000000000000000000000e-01.Je suis content de ne pas perdre en précision, mais ce n'est pas très conservateur d'espace.

Existe-t-il un moyen de supprimer les zéros de fin sans perdre de précision avec les outils existants ?Sinon, comment supprimer les zéros de fin de la chaîne résultante ?

Si les outils existants peuvent être utilisés pour satisfaire mon intention, comment cpp_dec_float être affiché en notation scientifique sans perte de précision et sans suppression des zéros à droite dans une chaîne ?Je ne trouve que le exemples de flux.

Plus proche

Grâce à mockinterface, je suis beaucoup plus proche.

J'ai changé le code comme ceci :

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;

Avoir une longueur potentiellement illimitée ;cependant, ceci est imprimé :

7.0710678118654752440084436210484903928480e-01

Ce qui est proche mais semble étrange.Dans le source mockinterface me l'a si gracieusement fait remarquer, j'ai trouvé ces lignes

if(number_of_digits == 0)
    number_of_digits = cpp_dec_float_total_digits10;

ce qui me suggère qu'il devrait prendre en compte tous les chiffres significatifs, produisant essentiellement ce qui a été saisi en raison de la longueur illimitée.

j'ai vérifié le source pour cpp_dec_float_total_digits10, et je suis incapable de déterminer exactement de quoi il s'agit ;bien que j'ai trouvé cette section de code qui semble le définir.

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

Le nombre de chiffres significatifs peut-il être déterminé et utilisé comme premier argument pour boost::multiprecision::cpp_dec_float::str()?

Était-ce utile?

La solution

Cela s’est avéré difficile.

La nouvelle est :il n'existe pas une telle fonctionnalité dans cpp_dec_float.Pire encore, cpp_dec_float ne suit pas le nombre de chiffres significatifs qui ont été définis, il n'existe donc pas de moyen « bon marché » de trouver la longueur nécessaire pour imprimer la fraction.

Idées :

  • Pour certains cas frontaliers (par ex.123.000000000000001) on pourrait prendre le log10 de l'inverse de la partie fractionnaire + log10 de la partie entière.Cela ne parvient absolument pas à être applicable de manière générique.

  • Si vous souhaitez utiliser les détails d'implémentation, vous pouvez trouver le « dernier élément habité » dans le tableau backend et faire le calcul.Cependant, cela est assez complexe (nécessite de modifier cpp_dec_float.hpp et beaucoup de tests).

  • Enfin, j'ai observé que l'implémentation actuelle de .str() fait clairement zéro effort pour être efficace.Du tout.

Donc, dans l’ensemble, j’ai les suggestions suivantes.Soit

  1. passer au gmp backend (si vous pouvez vous le permettre).Note

    • ce n'est pas une représentation décimale flottante AFAICT
    • cela nécessite qu'une bibliothèque supplémentaire (libgmp) soit liée
    • gmp_float fait ont cependant une précision arbitraire, et
    • c'est str() mise en œuvre fait prendre en compte la signification des zéros dans la mantisse

    Voir-le En direct sur 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';
    }
    

    Impressions 7.071067811865475244008443621048490392848359376884740365883398690e-01 sans autre action requise.

  2. Si ce n'est pas une option, je post-traiterais simplement la sortie, en supprimant les zéros de fin :

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

Voir-le En direct sur Coliru encore

Autres conseils

Vous pouvez indiquer explicitement le nombre de chiffres à afficher avec le cpp_dec_float::str() méthode:

std::cout << std::scientific << test_num.str(75) << std::endl;
// output: 0.707106781186547524400844362104849039284835937688474036588339869
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top