Вывести cpp_dec_float в научную запись без конечных нулей

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

ближе

Благодаря mockinterface я намного ближе.

Я изменил код на это:

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

Что близко, но кажется странным. в источник mockinterface так милостиво указал мне, я нашел эти строки

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()?

3

Решение

Это оказалось сложным.

Короткая история: в 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);
    }
    

Видеть это Жить на Колиру снова

2

Другие решения

Вы можете явно указать количество цифр, которое необходимо вывести, с помощью cpp_dec_float::str() метод:

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

По вопросам рекламы [email protected]