Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Importing and Exporting Data to and from cpp_int and cpp_bin_float

Any integer number type that uses cpp_int_backend as its implementation layer can import or export its bits via two non-member functions:

template <unsigned MinBits, unsigned MaxBits, cpp_integer_type SignType, cpp_int_check_type Checked, class Allocator,
          expression_template_option ExpressionTemplates, class OutputIterator>
OutputIterator export_bits(
   const number<const cpp_int_backend<MinBits, MaxBits, SignType, Checked, Allocator>, ExpressionTemplates>& val,
   OutputIterator out,
   unsigned chunk_size,
   bool msv_first = true);

template <unsigned MinBits, unsigned MaxBits, cpp_integer_type SignType, cpp_int_check_type Checked, class Allocator,
          expression_template_option ExpressionTemplates, class Iterator>
number<cpp_int_backend<MinBits, MaxBits, SignType, Checked, Allocator>, ExpressionTemplates>&
   import_bits(
      number<cpp_int_backend<MinBits, MaxBits, SignType, Checked, Allocator>, ExpressionTemplates>& val,
      Iterator i,
      Iterator j,
      unsigned chunk_size = 0,
      bool msv_first = true);

These functions are designed for data-interchange with other storage formats, and since cpp_bin_float uses cpp_int internally, by extension they can be used for floating-point numbers based on that backend as well (see example below). Parameters and use are as follows:

template <unsigned MinBits, unsigned MaxBits, cpp_integer_type SignType, cpp_int_check_type Checked, class Allocator,
          expression_template_option ExpressionTemplates, class OutputIterator>
OutputIterator export_bits(
   const number<const cpp_int_backend<MinBits, MaxBits, SignType, Checked, Allocator>, ExpressionTemplates>& val,
   OutputIterator out,
   unsigned chunk_size,
   bool msv_first = true);

Exports the absolute value of val to OutputIterator out. The function will write chunk_size bits at a time to the OutputIterator, and if msv_first is true, will write the most-significant block first. Byte and bit order within each chunk_size block is always in the machines native format. Further, each block is stored in a boost::uintmax_t when it's assigned to *out.

[Note] Note

Unfortunately, the standard's OutputIterator concept provides no means of deducing the type to output since std::iterator_traits<OutputIteratorType>::value_type is type void. This is why the bit count for each block has to be specified manually. It may also result in compiler warnings about the value being narrowed.

[Tip] Tip

If you're exporting to non-native byte layout, then use Boost.Endian to create a custom OutputIterator that reverses the byte order of each chunk prior to actually storing the result.

template <unsigned MinBits, unsigned MaxBits, cpp_integer_type SignType, cpp_int_check_type Checked, class Allocator,
          expression_template_option ExpressionTemplates, class ForwardIterator>
number<cpp_int_backend<MinBits, MaxBits, SignType, Checked, Allocator>, ExpressionTemplates>&
   import_bits(
      number<cpp_int_backend<MinBits, MaxBits, SignType, Checked, Allocator>, ExpressionTemplates>& val,
      ForwardIterator i,
      ForwardIterator j,
      unsigned chunk_size = 0,
      bool msv_first = true);

Imports bits from the iterator range [i,j) and stores them in val to produce an unsigned result (if the result is to be signed you will need to handle that separately). When msv_first is true, takes *i as the most significant chunk. Assumes there are chunk_size bits in each value read from the iterator range, and that these are in machine native bit/byte order. When chunk_size is zero, then assumes that each chunk contains std::numeric_limits<std::iterator_traits<ForwardIterator>::value_type>::digits, note that this will give the wrong result if dereferencing the iterators leads to a signed-integer type, and the sign bit is significant (be particularly careful if you expect type char to contain 8-bit values, as by default it will extract only 7-bits at a time if char is signed). As with exporting, if the external data is to be in a non-native byte order (within each chunk), then you will need to create an iterator adaptor that presents it in native order (see Boost.Endian).

[Note] Note

Note that this function is optimized for the case where the data can be memcpyed from the source to the integer - in this case both iterators much be pointers, and everything must be little-endian.

Examples

In this simple example, we'll import/export the bits of a cpp_int to a vector of 8-bit unsigned values:

#include <boost/multiprecision/cpp_int.hpp>
#include <iostream>
#include <iomanip>
#include <vector>
#include <iterator>


int main()
{
   using boost::multiprecision::cpp_int;
   // Create a cpp_int with just a couple of bits set:
   cpp_int i;
   bit_set(i, 5000); // set the 5000'th bit
   bit_set(i, 200);
   bit_set(i, 50);
   // export into 8-bit unsigned values, most significant bit first:
   std::vector<unsigned char> v;
   export_bits(i, std::back_inserter(v), 8);
   // import back again, and check for equality:
   cpp_int j;
   import_bits(j, v.begin(), v.end());
   BOOST_ASSERT(i == j);
}

Importing or exporting cpp_bin_float is similar, but we must proceed via an intermediate integer:

#include <boost/multiprecision/cpp_bin_float.hpp>
#include <iostream>
#include <iomanip>
#include <vector>
#include <iterator>


int main()
{
   using boost::multiprecision::cpp_bin_float_100;
   using boost::multiprecision::cpp_int;
   // Create a cpp_bin_float to import/export:
   cpp_bin_float_100 f(1);
   f /= 3;
   // export into 8-bit unsigned values, most significant bit first:
   std::vector<unsigned char> v;
   export_bits(cpp_int(f.backend().bits()), std::back_inserter(v), 8);
   // Grab the exponent as well:
   int e = f.backend().exponent();
   // Import back again, and check for equality, we have to proceed via
   // an intermediate integer:
   cpp_int i;
   import_bits(i, v.begin(), v.end());
   cpp_bin_float_100 g(i);
   g.backend().exponent() = e;
   BOOST_ASSERT(f == g);
}

PrevUpHomeNext