10

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.

Ross
  • 14,266
  • 12
  • 60
  • 91
  • `(int)anUnsignedLong` is enough? –  Jul 08 '12 at 22:55
  • An appropriate typecast should do the trick. –  Jul 08 '12 at 22:58
  • convert it to a float to send it and then back to an unsigned long on the other end? You will loose precision, but it saves you from any strange roll-over problems. If size isn't an issue send it as a string? – tacaswell Jul 08 '12 at 22:58
  • 1
    If the id is > max, you'll get a "random" number. It's kinda hard to determine if that will be a problem for you - somewhat depends on the receiver... – MFH Jul 08 '12 at 22:58
  • @tcaswell thanks. roll-over problems are what I was worries about. Sounds good – Ross Jul 08 '12 at 23:00
  • @Ross: possible 'cause the question is pretty vague and depdends on external code - which you didn't show... – MFH Jul 08 '12 at 23:01
  • What does the protocol use the ID for? – Ben Voigt Jul 08 '12 at 23:01
  • @ MFH I am sending the value over the OSC protocol to hardware that only supports int. Your answered the question: "you'll get a "random" number" – Ross Jul 08 '12 at 23:02
  • The question is too vague to answer meaningfully. What does "convert" mean? You understand that the range of `long int` is greater than the range of `int`, meaning that in general case the value will be altered. So, how do you want to alter it? Form your question it seems that you don't really care about the resultant value. If so, then you can simply send `0` through the protocol and forget about the conversion. But of you actually care about the value sent, you have to provide more details. What do you want to obtain after the conversion? What to do with values that don't fit? – AnT stands with Russia Jul 08 '12 at 23:19
  • @AndreyT sorry, you are correct. I wish to maintain some uniqueness to the id. I hope my last edit explains this. – Ross Jul 08 '12 at 23:22
  • If the protocol doesn't need that larger range, why does the application? – Lightness Races in Orbit Oct 19 '15 at 13:03
  • This was three years ago but since you ask, the id's were being generated by a library in the application. – Ross Oct 19 '15 at 16:12

6 Answers6

16

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.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • Sorry for the vagueness. 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. – Ross Jul 08 '12 at 23:10
4

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.

Philipp
  • 48,066
  • 12
  • 84
  • 109
3

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.

Community
  • 1
  • 1
thb
  • 13,796
  • 3
  • 40
  • 68
  • Don't make exception types that don't inherit from `std::exception`. In this case you probably want `std::domain_error` anyway. – GManNickG Jul 09 '12 at 03:50
3

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.

phonetagger
  • 7,701
  • 3
  • 31
  • 55
  • Btw, it's not actually guaranteed that the bit representation is the same after that cast unsigned long -> int, even if the types are the same size. It's probably true in all useful C++ implementations, though. – Steve Jessop Jul 09 '12 at 00:27
  • @SteveJessop - No argument about that, I agree, from a purist standpoint. The C++ implementation could, in theory, represent integers in ways not amenable to direct computation on the native hardware platform, but that would be pretty inefficient, so I think in virtually all cases, it’s more an issue of the processor (CPU) and how it stores signed vs. unsigned integers. I’ve worked on many 2’s complement systems, where casting a like-sized integer between signed & unsigned does not change its binary representation, only how it’s interpreted. .... – phonetagger Jul 09 '12 at 03:10
  • ...I’ve never worked on systems with other representations (sign & magnitude, 1’s compl, ...), but I’m guessing that casting is similar; i.e. simply changes how a binary value is interpreted, since, like with 2’s compl, positive values have the same representation whether interpreted as signed or unsigned. Have you ever worked on non-2’s-compl systems? – phonetagger Jul 09 '12 at 03:10
  • no, I haven't. The only practical reason I can think for it to do anything different is on a sign-magnitude system where negative zero is a trap representation, `(unsigned long)INT_MAX+1` would have to convert to some other value, and the same for 1s' complement with `ULONG_MAX`. – Steve Jessop Jul 09 '12 at 08:44
1

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;
}
Leandros
  • 16,805
  • 9
  • 69
  • 108
0

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());
djcj
  • 79
  • 1
  • 3