13

我有一个string代表十六进制数字的 8 个字符,我需要将其转换为int. 这种转换必须保留字符串"80000000"和更高的位模式,即这些数字应该是负数。不幸的是,天真的解决方案:

int hex_str_to_int(const string hexStr)
{    
    stringstream strm;
    strm << hex << hexStr;
    unsigned int val = 0;
    strm >> val;
    return static_cast<int>(val);
}

val > MAX_INT如果(返回值为 0)不适用于我的编译器。将 val 的类型更改为int也会导致更大的数字为 0。我已经从这里的各种答案中尝试了几种不同的解决方案,但还没有成功。

以下是我所知道的:

  • 我在 OpenVMS 上使用 HP 的 C++ 编译器(我相信使用的是安腾处理器)。
  • sizeof(int)在我的代码将运行的每个架构上至少有 4 个。
  • 从数字 > INT_MAX 转换为 int 是实现定义的。在我的机器上,它通常会导致 0,但有趣的是,当值太大时,转换 fromlongint导致结果。INT_MAX

很难正确地做到这一点,或者至少对我来说是这样。有谁知道这个的便携式解决方案?

更新:

更改static_castreinterpret_cast会导致编译器错误。一条评论提示我尝试 C 风格的转换:return (int)val在上面的代码中,它起作用了。 在这台机器上。 这在其他架构上仍然安全吗?

4

6 回答 6

14

引用 C++03 标准,§4.7/3(积分转换):

如果目标类型是有符号的,则如果它可以在目标类型(和位域宽度)中表示,则该值不变;否则,该值是实现定义的

因为结果是实现定义的,根据定义,不可能有一个真正可移植的解决方案。

于 2011-09-29T18:50:52.423 回答
8

虽然有一些方法可以使用强制转换和转换来做到这一点,但大多数都依赖于未定义的行为,这些行为恰好在某些机器/某些编译器上具有明确定义的行为。不要依赖未定义的行为,而是复制数据:

int signed_val;
std::memcpy (&signed_val, &val, sizeof(int));
return signed_val;
于 2011-09-29T18:52:41.647 回答
5

您可以通过取补加一来否定无符号二进制补码数。因此,让我们为否定做这件事:

if (val < 0x80000000) // positive values need no conversion
  return val;
if (val == 0x80000000) // Complement-and-addition will overflow, so special case this
  return -0x80000000; // aka INT_MIN
else
  return -(int)(~val + 1);

这假设您的整数用 32 位二进制补码表示(或具有相似范围)表示。它不依赖于任何与有符号整数溢出相关的未定义行为(请注意,无符号整数溢出的行为是明确定义的——尽管这也不应该发生在这里!)。

请注意,如果您的整数不是 32 位的,事情会变得更加复杂。您可能需要使用类似的东西~(~0U >> 1)而不是0x80000000. 此外,如果您的整数不是二进制补码,则某些值可能会出现溢出问题(例如,在二进制补码机器上,-0x80000000不能用 32 位有符号整数表示)。但是,今天非二进制补码机器非常罕见,因此这不太可能成为问题。

于 2011-09-29T18:30:39.983 回答
4

这是另一个对我有用的解决方案:

if (val <= INT_MAX) {
    return static_cast<int>(val);
}
else {
    int ret = static_cast<int>(val & ~INT_MIN);
    return ret | INT_MIN;
}

如果我屏蔽了高位,我会在投射时避免溢出。然后我可以安全地返回它。

于 2011-09-29T18:55:23.520 回答
2

C++20 将有std::bit_cast逐字复制位:

#include <bit>
#include <cassert>
#include <iostream>

int main()
{
    int i = -42;
    auto u = std::bit_cast<unsigned>(i);
    // Prints 4294967254 on two's compliment platforms where int is 32 bits
    std::cout << u << "\n";

    auto roundtripped = std::bit_cast<int>(u);
    assert(roundtripped == i);
    std::cout << roundtripped << "\n"; // Prints -42

    return 0;
}

cppreference 显示了一个示例bit_cast,说明如何根据memcpy(在注释下)实现自己的。

虽然 OpenVMS 不太可能很快获得 C++20 支持,但我希望这个答案可以帮助通过互联网搜索找到相同问题的人。

于 2020-02-11T17:58:41.243 回答
-1
unsigned int u = ~0U;
int s = *reinterpret_cast<int*>(&u); // -1

相反:

int s = -1;
unsigned int u = *reinterpret_cast<unsigned int*>(&s); // all ones
于 2019-07-02T09:48:24.107 回答