Domanda

sto usando cpp_dec_float per precisione arbitraria, ed è fantastico, ma ho difficoltà a capire come stampare tutte le cifre significative.

Ad esempio, con questo codice per setup

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

mp_type test_num("7.0710678118654752440084436210484903928483593768847403658833986900e-01");

e se stampo semplicemente con

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

il risultato è 7.071068e-01, quindi è fuori.

Se vado fino in fondo

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

ottengo 7.0710678118654752440084436210484903928483593768847403658833986900000000000000000000000000000000000000e-01.Sono felice di non perdere la precisione, ma non è molto conservativo in termini di spazio.

Esiste un modo per rimuovere gli zeri finali senza perdere precisione con gli strumenti esistenti?In caso contrario, come è possibile rimuovere gli zeri finali dalla stringa risultante?

Se gli strumenti esistenti possono essere utilizzati per soddisfare il mio intento, come è possibile farlo? cpp_dec_float essere emesso in notazione scientifica senza perdita di precisione e zeri finali rimossi in una stringa?riesco a trovare solo il esempi di flusso.

Più vicino

Grazie a mockinterface, sono molto più vicino.

Ho cambiato il codice in questo:

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;

Avere una lunghezza potenzialmente illimitata;tuttavia, questo è stampato:

7.0710678118654752440084436210484903928480e-01

Il che è vicino ma sembra strano.Nel fonte mockinterface mi è stato così gentilmente segnalato che ho trovato queste righe

if(number_of_digits == 0)
    number_of_digits = cpp_dec_float_total_digits10;

il che mi suggerisce che dovrebbe prendere in considerazione tutte le cifre significative, sostanzialmente emettendo ciò che è stato inserito a causa della lunghezza illimitata.

Ho controllato il fonte per cpp_dec_float_total_digits10, e non sono in grado di determinare esattamente di cosa si tratta;tuttavia, ho trovato questa sezione di codice che sembra definirla.

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

È possibile determinare il numero di cifre significative e utilizzarlo come primo argomento per boost::multiprecision::cpp_dec_float::str()?

È stato utile?

Soluzione

Questa si è rivelata dura.

La breve storia è:non esiste tale funzionalità in cpp_dec_float.Quel che è peggio, cpp_dec_float non tiene traccia del numero di cifre significative impostate, quindi non esiste un modo "economico" per trovare la lunghezza necessaria per stampare la frazione.

Idee:

  • Per alcuni casi limite (es.123.000000000000001) si potrebbe prendere il log10 del reciproco della parte frazionaria + log10 della parte intera.Ciò non riesce completamente ad essere genericamente applicabile.

  • Se desideri utilizzare i dettagli di implementazione potresti trovare l'elemento "ultimo abitato" nell'array backend e fare i conti.Tuttavia, questo è piuttosto complicato (richiede la modifica cpp_dec_float.hpp e molti test).

  • Infine, ho osservato che l'attuale implementazione di .str() fa chiaramente zero sforzo per essere efficiente.Affatto.

Quindi tutto sommato ho i seguenti suggerimenti.O

  1. passare a gmp backend (se te lo puoi permettere).Nota

    • questa non è una rappresentazione float decimale AFAICT
    • ciò richiede il collegamento di una libreria aggiuntiva (libgmp).
    • gmp_float fa hanno però una precisione arbitraria, E
    • suo str() implementazione fa prendere in considerazione il significato degli zeri nella mantissa

    Guardalo Vivi su 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';
    }
    

    Stampe 7.071067811865475244008443621048490392848359376884740365883398690e-01 senza che siano necessarie ulteriori azioni.

  2. Se questa non è un'opzione, post-elaborerei semplicemente l'output, rimuovendo gli zeri finali:

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

Guardalo Vivi su Coliru Ancora

Altri suggerimenti

È possibile indicare esplicitamente il numero di cifre che devi emettere con il metodo cpp_dec_float::str():

std::cout << std::scientific << test_num.str(75) << std::endl;
// output: 0.707106781186547524400844362104849039284835937688474036588339869
.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top