首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何将boost multiprecision整数从低字节转换为高字节?

如何将boost multiprecision整数从低字节转换为高字节?
EN

Stack Overflow用户
提问于 2019-09-29 15:30:50
回答 1查看 350关注 0票数 1

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

正如您可以看到的,see the data of PUSH instructions的顺序是错误的,而Ethereum是一台高字节顺序的机器(地址被正确表示,因为它们使用的是较小的类型)。

另一种方法是运行porosity.exe --code '0x61004b60026319e44e32' --disassm

u256类型定义为

代码语言:javascript
复制
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

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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序列

EN

回答 1

Stack Overflow用户

发布于 2019-09-29 16:06:02

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

如果您确实知道目标C实现的字节顺序和boost对象的数据布局,就可以使用unsigned char*按地址降序高效地遍历它。

引入字节序的概念只是因为它与字节颠倒相关,而字节颠倒正是您要做的。但这真的很低效,只需以另一种方式循环你的bigint的字节。

我很犹豫是否推荐一个具体的解决方案,因为我不知道什么才能有效地编译。但是您可能想要类似这样的,而不是在之前进行字节反转

代码语言:javascript
复制
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
    }
}

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

在外部循环中,IDK if boost::multiprecision::number有任何用于高效索引内建块的API;如果有,使用这些API可能会更有效。

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

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

例如:

代码语言:javascript
复制
  // 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;

如果你想把它分成块来换行,你也可以这样做。

如果您对一次8、16或32字节的数据有效转换为十六进制感兴趣,请参阅How to convert a number to hex? 中的一些x86 SIMD ways。asm应该可以很容易地移植到C++内部。(您可以使用SIMD shuffles处理从little-endian整数加载后将字节放入MSB优先打印顺序。)

您还可以使用SIMD shuffle来分隔您的十六进制数字对,然后再存储到内存中,就像您显然想要的那样。

您添加的代码中存在错误:

,所以我在上面的循环之前添加了这段代码:

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包装器函数。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58153194

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档