Pregunta

Estoy usando cpp_dec_float de precisión arbitraria, y es genial, pero estoy teniendo problemas para averiguar cómo imprimir todos los dígitos significativos.

Por ejemplo, con este código, el programa de instalación

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

mp_type test_num("7.0710678118654752440084436210484903928483593768847403658833986900e-01");

y si yo simplemente imprimir con

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

el resultado es 7.071068e-01, por lo que está fuera.

Si puedo ir a por todas con

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

Puedo conseguir 7.0710678118654752440084436210484903928483593768847403658833986900000000000000000000000000000000000000e-01.Estoy feliz de no perder la precisión, pero no es muy de espacio conservador.

Es allí una manera de eliminar los ceros a la derecha sin perder precisión con herramientas ya existentes?Si no, ¿cómo pueden los ceros de ser eliminado de la cadena resultante?

Si las herramientas existentes pueden utilizarse para satisfacer mi intención, ¿cómo puede cpp_dec_float ser la salida en notación científica sin perdida de precisión y ceros quitar a una cadena?Sólo puedo encontrar la secuencia de ejemplos.

Más

Gracias a mockinterface, estoy mucho más cerca.

He cambiado el código para esto:

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;

Tener potencialmente ilimitado de longitud;sin embargo, este es impreso:

7.0710678118654752440084436210484903928480e-01

Que está cerca, pero parece extraño.En el fuente mockinterface tan amablemente me indicó, me he encontrado con estas líneas

if(number_of_digits == 0)
    number_of_digits = cpp_dec_float_total_digits10;

que me sugiere que se debe de tomar en cuenta todos los dígitos significativos, básicamente, la salida de lo que fue la entrada debido a la longitud ilimitada.

He comprobado el fuente para cpp_dec_float_total_digits10, y yo soy incapaz de determinar exactamente de qué se trata;aunque, me hizo encontrar esta sección de código que se parece a la definen.

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

Puede que el número de dígitos significativos se determina y se utiliza como el primer argumento para boost::multiprecision::cpp_dec_float::str()?

¿Fue útil?

Solución

Esto resultó ser una pregunta difícil.

La historia corta es:no existe tal funcionalidad en cpp_dec_float.Lo que es peor, cpp_dec_float no se controla el número de dígitos significativos que se han establecido, por lo que no hay "barato" para encontrar la longitud necesaria para la impresión de la fracción.

Ideas:

  • Para algunos casos fronterizos (por ejemplo,123.000000000000001), se puede tomar el log10 de la recíproca de la parte fraccionaria + log10 de la parte entera.Esto es totalmente incapaz de ser genéricamente aplicable.

  • Si desea utilizar los detalles de la implementación se puede encontrar la 'última habitada' elemento en el backend de la matriz, y hacer la matemáticas.Sin embargo, esto es bastante involucrados (requiere la modificación de cpp_dec_float.hpp y un montón de pruebas).

  • Por último, he observado que la implementación actual para .str() claramente hace cero esfuerzo para ser eficiente.En todos los.

Así que todo me tiene las siguientes sugerencias.Ya sea

  1. cambie a la gmp backend (si se lo puede permitir).Nota

    • este no es un decimal float representación AFAICT
    • esto requiere un mayor biblioteca (libgmp, ya que) esté vinculada
    • gmp_float ¿ han de precisión arbitraria, aunque, y
    • es str() la aplicación ¿ tener en cuenta la importancia de ceros en la mantisa

    Ver Vivir En 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';
    }
    

    Imprime 7.071067811865475244008443621048490392848359376884740365883398690e-01 sin más acciones necesarias.

  2. Si eso no es una opción, yo acababa de post-proceso de la salida, la eliminación de los ceros finales:

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

Ver Vivir En Coliru de nuevo

Otros consejos

Se debe indicar explícitamente el número de dígitos que debe ser la salida con la cpp_dec_float::str() método:

std::cout << std::scientific << test_num.str(75) << std::endl;
// output: 0.707106781186547524400844362104849039284835937688474036588339869
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top