C++ 如何安全地将 `unsigned long int` 转换为 `int`?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11387370/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 15:06:24  来源:igfitidea点击:

How can I safely convert `unsigned long int` to `int`?

c++

提问by Ross

I have an app which is creating unique idsin the form of unsigned long ints. The app needs this precision.

我有一个应用程序,它以s的形式创建唯一的IDunsigned long int。该应用程序需要这种精度。

However, I have to send these idsin a protocol that only allows for ints. The receiving application –?of the protocol –?does not need this precision. So my questions is: how can I convert an unsigned long intto an int, especially when the unsigned long intis larger than an int?

但是,我必须在只允许s的协议中发送这些idint。接收应用程序——协议的——不需要这种精度。所以我的问题是:如何将 an 转换unsigned long int为 an int,尤其是当 theunsigned long int大于 an 时int

edit:

编辑:

The protocolonly supports int. I would be good to know how to avoid "roll-over problems"

协议仅支持int. 我很高兴知道如何避免“翻车问题”

The application sending the message needs to know the uniqueness for a long period of time, whereas the receiver needs to know the uniqueness only over a short period of time.

发送消息的应用程序需要在很长一段时间内知道唯一性,而接收者只需要在很短的时间内知道唯一性。

回答by Keith Thompson

Here's one possible approach:

这是一种可能的方法:

#include <climits>
unsigned long int uid = ...;
int abbreviated_uid = uid & INT_MAX;

If intis 32 bits, for example, this discards all but the low-order 31 bits of the UID. It will only yield non-negative values.

int例如,如果是 32 位,这将丢弃除 UID 的低 31 位之外的所有位。它只会产生非负值。

This loses information from the original uid, but you indicated that that's not a problem.

这会丢失原始信息uid,但您表示这不是问题。

But your question is vague enough that it's hard to tell whether this will suit your purposes.

但是你的问题很模糊,很难判断这是否适合你的目的。

回答by thb

As you know, one cannot in theory safely convert an unsigned long intto an intin the general case. However, one can indeed do so in many practical cases of interest, in which the integer is not too large.

如您所知,在一般情况下,理论上不能安全地将 an 转换unsigned long int为 an int。然而,在许多实际感兴趣的情况下确实可以这样做,其中整数不太大。

I would probably define and use this:

我可能会定义和使用这个:

struct Exc_out_of_range {};

int make_int(const unsigned long int a) {
    const int n = static_cast<int>(a);
    const unsigned long int a2 = static_cast<unsigned long int>(n);
    if (a2 != a) throw Exc_out_of_range();
    return n;
}

An equivalent solution using the <limits>header naturally is possible, but I don't know that it is any better than the above. (If the code is in a time-critical loop and portability is not a factor, then you could code it in assembly, testing the bit or bits of interest directly, but except as an exercise in assembly language this would be a bother.)

使用<limits>标题的等效解决方案自然是可能的,但我不知道它比上面的更好。(如果代码处于时间关键循环中并且可移植性不是一个因素,那么您可以在汇编中对其进行编码,直接测试感兴趣的位,但除了作为汇编语言的练习之外,这将是一个麻烦。)

Regarding performance, it is worth noting that -- unless your compiler is very old -- the throwimposes no runtime burden unless used.

关于性能,值得注意的是——除非你的编译器很旧——throw除非使用,否则不会施加运行时负担。

@GManNickG adds the advice to inherit from std::exception. I personally don't have a strong feeling about this, but the advice is well founded and appreciated, and I see little reason not to follow it. You can read more about such inheritance here.

@GManNickG 添加了从std::exception. 我个人对此没有强烈的感觉,但该建议是有根据的并且值得赞赏,我认为没有理由不遵循它。 您可以在此处阅读有关此类继承的更多信息。

回答by Philipp

Boost has numeric_cast:

提升有numeric_cast

unsigned long l = ...;
int i = boost::numeric_cast<int>(l);

This will throw an exception if the conversion would overflow, which may or may not be what you want.

如果转换溢出,这将引发异常,这可能是您想要的,也可能不是。

回答by phonetagger

Keith Thompson's "& INT_MAX" is only necessary if you need to ensure that abbreviated_uid is non-negative. If that's not an issue, and you can tolerate negative IDs, then a simple cast (C-style or static_cast()) should suffice, with the benefit that ifsizeof(unsigned long int)==sizeof(int), then the binary representation will be the same on both ends (and if you cast it back to unsigned long inton the receiving end it will be the same value as on the sending end).

仅当您需要确保 abbreviated_uid 为非负数时,才需要 Keith Thompson 的“& INT_MAX”。如果这不是问题,并且您可以容忍负 ID,那么简单的强制转换(C 样式或 static_cast())就足够了,好处是如果sizeof(unsigned long int)==sizeof(int),那么两端的二进制表示将相同(如果您将其投射回unsigned long int接收端,它将与发送端的值相同)。

Does the receiver send responses back to the sender regarding the IDs, and does the original sender (now the receiver of the response) need to match this up with the original unsigned long intID? If so, you'll need some additional logic to match up the response with the original ID. If so, post an edit indicating such requirement and I (or others) can suggest ways of addressing that issue. One possible solution to that issue would be to break up the ID into multiple intpieces and reconstruct it into the exact same unsigned long intvalue on the other end. If you need help with that, I or someone else can help with that.

接收者是否将有关 ID 的响应发送回发送者,原始发送者(现在是响应的接收者)是否需要将其与原始unsigned long intID进行匹配?如果是这样,您将需要一些额外的逻辑来将响应与原始 ID 相匹配。如果是这样,请发布表明此类要求的编辑,我(或其他人)可以建议解决该问题的方法。该问题的一种可能解决方案是将 ID 分解为多个int部分,然后unsigned long int在另一端将其重构为完全相同的值。如果您需要帮助,我或其他人可以提供帮助。

回答by Leandros

I came along this, since I had to have a solution for converting larger integer types to smaller types, even when potentially loosing information.

我遇到了这个问题,因为我必须有一个将较大的整数类型转换为较小类型的解决方案,即使可能会丢失信息。

I came up with a pretty neat solution using templates:

我想出了一个使用模板的非常简洁的解决方案:

template<typename Tout, typename Tin>
Tout toInt(Tin in)
{
    Tout retVal = 0;

    if (in > 0)
        retVal = static_cast<Tout>(in & std::numeric_limits<Tout>::max());
    else if (in < 0)
        retVal = static_cast<Tout>(in | std::numeric_limits<Tout>::min());

    return retVal;
}

回答by djcj

You can try to use std::stringstreamand atoi():

您可以尝试使用std::stringstreamatoi()

#include <sstream>
#include <stdlib.h>
unsigned long int a = ...;
std::stringstream ss;
ss << a;
std::string str = ss.str();
int i = atoi(str.c_str());