【问题标题】:How to change a boost::multiprecision::cpp_int from big endian to little endian如何将 boost::multiprecision::cpp_int 从大端更改为小端
【发布时间】:2020-04-29 11:39:43
【问题描述】:

我有一个大端的 boost::multiprecision::cpp_int 并且必须将其更改为小端。我怎样才能做到这一点?我尝试了 boost::endian::conversion 但没有奏效。

boost::multiprecision::cpp_int bigEndianInt("0xe35fa931a0000*);
boost::multiprecision::cpp_int littleEndianInt;

littleEndianIn = boost::endian::endian_reverse(m_cppInt);

【问题讨论】:

    标签: boost


    【解决方案1】:

    boost 多精度类型的内存布局是实现细节。所以无论如何你都不能假设太多(它们不应该是按位可序列化的)。

    只需阅读文档的随机部分:

    最小比特数

    在诉诸动态内存分配之前确定要直接存储在对象中的位数。当为零时,该字段根据可以与动态存储标头联合存储的位数自动确定:设置较大的值可能会提高性能,因为在需要内存分配之前将在内部存储较大的整数值。

    目前尚不清楚您是否有机会在内存布局中实现某种程度的“正常 int 行为”。唯一的例外是 MinBits==MaxBits。

    确实,我们可以静态断言带有此类后端配置的 cpp_int 的大小与相应的字节大小匹配。

    事实证明,在后端基类中甚至还有一个有希望的标签来表示“琐碎”(这确实很有希望):trivial_tag,所以让我们使用它:

    Live On Coliru

    #include <boost/multiprecision/cpp_int.hpp>
    namespace mp = boost::multiprecision;
    
    template <int bits> using simple_be =
        mp::cpp_int_backend<bits, bits, mp::unsigned_magnitude>;
    template <int bits> using my_int =
        mp::number<simple_be<bits>, mp::et_off>;
    
    using my_int8_t = my_int<8>;
    using my_int16_t = my_int<16>;
    using my_int32_t = my_int<32>;
    using my_int64_t = my_int<64>;
    using my_int128_t = my_int<128>;
    using my_int192_t = my_int<192>;
    using my_int256_t = my_int<256>;
    
    template <typename Num>
        constexpr bool is_trivial_v = Num::backend_type::trivial_tag::value;
    
    int main() {
        static_assert(sizeof(my_int8_t) == 1);
        static_assert(sizeof(my_int16_t) == 2);
        static_assert(sizeof(my_int32_t) == 4);
        static_assert(sizeof(my_int64_t) == 8);
        static_assert(sizeof(my_int128_t) == 16);
    
        static_assert(is_trivial_v<my_int8_t>);
        static_assert(is_trivial_v<my_int16_t>);
        static_assert(is_trivial_v<my_int32_t>);
        static_assert(is_trivial_v<my_int64_t>);
        static_assert(is_trivial_v<my_int128_t>);
    
        // however it doesn't scale
        static_assert(sizeof(my_int192_t) != 24);
        static_assert(sizeof(my_int256_t) != 32);
        static_assert(not is_trivial_v<my_int192_t>);
        static_assert(not is_trivial_v<my_int256_t>);
    }
    

    结论:您可以拥有微不足道的 int 表示直到某一点,之后您将获得基于分配器的动态肢体实现无论如何。

    • 请注意,使用 unsigned_packed 而不是 unsigned_magnitude 表示永远不会导致微不足道的后端实现。

    • 请注意,琐碎性可能取决于编译器/平台选择(cpp_128_t 可能在 GCC 上使用了一些内置编译器/标准库支持,例如)


    鉴于此,您可能能够利用 hacksIF 实现您想要做的事情,您的后端配置支持微不足道。遗憾的是,我认为它需要您手动重载 endian_reverse 以实现 128 位大小写,因为 GCC builtins 没有 __builtin_bswap128,Boost Endian 也没有定义。

    我建议在这里处理How to make GCC generate bswap instruction for big endian store without builtins?的信息

    最终演示(未完成)

    #include <boost/multiprecision/cpp_int.hpp>
    #include <boost/endian/buffers.hpp>
    namespace mp = boost::multiprecision;
    namespace be = boost::endian;
    
    template <int bits> void check() {
        using T = mp::number<mp::cpp_int_backend<bits, bits, mp::unsigned_magnitude>, mp::et_off>;
    
        static_assert(sizeof(T) == bits/8);
        static_assert(T::backend_type::trivial_tag::value);
    
        be::endian_buffer<be::order::big, T, bits, be::align::no> buf;
        buf = T("0x0102030405060708090a0b0c0d0e0f00");
    
        std::cout << std::hex << buf.value() << "\n";
    }
    
    int main() {
        check<128>();
    }
    

    (将be::order::big 更改为be::order::native 显然可以编译。完成它的另一种方法是为您的int 类型为endian_reverse 设置一个ADL 可访问重载。)

    【讨论】:

    • 非常感谢您的详细解答。
    【解决方案2】:

    这是微不足道的,在一般情况下无法回答,让我解释一下:

    • 对于一般的 N 位整数,其中 N 是一个很大的数,不可能有任何明确定义的字节顺序,实际上即使是 64 位和 128 位整数,也有超过 2 个可能的顺序在使用:https://en.wikipedia.org/wiki/Endianness#Middle-endian .
    • 在任何平台上,使用任何本机字节序,您始终可以提取 cpp_int 的字节,这里的第一个示例:https://www.boost.org/doc/libs/1_73_0/libs/multiprecision/doc/html/boost_multiprecision/tut/import_export.html#boost_multiprecision.tut.import_export.examples 向您展示了如何。当像这样导出字节时,它们总是最重要的字节在前,因此您可以随后按照您的意愿重新排列它们。但是,您不应该重新排列它们并将它们加载回 cpp_int,因为该类不知道如何处理结果!
    • 如果您知道该值足够小以适合本地整数类型,那么您可以简单地转换为本地整数并在结果上使用系统 API。如endian_reverse(static_cast&lt;int64_t&gt;(my_cpp_int))。同样,不要将结果分配回 cpp_int,因为它需要本地字节顺序。
    • 如果您想检查一个值是否足够小以适合上述方法的 N 位整数,您可以使用 msb 函数,该函数返回 cpp_int 中最高有效位的索引,添加一对一来获得使用的位数,并过滤掉零的情况,代码如下:

      unsigned bits_used = my_cpp_int.is_zero() ? 0 : msb(my_cpp_int) + 1;

    请注意,以上所有代码都使用完全可移植的代码 - 不需要对底层实现进行黑客攻击。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-14
      • 2015-07-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多