Напечатайте cpp_dec_float в экспоненциальном представлении без конечных нулей.

StackOverflow https://stackoverflow.com//questions/23026257

Вопрос

я использую 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() явно делает нуль стараться быть эффективным.Совсем.

В целом у меня есть следующие предложения.Или

  1. переключиться на gmp бэкэнд (если вы можете себе это позволить).Примечание

    • это не десятичное представление с плавающей запятой AFAICT
    • для этого требуется подключение дополнительной библиотеки (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 без необходимости дополнительных действий.

  2. Если это невозможно, я бы просто обработал вывод, удалив конечные нули:

    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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top