16

Is there a function in c# that takes two 32 bit integers (int) and returns a single 64 bit one (long)?

Sounds like there should be a simple way to do this, but I couldn't find a solution.

7 Answers 7

27

Try the following

public long MakeLong(int left, int right) {
  //implicit conversion of left to a long
  long res = left;

  //shift the bits creating an empty space on the right
  // ex: 0x0000CFFF becomes 0xCFFF0000
  res = (res << 32);

  //combine the bits on the right with the previous value
  // ex: 0xCFFF0000 | 0x0000ABCD becomes 0xCFFFABCD
  res = res | (long)(uint)right; //uint first to prevent loss of signed bit

  //return the combined result
  return res;
}
Sign up to request clarification or add additional context in comments.

9 Comments

Er, shouldn't that be: long res = left; res = (res << 32) res |=right; return res; ??
I think you mean (res << 32) above.
thanks, though could you explain how this works? I'd like to understand the code if possible.
To be safe, I'd recommend using uints and ulongs... Won't get the right data otherwise XD. Especially if right is a negative number; It'll sign extend to 11111...111[right]
Using ulong alone for right will not solve the problem as casting from int to long or ulong will result in sign extension, as we are casting from a smaller source type to a destination type, and the source type is signed. The correct way to do so is to cast right as uint first. In short: return ((long) left << 32) | (long)(uint) right; There is no need for unchecked if you use in a method.
|
23

Just for clarity... While the accepted answer does appear to work correctly. All of the one liners presented do not appear to produce accurate results.

Here is a one liner that does work:

long correct = (long)left << 32 | (long)(uint)right;

Here is some code so you can test it for yourself:

long original = 1979205471486323557L;
int left = (int)(original >> 32);
int right = (int)(original & 0xffffffffL);

long correct = (long)left << 32 | (long)(uint)right;

long incorrect1 = (long)(((long)left << 32) | (long)right);
long incorrect2 = ((Int64)left << 32 | right);
long incorrect3 = (long)(left * uint.MaxValue) + right;
long incorrect4 = (long)(left * 0x100000000) + right;

Console.WriteLine(original == correct);
Console.WriteLine(original == incorrect1);
Console.WriteLine(original == incorrect2);
Console.WriteLine(original == incorrect3);
Console.WriteLine(original == incorrect4);

1 Comment

Second type cast is technically redundant. You could shorten it up by writing (long)left << 32 | (uint)right
3

Try

(long)(((long)i1 << 32) | (long)i2)

this shifts the first int left by 32 bits (the length of an int), then ors in the second int, so you end up with the two ints concatentated together in a long.

Comments

2

Be careful with the sign bit. Here is a fast ulong solution, that is also not portable from little endian to big endian:

    var a = 123;
    var b = -123;

    unsafe
    {
        ulong result = *(uint*)&a;

        result <<= 32;
        result |= *(uint*)&b;
    }

Comments

0

This should do the trick

((Int64) a << 32 | b)

Where a and b are Int32. Although you might want to check what happens with the highest bits. Or just put it inside an "unchecked {...}" block.

Comments

0

Gotta be careful with bit twiddling like this though cause you'll have issues on little endian/big endian machines (exp Mono platforms aren't always little endian). Plus you have to deal with sign extending. Mathematically the following is the same but deals with sign extension and is platform agnostic.

return (long)( high * uint.MaxValue ) + low;

When jitted at runtime it will result in performance similar to the bit twiddling. That's one of the nice things about interpreted languages.

1 Comment

Actually, you need to multiply by uint.MaxValue + 1, i.e 0x100000000, not 0x11111111
0

There is a problem when i2 < 0 - high 32 bits will be set (0xFFFFFFFF,1xxx... binary) - thecoop was wrong
Better would be something like (Int64)(((UInt64)i1 << 32) | (UInt32)i2)

Or simply C++ way

public static unsafe UInt64 MakeLong(UInt32 low, UInt32 high)
{
    UInt64 retVal;
    UInt32* ptr = (UInt32*)&retVal;
    *ptr++ = low;
    *ptr = high;
    return retVal;
}

UInt64 retVal;
unsafe
{
    UInt32* ptr = (UInt32*)&retVal;
    *ptr++ = low;
    *ptr = high;
}

But the best solution found then here ;-)
[StructLayout(LayoutKind.Explicit)]
[FieldOffset()]
https://stackoverflow.com/questions/12898591
(even w/o unsafe) Anyway FieldOffset works for each item, so you have to specify position of each half separate and remember negative #s are zero complements, so ex. low <0 and high >0 will not make sense - for example -1,0 will give Int64 as 4294967295 probably.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.