...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
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 <std::size_t MinBits, std::size_t 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 <std::size_t MinBits, std::size_t 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 <std::size_t MinBits, std::size_t 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 std::uintmax_t
when it's assigned to *out
.
Note | |
---|---|
Unfortunately, the standard's OutputIterator concept provides no means
of deducing the type to output since |
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 <std::size_t MinBits, std::size_t 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 that this function is optimized for the case where the data can be
|
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_MP_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_MP_ASSERT(f == g); }