Ross
Ross

Reputation: 14415

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

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

However, I have to send these ids in 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 int to an int, especially when the unsigned long int is larger than an int?

edit:

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

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.

Upvotes: 10

Views: 64599

Answers (6)

djcj
djcj

Reputation: 89

You can try to use std::stringstream and atoi():

#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());

Upvotes: 0

Leandros
Leandros

Reputation: 16825

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;
}

Upvotes: 1

thb
thb

Reputation: 14434

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

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.)

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

@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.

Upvotes: 3

phonetagger
phonetagger

Reputation: 7873

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 if sizeof(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 int on the receiving end it will be the same value as on the sending end).

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 int ID? 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 int pieces and reconstruct it into the exact same unsigned long int value on the other end. If you need help with that, I or someone else can help with that.

Upvotes: 3

Philipp
Philipp

Reputation: 49802

Boost has 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.

Upvotes: 4

Keith Thompson
Keith Thompson

Reputation: 263177

Here's one possible approach:

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

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

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

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

Upvotes: 16

Related Questions