如何安全地在unsigned int和int之间进行static_cast?

时间:2011-09-29 18:25:31

标签: c++ casting integer-overflow openvms

我有一个8个字符string表示十六进制数字,我需要将其转换为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。我已经在SO上尝试了几种不同的解决方案,但还没有成功。

这就是我所知道的:

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

这是非常难以正确地做到,或者至少它已经适合我。有没有人知道便携式解决方案?

更新

static_cast更改为reinterpret_cast会导致编译错误。评论促使我在上面的代码中尝试C风格的演员:return (int)val,并且它有效。 在这台机器上。在其他架构上它仍然是安全的吗?

6 个答案:

答案 0 :(得分:10)

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

  

如果目标类型已签名,则该值如果可以在目标类型(和位字段宽度)中表示,则不会更改; 否则,该值是实现定义的

因为结果是实现定义的,所以根据定义,不可能有真正的便携式解决方案。

答案 1 :(得分:8)

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

int signed_val;
std::memcpy (signed_val, val, sizeof(int));
return signed_val;

答案 2 :(得分: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位二进制补码表示(或具有相似范围)表示。它不依赖于与有符号整数溢出相关的任何未定义行为(注意 unsigned 整数溢出的行为是明确定义的 - 尽管这不应该发生在这里!)。

请注意,如果您的整数不是32位,事情会变得更复杂。您可能需要使用~(~0U >> 1)而不是0x80000000之类的内容。此外,如果您的int不是二进制补码,则某些值可能存在溢出问题(例如,在补码机器上,-0x80000000无法用32位有符号整数表示)。然而,非二重补偿机器在今天非常罕见,所以这不太可能是一个问题。

答案 3 :(得分:4)

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

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

如果我屏蔽掉高位,我会避免在施法时溢出。然后,我可以安全地将其恢复原状。

答案 4 :(得分: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显示了example,如何根据bit_cast(在Notes中)实现自己的memcpy

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

答案 5 :(得分:-1)

unsigned int u = ~0U;
int s = *reinterpret_cast<int*>(&u); // -1

Сontrariwise:

int s = -1;
unsigned int u = *reinterpret_cast<unsigned int*>(&s); // all ones