я использую 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()
?
Это оказалось сложным.
Короткая история: в cpp_dec_float такой функциональности нет. Что еще хуже, cpp_dec_float не отслеживает количество значащих цифр, которые были установлены, поэтому нет «дешевого» способа найти длину, необходимую для печати дроби.
Идеи:
Для некоторых граничных случаев (например, 123.000000000000001) можно взять log10 обратной дробной части + log10 целочисленной части. Это совершенно не применимо в общем случае.
Если вы хотите использовать подробности реализации, вы можете найти «последний обитаемый» элемент в массиве бэкэнда и выполнить математику. Тем не менее, это довольно сложно (требует изменения cpp_dec_float.hpp
и много испытаний).
Наконец, я заметил, что текущая реализация для .str()
четко делает нуль усилие быть эффективным. Совсем.
Итак, в целом у меня есть следующие предложения. Или
переключиться на gmp
бэкэнд (если вы можете себе это позволить). Заметка
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
без дальнейших действий требуется.
Если это не вариант, я бы просто обработал вывод, удалив конечные нули:
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