3

In C#

var buffer = new byte[] {71, 20, 0, 0, 9, 0, 0, 0};

var g = (ulong) ((uint) (buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24) |
                    (long) (buffer[4] | buffer[5] << 8 | buffer[6] << 16 | buffer[7] << 24) << 32);

In C++

#define byte unsigned char
#define uint unsigned int
#define ulong unsigned long long

byte buffer[8] = {71, 20, 0, 0, 9, 0, 0, 0};

ulong g = (ulong) ((uint) (buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24) |
                    (long) (buffer[4] | buffer[5] << 8 | buffer[6] << 16 | buffer[7] << 24) << 32);

C# outputs 38654710855, C++ outputs 5199.

Why? I have been scratching my head on this for hours...

Edit: C# has the correct output.

Thanks for the help everyone :) Jack Aidley's answer was the first so I will mark that as the accepted answer. The other answers were also correct, but I can't accept multiple answers :\

6
  • Which output were you expecting? Commented Feb 2, 2013 at 12:45
  • Can you make a smaller test case? Commented Feb 2, 2013 at 12:45
  • My bad, I forgot. Question edited. Commented Feb 2, 2013 at 12:45
  • 3
    Shifting a 32-bit integer by 32 is undefined behaviour in C++. I don't know how big your long is, though. Commented Feb 2, 2013 at 12:46
  • Integral promotion may be different in the two languages. Commented Feb 2, 2013 at 12:47

4 Answers 4

6

The C++ is not working because you're casting to long which is typically 32-bits in most current C++ implementation but whose exact length is left to the implementor. You want long long.

Also, please read Bikeshedder's more complete answer below. He's quite correct that fixed size typedefs are a more reliable way of doing this.

Sign up to request clarification or add additional context in comments.

2 Comments

If I'm not mistaken, long is still 32 bits in MSVC++ while 64 bits in gcc and clang (on 64 bit platforms). So I wouldn't say it's typical. :)
I'd guess, since he's used long long in his type define he is working on a compiler where long is 32 bits. I'd also note that long is guaranteed to be 32 bits or more, while long long is guaranteed to be 64 bits or more, so long long is probably a better choice anyway.
4

The problem is that long type in C++ is still 4 byte or 32 bit(on most compilers) and thus your calculation overflows it. In C# however long is equivelent to C++'s long long and is 64 bit and so the result of the expression fits into the type.

Comments

2

Your unsigned long is not 64 bits long. You can easily check this using sizeof(unsigned long) which should return 4 (=32 bits) instead of 8 (=64 bits).

Don't use int/short/long if you expect them to be of a specific size. The standard does only say that short <= int <= long <= long long and defines a minimum size. They can actually be all the same size. long is guaranteed to be at least 32 bits and long long is guaranteed to be at least 64 bits. (See Page 22 of the C++ Standard) Still I would highly recommend against this and stick to stdint if you really want to work with a specific size.

Use <cstdint> (C++11) or <cstdint.h> (C++98) and the defined types uint8_t, uint16_t, uint32_t, uint64_t instead.

Corrected C++ code

#include <stdint.h>
#include <iostream>

int main(int argc, char *argv[]) {
    uint8_t buffer[8] = {71, 20, 0, 0, 9, 0, 0, 0};
    uint64_t g = (uint64_t) ((uint32_t) (buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24) |
                              (int64_t) (buffer[4] | buffer[5] << 8 | buffer[6] << 16 | buffer[7] << 24) << 32);
    std::cout << g << std::endl;
    return 0;
}

Demo with output: http://codepad.org/e8GOuvMp

2 Comments

No, you were right originally. The standard just says the minimum sizes (actually it gives minimum ranges, the size is implied by this). But they can all exceed that. They could all be the same size, if they are all 64 bits (or more).
@BenjaminLindley the standard defines the value ranges. According to this a long long must be at least 64 bits in size. It can be bigger though. The short <= int <= long <= long long still holds true though. I crossed out a bit too much.
1

There is a subtle error in your castings.

long in C# is a 64-bit integer. long in C++ is usually a 32-bit integer.

Thus your (long) (buffer[4] | buffer[5] << 8 | buffer[6] << 16 | buffer[7] << 24) << 32) has a different meaning when you execute it in C# or C++.

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.