admin管理员组文章数量:1122832
Packing and Unpacking 128-bit Integers using Boost Multiprecision
I'm compressing two 64-bit integers, i0 and i1, into a single 128-bit integer using the formula i = i0 * 2^64 + i1. The resulting multi-precision integer will undergo various arithmetic operations. However, I'm struggling to define the reverse operation to extract the original i0 and i1 values from the multi-precision integer.
The goal is to create the function unpack
that is the inverse of the function pack
is defined by
using int128_t = boost::multiprecision::int128_t;
// This packing is the definition of the desired multi-precision integer.
// Its definition is not negotiable.
auto pack = [](const std::array<int64_t, 2>& value) {
return (((static_cast<int128_t>(1) << 64) * value[0]) + value[1]);
};
The following code fails to properly invert the packing with boost::multiprecision::int128_t
:
C++
auto unpack = [](const int128_t& value) {
const auto lo = static_cast<int64_t>(value % (static_cast<int128_t>(1) << 64));
return std::array<int64_t, 2>{
static_cast<int64_t>((value - lo) / (static_cast<int128_t>(1) << 64)),
lo
};
};
The unpacking function doesn't work as expected. However, the same code works fine when using __int128 (a two's complement implementation). Does anyone know how to implement the unpack function to work correctly with Boost Multiprecision?
A version of the code that tests both boost's and native's int128 implementations is available by following the link:
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
#include <array>
#include <iomanip>
#include <utility>
#include <algorithm>
#include "boost/multiprecision/cpp_int.hpp"
#pragma GCC diagnostic ignored "-Wpedantic"
template <typename OStream> OStream& operator<< (OStream& stream, const std::array<int64_t, 2>& value)
{
stream << "{ " << value[0] << ", " << value[1] << " }";
return stream;
}
template <typename OStream> OStream& operator<< (OStream& stream, __int128 value)
{
if (value == 0) {
stream << "0";
return stream;
}
std::string result;
const auto is_negative = (value < 0);
if (is_negative)
value = -value;
while (value > 0) {
result = char('0' + (value % 10)) + result;
value /= 10;
}
if (is_negative)
result = "-" + result;
stream << result;
return stream;
}
int main()
{
#define USE_INT128
#ifdef USE_INT128
using int128_t = __int128;
constexpr auto ExtendedType = " Extended type __int128";
#else
using int128_t = boost::multiprecision::int128_t;
constexpr auto ExtendedType = " Extended type boost::multiprecision::int128_t";
#endif
auto pack = [](const std::array<int64_t, 2>& value) {
return (((static_cast<int128_t>(1) << 64) * value[0]) + value[1]);
};
auto unpack = [](const int128_t& value) {
const auto lo = static_cast<int64_t>(value % (static_cast<int128_t>(1) << 64));
return std::array<int64_t, 2>{
static_cast<int64_t>((value - lo) / (static_cast<int128_t>(1) << 64)),
lo
};
};
for (auto hiLo : {
std::array<int64_t, 2> { static_cast<int64_t> (-2), (static_cast<int64_t> (1) << 63) },
std::array<int64_t, 2> { static_cast<int64_t> (-1), 1 },
std::array<int64_t, 2> { 1, -10 },
std::array<int64_t, 2> { 1, (static_cast<int64_t> (1) << 63) }
})
{
std::cout << "====================================" << ExtendedType << std::endl;
std::cout << "hiLo = " << hiLo << std::endl;
const auto extended = pack (hiLo);
const auto backToHiLo = unpack (extended);
std::cout << "extended = " << extended << std::endl;
if (hiLo != backToHiLo)
std::cout << "Failed to unpack - to_HiLo (to_ExtendedInteger (" << hiLo << ")) = " << backToHiLo << std::endl;
std::cout << "eExtended = " << pack (backToHiLo) << std::endl << std::endl;
}
return 0;
}
Packing and Unpacking 128-bit Integers using Boost Multiprecision
I'm compressing two 64-bit integers, i0 and i1, into a single 128-bit integer using the formula i = i0 * 2^64 + i1. The resulting multi-precision integer will undergo various arithmetic operations. However, I'm struggling to define the reverse operation to extract the original i0 and i1 values from the multi-precision integer.
The goal is to create the function unpack
that is the inverse of the function pack
is defined by
using int128_t = boost::multiprecision::int128_t;
// This packing is the definition of the desired multi-precision integer.
// Its definition is not negotiable.
auto pack = [](const std::array<int64_t, 2>& value) {
return (((static_cast<int128_t>(1) << 64) * value[0]) + value[1]);
};
The following code fails to properly invert the packing with boost::multiprecision::int128_t
:
C++
auto unpack = [](const int128_t& value) {
const auto lo = static_cast<int64_t>(value % (static_cast<int128_t>(1) << 64));
return std::array<int64_t, 2>{
static_cast<int64_t>((value - lo) / (static_cast<int128_t>(1) << 64)),
lo
};
};
The unpacking function doesn't work as expected. However, the same code works fine when using __int128 (a two's complement implementation). Does anyone know how to implement the unpack function to work correctly with Boost Multiprecision?
A version of the code that tests both boost's and native's int128 implementations is available by following the link:
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
#include <array>
#include <iomanip>
#include <utility>
#include <algorithm>
#include "boost/multiprecision/cpp_int.hpp"
#pragma GCC diagnostic ignored "-Wpedantic"
template <typename OStream> OStream& operator<< (OStream& stream, const std::array<int64_t, 2>& value)
{
stream << "{ " << value[0] << ", " << value[1] << " }";
return stream;
}
template <typename OStream> OStream& operator<< (OStream& stream, __int128 value)
{
if (value == 0) {
stream << "0";
return stream;
}
std::string result;
const auto is_negative = (value < 0);
if (is_negative)
value = -value;
while (value > 0) {
result = char('0' + (value % 10)) + result;
value /= 10;
}
if (is_negative)
result = "-" + result;
stream << result;
return stream;
}
int main()
{
#define USE_INT128
#ifdef USE_INT128
using int128_t = __int128;
constexpr auto ExtendedType = " Extended type __int128";
#else
using int128_t = boost::multiprecision::int128_t;
constexpr auto ExtendedType = " Extended type boost::multiprecision::int128_t";
#endif
auto pack = [](const std::array<int64_t, 2>& value) {
return (((static_cast<int128_t>(1) << 64) * value[0]) + value[1]);
};
auto unpack = [](const int128_t& value) {
const auto lo = static_cast<int64_t>(value % (static_cast<int128_t>(1) << 64));
return std::array<int64_t, 2>{
static_cast<int64_t>((value - lo) / (static_cast<int128_t>(1) << 64)),
lo
};
};
for (auto hiLo : {
std::array<int64_t, 2> { static_cast<int64_t> (-2), (static_cast<int64_t> (1) << 63) },
std::array<int64_t, 2> { static_cast<int64_t> (-1), 1 },
std::array<int64_t, 2> { 1, -10 },
std::array<int64_t, 2> { 1, (static_cast<int64_t> (1) << 63) }
})
{
std::cout << "====================================" << ExtendedType << std::endl;
std::cout << "hiLo = " << hiLo << std::endl;
const auto extended = pack (hiLo);
const auto backToHiLo = unpack (extended);
std::cout << "extended = " << extended << std::endl;
if (hiLo != backToHiLo)
std::cout << "Failed to unpack - to_HiLo (to_ExtendedInteger (" << hiLo << ")) = " << backToHiLo << std::endl;
std::cout << "eExtended = " << pack (backToHiLo) << std::endl << std::endl;
}
return 0;
}
Share
Improve this question
edited Nov 24, 2024 at 21:50
Catriel
asked Nov 22, 2024 at 21:32
CatrielCatriel
6294 silver badges17 bronze badges
0
3 Answers
Reset to default 0The problem is from sign extension. Bit manipulation becomes a lot easier if you make the inert parts uint64_t
. Is there a reason you can't do that?
Here's a demo, updated for the comments. We generalize Tuple
(which can be array<int64_t, 2>
, array<uint64_t, 2>
, tuple<int64_t, uint64_t>
etc.) as well as T
(which can be Boost's or the compiler builtin 128 bit signed integer type).
template <typename Tuple> Tuple make(auto hi, auto lo) {
return Tuple{
std::tuple_element_t<0, Tuple>(hi),
std::tuple_element_t<1, Tuple>(lo),
};
};
template <typename T, typename Tuple> T single_test(Tuple const& hiLo) {
auto pack = [](Tuple const& v) {
static_assert(sizeof(v) == 2 * sizeof(int64_t));
auto& [hi, lo] = v;
return (T(static_cast<int64_t>(hi)) << 64) + static_cast<uint64_t>(lo);
};
auto unpack = [](T const& v) -> Tuple {
static constexpr T scale64 = T(1) << 64;
return make<Tuple>( //
static_cast<int64_t>(v >> 64), //
static_cast<uint64_t>(v & (scale64 - 1)) //
);
};
auto const extended = pack(hiLo);
auto const roundtrip = unpack(extended);
if (hiLo != roundtrip)
fmt::print("\n\n ****Failed*** to unpack {} - to_HiLo (to_ExtendedInteger ({})) = {}\n\n", //
Name<T>, hiLo, roundtrip);
// fmt::print("eExtended = {0}\n\n", pack(roundtrip));
return extended;
}
template<typename Tuple> void run_tests() {
fmt::print("\n==== {} ====\n", __PRETTY_FUNCTION__);
for (auto hiLo : {
make<Tuple>(0xFFFFFFFFFFFFFFFE, 0x8000000000000000),
make<Tuple>(0xFFFFFFFFFFFFFFFF, 1),
make<Tuple>(1, 0xFFFFFFFFFFFFFFF6),
make<Tuple>(1, 0x8000000000000000),
}) {
// fmt::print("hiLo = {}\n", hiLo);
auto a = single_test<__int128_t>(hiLo);
auto b = single_test<boost::multiprecision::int128_t>(hiLo);
auto match = (a == b);
fmt::print("Matching: {} ({} == {})\n", match, a, b);
assert(match);
}
}
int main() {
run_tests<std::tuple<uint64_t, uint64_t>>();
run_tests<std::tuple<uint64_t, int64_t>>();
run_tests<std::tuple<int64_t, uint64_t>>();
run_tests<std::tuple<int64_t, int64_t>>();
run_tests<std::array<uint64_t, 2>>();
run_tests<std::array<int64_t, 2>>();
}
Live On Coliru
Printing
==== void run_tests() [with Tuple = std::tuple<long unsigned int, long unsigned int>] ====
Matching: true (-27670116110564327424 == -27670116110564327424)
Matching: true (-18446744073709551615 == -18446744073709551615)
Matching: true (36893488147419103222 == 36893488147419103222)
Matching: true (27670116110564327424 == 27670116110564327424)
==== void run_tests() [with Tuple = std::tuple<long unsigned int, long int>] ====
Matching: true (-27670116110564327424 == -27670116110564327424)
Matching: true (-18446744073709551615 == -18446744073709551615)
Matching: true (36893488147419103222 == 36893488147419103222)
Matching: true (27670116110564327424 == 27670116110564327424)
==== void run_tests() [with Tuple = std::tuple<long int, long unsigned int>] ====
Matching: true (-27670116110564327424 == -27670116110564327424)
Matching: true (-18446744073709551615 == -18446744073709551615)
Matching: true (36893488147419103222 == 36893488147419103222)
Matching: true (27670116110564327424 == 27670116110564327424)
==== void run_tests() [with Tuple = std::tuple<long int, long int>] ====
Matching: true (-27670116110564327424 == -27670116110564327424)
Matching: true (-18446744073709551615 == -18446744073709551615)
Matching: true (36893488147419103222 == 36893488147419103222)
Matching: true (27670116110564327424 == 27670116110564327424)
==== void run_tests() [with Tuple = std::array<long unsigned int, 2>] ====
Matching: true (-27670116110564327424 == -27670116110564327424)
Matching: true (-18446744073709551615 == -18446744073709551615)
Matching: true (36893488147419103222 == 36893488147419103222)
Matching: true (27670116110564327424 == 27670116110564327424)
==== void run_tests() [with Tuple = std::array<long int, 2>] ====
Matching: true (-27670116110564327424 == -27670116110564327424)
Matching: true (-18446744073709551615 == -18446744073709551615)
Matching: true (36893488147419103222 == 36893488147419103222)
Matching: true (27670116110564327424 == 27670116110564327424)
BONUS
Beefed up with systematic edge-cases construction and roundtrip verification in all possible combination, as well as a million uniformly random HiLo samples for each run:
Live On Coliru
#include <boost/multiprecision/cpp_int.hpp>
#include <fmt/ostream.h>
#include <fmt/ranges.h>
#include <fmt/std.h>
#include <random>
static constexpr size_t NUM_RANDOM_SAMPLES = 1'000'000;
template <> struct fmt::formatter<boost::multiprecision::int128_t> : fmt::ostream_formatter {};
#define ABORT_ASSERT(cond) \
do { \
if (!(cond)) { \
fmt::print("Assertion failed: {} in {}:{}\n", #cond, __FILE__, __LINE__); \
::exit(1); \
} \
} while (false)
template <typename Tuple> Tuple make(auto hi, auto lo) {
return Tuple{
std::tuple_element_t<0, Tuple>(hi),
std::tuple_element_t<1, Tuple>(lo),
};
};
template <typename T, typename Tuple> struct Impl { // Helpers grouping template parameters
static constexpr std::string_view Name() { return __PRETTY_FUNCTION__; }
static auto pack(Tuple const& v) {
static_assert(sizeof(v) == 2 * sizeof(int64_t));
auto& [hi, lo] = v;
return (T(static_cast<int64_t>(hi)) << 64) + static_cast<uint64_t>(lo);
};
static Tuple unpack(T const& v) {
static constexpr T scale64 = T(1) << 64;
return make<Tuple>( //
static_cast<int64_t>(v >> 64), //
static_cast<uint64_t>(v & (scale64 - 1)) //
);
};
static T run_single_test(Tuple const& hiLo) {
auto const extended = pack(hiLo);
auto const roundtrip = unpack(extended);
if (hiLo != roundtrip)
fmt::print("\n\n ****Failed*** to unpack {} - to_HiLo (to_ExtendedInteger ({})) = {}\n\n", //
Name(), hiLo, roundtrip);
else
ABORT_ASSERT(hiLo == roundtrip);
ABORT_ASSERT(pack(roundtrip) == extended);
ABORT_ASSERT(unpack(pack(roundtrip)) == roundtrip); // etc... just driving the point home
return extended;
}
};
template <typename Tuple> void run_batch_tests(bool verbose, std::vector<Tuple> const& testCases) {
using GCC = Impl<__int128_t, Tuple>;
using Boost = Impl<boost::multiprecision::int128_t, Tuple>;
for (auto hiLo : testCases) {
// fmt::print("hiLo = {}\n", hiLo);
auto a = GCC::run_single_test(hiLo);
auto b = Boost::run_single_test(hiLo);
auto match = (a == b);
if (verbose)
fmt::print("Matching: {} ({} == {})\n", match, a, b);
ABORT_ASSERT(match);
}
}
template <typename Tuple> auto generateRandomSamples(size_t seed, unsigned howMany) {
auto gen = bind(std::uniform_int_distribution<uint64_t>{}, std::mt19937_64(seed));
std::vector<Tuple> result;
generate_n(back_inserter(result), howMany, [&] { return make<Tuple>(gen(), gen()); });
return result;
}
template <typename Tuple> void driver(size_t seed) {
enum : bool { quiet, verbose };
run_batch_tests<Tuple>(quiet, generateRandomSamples<Tuple>(seed, NUM_RANDOM_SAMPLES));
fmt::print("\n==== {} ====\n", __PRETTY_FUNCTION__);
fmt::print("{:L} uniform random samples passed (seed: {})\n", NUM_RANDOM_SAMPLES, seed);
using Hi = std::tuple_element_t<0, Tuple>;
using Lo = std::tuple_element_t<1, Tuple>;
using HiLim = std::numeric_limits<Hi>;
using LoLim = std::numeric_limits<Lo>;
std::vector<Tuple> edgeCases;
for (Hi hi : {Hi{}, Hi{+1}, static_cast<Hi>(-1), //
HiLim::min(), HiLim::max(), //
Hi(LoLim::min()), Hi(LoLim::max())})
for (Lo lo : {Lo{}, Lo{+1}, static_cast<Lo>(-1), //
LoLim::min(), LoLim::max(), //
Lo(HiLim::min()), Lo(HiLim::max())})
edgeCases.push_back(Tuple{hi, lo});
std::ranges::sort(edgeCases);
edgeCases.erase(std::unique(edgeCases.begin(), edgeCases.end()), edgeCases.end());
run_batch_tests<Tuple>(verbose, edgeCases);
}
int main() {
size_t const seed = std::random_device{}();
fmt::print("Seed: {}\n", seed);
driver<std::tuple<uint64_t, uint64_t>>(seed);
driver<std::tuple<uint64_t, int64_t>>(seed);
driver<std::tuple<int64_t, uint64_t>>(seed);
driver<std::tuple<int64_t, int64_t>>(seed);
driver<std::array<uint64_t, 2>>(seed);
driver<std::array<int64_t, 2>>(seed);
}
Printing
Seed: 675096274
==== void driver(size_t) [with Tuple = std::tuple<long unsigned int, long unsigned int>; size_t = long unsigned int] ====
1000000 uniform random samples passed (seed: 675096274)
Matching: true (0 == 0)
Matching: true (1 == 1)
Matching: true (18446744073709551615 == 18446744073709551615)
Matching: true (18446744073709551616 == 18446744073709551616)
Matching: true (18446744073709551617 == 18446744073709551617)
Matching: true (36893488147419103231 == 36893488147419103231)
Matching: true (-18446744073709551616 == -18446744073709551616)
Matching: true (-18446744073709551615 == -18446744073709551615)
Matching: true (-1 == -1)
==== void driver(size_t) [with Tuple = std::tuple<long unsigned int, long int>; size_t = long unsigned int] ====
1000000 uniform random samples passed (seed: 675096274)
Matching: true (9223372036854775808 == 9223372036854775808)
Matching: true (18446744073709551615 == 18446744073709551615)
Matching: true (0 == 0)
Matching: true (1 == 1)
Matching: true (9223372036854775807 == 9223372036854775807)
Matching: true (27670116110564327424 == 27670116110564327424)
Matching: true (36893488147419103231 == 36893488147419103231)
Matching: true (18446744073709551616 == 18446744073709551616)
Matching: true (18446744073709551617 == 18446744073709551617)
Matching: true (27670116110564327423 == 27670116110564327423)
Matching: true (170141183460469231722463931679029329920 == 170141183460469231722463931679029329920)
Matching: true (170141183460469231731687303715884105727 == 170141183460469231731687303715884105727)
Matching: true (170141183460469231713240559642174554112 == 170141183460469231713240559642174554112)
Matching: true (170141183460469231713240559642174554113 == 170141183460469231713240559642174554113)
Matching: true (170141183460469231722463931679029329919 == 170141183460469231722463931679029329919)
Matching: true (-170141183460469231722463931679029329920 == -170141183460469231722463931679029329920)
Matching: true (-170141183460469231713240559642174554113 == -170141183460469231713240559642174554113)
Matching: true (-170141183460469231731687303715884105728 == -170141183460469231731687303715884105728)
Matching: true (-170141183460469231731687303715884105727 == -170141183460469231731687303715884105727)
Matching: true (-170141183460469231722463931679029329921 == -170141183460469231722463931679029329921)
Matching: true (-9223372036854775808 == -9223372036854775808)
Matching: true (-1 == -1)
Matching: true (-18446744073709551616 == -18446744073709551616)
Matching: true (-18446744073709551615 == -18446744073709551615)
Matching: true (-9223372036854775809 == -9223372036854775809)
==== void driver(size_t) [with Tuple = std::tuple<long int, long unsigned int>; size_t = long unsigned int] ====
1000000 uniform random samples passed (seed: 675096274)
Matching: true (-170141183460469231731687303715884105728 == -170141183460469231731687303715884105728)
Matching: true (-170141183460469231731687303715884105727 == -170141183460469231731687303715884105727)
Matching: true (-170141183460469231722463931679029329921 == -170141183460469231722463931679029329921)
Matching: true (-170141183460469231722463931679029329920 == -170141183460469231722463931679029329920)
Matching: true (-170141183460469231713240559642174554113 == -170141183460469231713240559642174554113)
Matching: true (-18446744073709551616 == -18446744073709551616)
Matching: true (-18446744073709551615 == -18446744073709551615)
Matching: true (-9223372036854775809 == -9223372036854775809)
Matching: true (-9223372036854775808 == -9223372036854775808)
Matching: true (-1 == -1)
Matching: true (0 == 0)
Matching: true (1 == 1)
Matching: true (9223372036854775807 == 9223372036854775807)
Matching: true (9223372036854775808 == 9223372036854775808)
Matching: true (18446744073709551615 == 18446744073709551615)
Matching: true (18446744073709551616 == 18446744073709551616)
Matching: true (18446744073709551617 == 18446744073709551617)
Matching: true (27670116110564327423 == 27670116110564327423)
Matching: true (27670116110564327424 == 27670116110564327424)
Matching: true (36893488147419103231 == 36893488147419103231)
Matching: true (170141183460469231713240559642174554112 == 170141183460469231713240559642174554112)
Matching: true (170141183460469231713240559642174554113 == 170141183460469231713240559642174554113)
Matching: true (170141183460469231722463931679029329919 == 170141183460469231722463931679029329919)
Matching: true (170141183460469231722463931679029329920 == 170141183460469231722463931679029329920)
Matching: true (170141183460469231731687303715884105727 == 170141183460469231731687303715884105727)
==== void driver(size_t) [with Tuple = std::tuple<long int, long int>; size_t = long unsigned int] ====
1000000 uniform random samples passed (seed: 675096274)
Matching: true (-170141183460469231722463931679029329920 == -170141183460469231722463931679029329920)
Matching: true (-170141183460469231713240559642174554113 == -170141183460469231713240559642174554113)
Matching: true (-170141183460469231731687303715884105728 == -170141183460469231731687303715884105728)
Matching: true (-170141183460469231731687303715884105727 == -170141183460469231731687303715884105727)
Matching: true (-170141183460469231722463931679029329921 == -170141183460469231722463931679029329921)
Matching: true (-9223372036854775808 == -9223372036854775808)
Matching: true (-1 == -1)
Matching: true (-18446744073709551616 == -18446744073709551616)
Matching: true (-18446744073709551615 == -18446744073709551615)
Matching: true (-9223372036854775809 == -9223372036854775809)
Matching: true (9223372036854775808 == 9223372036854775808)
Matching: true (18446744073709551615 == 18446744073709551615)
Matching: true (0 == 0)
Matching: true (1 == 1)
Matching: true (9223372036854775807 == 9223372036854775807)
Matching: true (27670116110564327424 == 27670116110564327424)
Matching: true (36893488147419103231 == 36893488147419103231)
Matching: true (18446744073709551616 == 18446744073709551616)
Matching: true (18446744073709551617 == 18446744073709551617)
Matching: true (27670116110564327423 == 27670116110564327423)
Matching: true (170141183460469231722463931679029329920 == 170141183460469231722463931679029329920)
Matching: true (170141183460469231731687303715884105727 == 170141183460469231731687303715884105727)
Matching: true (170141183460469231713240559642174554112 == 170141183460469231713240559642174554112)
Matching: true (170141183460469231713240559642174554113 == 170141183460469231713240559642174554113)
Matching: true (170141183460469231722463931679029329919 == 170141183460469231722463931679029329919)
==== void driver(size_t) [with Tuple = std::array<long unsigned int, 2>; size_t = long unsigned int] ====
1000000 uniform random samples passed (seed: 675096274)
Matching: true (0 == 0)
Matching: true (1 == 1)
Matching: true (18446744073709551615 == 18446744073709551615)
Matching: true (18446744073709551616 == 18446744073709551616)
Matching: true (18446744073709551617 == 18446744073709551617)
Matching: true (36893488147419103231 == 36893488147419103231)
Matching: true (-18446744073709551616 == -18446744073709551616)
Matching: true (-18446744073709551615 == -18446744073709551615)
Matching: true (-1 == -1)
==== void driver(size_t) [with Tuple = std::array<long int, 2>; size_t = long unsigned int] ====
1000000 uniform random samples passed (seed: 675096274)
Matching: true (-170141183460469231722463931679029329920 == -170141183460469231722463931679029329920)
Matching: true (-170141183460469231713240559642174554113 == -170141183460469231713240559642174554113)
Matching: true (-170141183460469231731687303715884105728 == -170141183460469231731687303715884105728)
Matching: true (-170141183460469231731687303715884105727 == -170141183460469231731687303715884105727)
Matching: true (-170141183460469231722463931679029329921 == -170141183460469231722463931679029329921)
Matching: true (-9223372036854775808 == -9223372036854775808)
Matching: true (-1 == -1)
Matching: true (-18446744073709551616 == -18446744073709551616)
Matching: true (-18446744073709551615 == -18446744073709551615)
Matching: true (-9223372036854775809 == -9223372036854775809)
Matching: true (9223372036854775808 == 9223372036854775808)
Matching: true (18446744073709551615 == 18446744073709551615)
Matching: true (0 == 0)
Matching: true (1 == 1)
Matching: true (9223372036854775807 == 9223372036854775807)
Matching: true (27670116110564327424 == 27670116110564327424)
Matching: true (36893488147419103231 == 36893488147419103231)
Matching: true (18446744073709551616 == 18446744073709551616)
Matching: true (18446744073709551617 == 18446744073709551617)
Matching: true (27670116110564327423 == 27670116110564327423)
Matching: true (170141183460469231722463931679029329920 == 170141183460469231722463931679029329920)
Matching: true (170141183460469231731687303715884105727 == 170141183460469231731687303715884105727)
Matching: true (170141183460469231713240559642174554112 == 170141183460469231713240559642174554112)
Matching: true (170141183460469231713240559642174554113 == 170141183460469231713240559642174554113)
Matching: true (170141183460469231722463931679029329919 == 170141183460469231722463931679029329919)
I found an implementation of the function to unpack a boost::multiprecision::int128_t
into a std::array<int64_t, 2>
, but I'm not confident about its robustness across all possible cases.
After studying the Boost multi-precision code and experimenting, I came up with:
inline std::array<int64_t, 2> unpack_int128_t (const boost::multiprecision::int128_t& value)
{
constexpr auto TwoPowerNBits = boost::multiprecision::int128_t (1) << 64;
const auto lo = (value % TwoPowerNBits);
const auto hi = (value - static_cast<int64_t> (lo)) / TwoPowerNBits;
if (lo != (static_cast<int64_t> (lo)))
{
return ((lo > 0) ?
std::array { static_cast<int64_t>(hi + 1), static_cast<int64_t> (lo - TwoPowerNBits) } :
std::array { static_cast<int64_t>(hi - 1), static_cast<int64_t> (lo + TwoPowerNBits) });
}
return std::array { static_cast<int64_t>(hi), static_cast<int64_t> (lo) };
}
Concerns and Suggestions
While this function works for my test cases (available on Coliru), I'm not confident in its generality. It would be beneficial if the Boost library provided a function to split a multi-precision object into n integers satisfying:
Here is the rendered mathematical expression:
This function could work similarly to convert_to<...>
, returning a vector with the minimal set of integers. The function could be named unpack_to_vector
that would take, for example, a boost::multiprecision::int128_t
as input, returning a std::vector<int64_t>
.
std::vector<int64_t> unpack_to_vector(const boost::multiprecision::int128_t& value);
In general, the function would depend on the size of the multi-precision integer. This would provide a more robust and general solution for unpacking multi-precision integers given that boost's implementation does not adhere to the most common two's complement implementation used by our compilers.
JZMaddock's reply to this issue
I came up with:
std::array<int64_t, 2> unpack_int128_t(const boost::multiprecision::int128_t& value) { constexpr auto TwoPowerNBits = boost::multiprecision::int128_t(1) << 64; boost::multiprecision::int128_t high, low; boost::multiprecision::divide_qr(value, TwoPowerNBits, high, low); if (boost::multiprecision::bit_test(value, 63)) { low -= TwoPowerNBits; ++high; } std::array<int64_t, 2> result = { static_cast<int64_t>(high), static_cast<int64_t>(low) }; assert(boost::multiprecision::int128_t(result[0]) * TwoPowerNBits + result[1] == value); return result; }
Which I hope is a little easier to see what's going on.
It would be nice to avoid divide_qr which is more efficient than separate / and %, but still horribly inefficient compared to bit-fiddling, but I don't see a good bit-fiddling solution at present!
本文标签: How can I split a boostmultiprecisionint128t into a hi and lo int64t portionsStack Overflow
版权声明:本文标题:How can I split a boost::multiprecision::int128_t into a hi and lo int64_t portions? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736300707a1930913.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论