I'm using cpp_dec_float
for arbitrary precision, and it's great, but I'm having trouble figuring out how to print all significant digits.
For example, with this code to setup
using boost::multiprecision::cpp_dec_float;
typedef boost::multiprecision::number<cpp_dec_float<100>> mp_type;
mp_type test_num("7.0710678118654752440084436210484903928483593768847403658833986900e-01");
and if I simply print with
std::cout << std::scientific << test_num << std::endl;
the result is 7.071068e-01
, so that's out.
If I go for broke with
std::cout << std::setprecision(std::numeric_limits<mp_type>::digits) << std::scientific << test_num << std::endl;
I get 7.0710678118654752440084436210484903928483593768847403658833986900000000000000000000000000000000000000e-01
. I'm happy not to lose the precision, but it's not very space conservative.
Is there a way to remove the trailing zeros without losing any precision with existing tools? If not, how can the trailing zeros be removed from the resultant string?
If existing tools can be used to satisfy my intent, how can cpp_dec_float
be output in scientific notation without lost precision and trailing zeros removed to a string? I can only find the stream examples.
Closer
Thanks to mockinterface, I'm much closer.
I've changed the code to this:
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;
To have potentially unlimited length; however, this is printed:
7.0710678118654752440084436210484903928480e-01
Which is close but seems strange. In the source mockinterface so graciously pointed out to me, I found these lines
if(number_of_digits == 0)
number_of_digits = cpp_dec_float_total_digits10;
which suggests to me that it should take into account all significant digits, basically outputting what was input because of the unlimited length.
I checked the source for cpp_dec_float_total_digits10
, and I am unable to determine exactly what it is; although, I did find this code section that seems to define it.
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);
Can the number of significant digits be determined and used as the first argument for boost::multiprecision::cpp_dec_float::str()
?
You can explicitly state the number of digits you need to be output with the
cpp_dec_float::str()
method:This turned out to be a tough one.
The short story is: there is no such functionality in cpp_dec_float. What's worse, cpp_dec_float doesn't track the number of significant digits that have been set, so there's no "cheap" way to find the length needed to print the fraction.
Ideas:
For some border cases (e.g. 123.000000000000001) one could take the log10 of the reciprocal of the the fractional part + log10 of the integer part. This completely fails to be generically applicable.
If you want to use implementation details you might find the 'last inhabited' element in the backend array, and do the maths. However, this is pretty involved (requires modifying
cpp_dec_float.hpp
and a lot of testing).Finally, I observed that the current implementation for
.str()
clearly makes zero effort to be efficient. At all.So all in all I have the following suggestions. Either
switch to the
gmp
backend (if you can afford it). Notegmp_float
does have arbitrary precision though, andstr()
implementation does take into account the significance of zeroes in the mantissaSee it Live On Coliru
Prints
7.071067811865475244008443621048490392848359376884740365883398690e-01
without further actions required.If that's not an option, I'd just post-process the output, removing the trailing zeroes:
See it Live On Coliru again