c++ - 如何将boost多精度整数从小端转换为大端?

标签 c++ boost c++14 endianness boost-multiprecision

I am trying to fix this part of an abandonware program因为I failed to find an alternative program .

尽你所能 see the data of PUSH instructions顺序错误,而以太坊是大端机器(地址正确表示,因为它们使用较小的类型)。
另一种方法是运行 porosity.exe --code '0x61004b60026319e44e32' --disassm

u256 类型定义为

using u256 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<256, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>;

这是一个 minimal example to reproduce the bug :

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

using u256 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<256, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>;

int main() {
    std::stringstream stream;
    u256 data=0xFEDEFA;
    for (int i = 0; i<5; ++i) { // print only the first 5 digits
        uint8_t dataByte = int(data & 0xFF);
        data >>= 8;
        stream << std::setfill('0') << std::setw(sizeof(char) * 2) << std::hex << int(dataByte) << "  ";
    }
    std::cout << stream.str();
}

所以数字被转换为字符串,每个字节之间有一个空格(并且只有第一个字节)。

但后来我遇到了字节序问题:字节以相反的顺序打印。我的意思是,例如,31722 在我的机器上写成 8a 02 02 而在为大端目标编译时写成 02 02 8a

因为我不知道要调用哪个 boost 函数,所以我 modified the code :

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

using u256 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<256, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>;

int main() {
    std::stringstream stream;
    u256 data=0xFEDEFA;
    for (int i = 0; i<5; ++i) {
        uint8_t dataByte = int(data >> ((32 - i - 1) * 8));
        stream << std::setfill('0') << std::setw(sizeof(char) * 2) << std::hex << int(dataByte) << "  ";
    }
    std::cout << stream.str();
}

现在,为什么我的 256 位整数大部分打印为 00 00 00 00 00 系列?

最佳答案

顺便说一句,这不是字节序问题;您没有对对象表示进行字节访问。您将其作为 256 位整数进行操作,并使用 data & 0xFF 一次简单地请求低 8 位。

如果您确实知道 objective-c 实现的字节序,以及 boost 对象的数据布局,您可以使用 unsigned char* 按降序地址顺序有效地循环它。

您引入字节顺序的概念只是因为它与字节反转相关联,而这正是您想要做的。 但这真的很低效,只需以另一种方式循环遍历 bigint 的字节即可。


我对推荐一个特定的解决方案犹豫不决,因为我不知道什么可以有效编译。但是你可能想要这样的东西而不是提前字节反转:

for (outer loop) {
    uint64_t chunk = data >> (64*3);  // grab the highest 64-bit chunk
    data <<= 64;   // and shift everything up
    // alternative: maybe keep a shift-count in a variable instead of modifying `data`

    // Then pick apart the chunk into its component bytes, in MSB first order
    for (int = 0 ; i<8 ; i++) {
        unsigned tmp = (chunk >> 56) & 0xFF;
        // do something with it
        chunk <<= 8;                   // bring the next byte to the top
    }
}

在内部循环中,比使用两次移位更有效的方法是使用 rotate 将高字节移到底部(对于 & 0xFF ),同时向上移动低字节。 Best practices for circular shift (rotate) operations in C++

在外层循环中,如果 boost::multiprecision::number 有任何内置 block 的高效索引的 API,则 IDK;如果是这样,使用它可能更有效。

我使用嵌套循环是因为我假设 data <<= 8 的编译效率不是特别高, (data >> (256-8)) & 0xFF 也不会。但这就是您从顶部而不是底部获取字节的方式。


另一个选项是将数字转换为字符串的标准技巧:将字符按降序存储到缓冲区中。一个 256 位(32 字节)的数字将占用 64 个十六进制数字,并且您需要在它们之间再留出 32 个字节的空格。

例如:

  // 97 = 32 * 2 + 32, plus 1 byte for an implicit-length C string terminator
  // plus another 1 for an extra space
  char buf[98];            // small enough to use automatic storage
  char *outp = buf+96;     // pointer to the end
  *outp = 0;               // terminator
  const char *hex_lut = "0123456789abcdef";

  for (int i=0 ; i<32 ; i++) {
      uint8_t byte = data & 0xFF;
      *--outp = hex_lut[byte >> 4];
      *--outp = hex_lut[byte & 0xF];
      *--outp = ' ';
      data >>= 8;
  }
  // outp points at an extra ' '
  outp++;
  // outp points at the first byte of a string like  "12 ab cd"
  stream << outp;

如果你想把它分成 block 并在其中放一个换行符,你也可以这样做。


如果您对一次性将 8、16 或 32 字节数据高效转换为十六进制感兴趣,请参阅 How to convert a number to hex? 了解一些 x86 SIMD 方式。 asm 应该很容易移植到 C++ 内在函数。 (您可以使用 SIMD 随机播放来处理从小端整数加载后将字节放入 MSB 优先的打印顺序。)

您还可以使用 SIMD 洗牌在存储到内存之前使用空格分隔您的十六进制数字对,就像您在这里显然想要的那样。


您添加的代码中的错误:

So I added this code before the loop above:

  for(unsigned int i=0,data,_data;i<33;++i)

unsigned i, data, _data 声明了 unsigned int 类型的新变量,这些变量隐藏了 data_data 之前的声明。该循环对循环范围外的 data_data 的影响为零。 (并且包含 UB,因为您读取 _datadata 而没有初始化它们。)

如果这些变量实际上仍然是外部范围的 u256 变量,那么除了效率之外我没有看到明显的问题,但也许我也遗漏了明显的问题。我看起来并没有很努力,因为使用 64x 256 位移位和 32x OR 似乎是一个可怕的想法。 可能它可以完全优化掉,或者在具有它们的 ISA 上进入 bswap 字节反转指令,但我对此表示怀疑。尤其是 boost::multiprecision::number 包装函数的额外复杂性。

关于c++ - 如何将boost多精度整数从小端转换为大端?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58153194/

相关文章:

c++ - 应用考虑特定边缘子集的算法

c++ - ODR 使用转发的 constexpr 参数?

c++ - 等效的 static_asserts 给出了 is_array<> 的冲突结果

c++ - 未命名命名空间内名称的外部链接

C++ Mac OS X semaphore.h : trouble with `sem_open()` and `sem_wait()`

c++ - 是否使用 volatile 引用保证读取操作不会被重新排序?

C++ 输出显示不正确, 'marks' 不显示

c++ - 我如何使用 std::constructible_from

c++ - Boost::Regex 在长表达式不匹配时抛出错误

c++ - 使用 Boost.Spirit 仅解析特定数字