1

Possible Duplicate:
Convert 4 bytes to int

I'm trying to pack 4 bytes into an int using some of the solutions found here, but it doesn't seem to work for one of my tests.

This is the code I'm using:

public static int pack(int c1, int c2, int c3, int c4)
{
    return (c1 << 24) | (c2 << 16) | (c3 << 8) | (c4);
}

Now when I use it on something simple like 0x34, 0x68, 0x77, and 0x23 I get what I expect: 0x34687723. But when I use it on 0xBA, 0xAD, 0xBE, and 0xEF I get something way off. Does anyone see what the problem might be?

EDIT

The above code was able to give me what I wanted, and the "wrong value" I mention below is just another way of representing 0xBAADBEEF in a decimal form.

Community
  • 1
  • 1
mighty_squash
  • 133
  • 1
  • 5
  • 17
  • 2
    It might be useful to know what _way off_ result you're actually getting, too. :) – sarnold May 24 '11 at 22:26
  • Somehow I am lost. Can you please tell me why you have that (0xFF & ...) at every byte? I'm really sorry, somehow I fail to get it at the moment... has been a long day. I'd really appreciate someone helping me out, here. – b.buchhold May 24 '11 at 22:28
  • 1
    @b.buchhold: I think it's to ensure that they are, in fact, single-byte values. Not sure why the arguments aren't just of type `byte` though. – Ry- May 24 '11 at 22:29
  • For my second example I got a value -1163018512 which is definitely not 0xBAADBEEF. – mighty_squash May 24 '11 at 22:32
  • @minitech if they were of type `byte` you'd still need to mask with `0xff` to ensure you get the correct "unsigned" equivalent integer of the signed byte value. – Alnitak May 24 '11 at 22:33
  • @Alnitak: Yes, but what I mean by that is someone could pass something like 359, 80, 1100, 662 and still expect to get a correct result. – Ry- May 24 '11 at 22:35
  • 1
    @minitech define "correct" if the input values are outside the 0 - 255 range? – Alnitak May 24 '11 at 22:41
  • @Alnitak: There is no correct. You can't pack four integers into one integer. The point of the function is to pack four bytes into an integer. – Ry- May 24 '11 at 22:42
  • @minitech: Ahhh, thanks! However, shouldn't one rather throw an IllegalArgumentEcxeption instead of truncating the values? If the arguments are no real byte values, I don't think any return value of "pack" is really valid, is it? – b.buchhold May 24 '11 at 22:42
  • @b.buchhold: That's why the argument types should be changed to `byte` - it wouldn't even compile, I don't think, with values out of the proper range. An IllegalArgumentException would be good also. I agree, masking the values is not the way to go. – Ry- May 24 '11 at 22:45

4 Answers4

9

Your code in the pack method is correct and preserves all bits but since the result is stored in a signed int you get a wrong result when you try to print it as an int or when you do arithmetic calculations or comparisons with it.

int result = pack( c1, c2, c3, c4 );
System.out.println( "result=" + Long.toHexString( result & 0xffffffffL );

This prints the int as a unsigned int.

x4u
  • 13,877
  • 6
  • 48
  • 58
7

A number stored in a Java int can only represent positive values up to 0x7fffffff (2147483647, Integer.MAX_VALUE), as it's a signed type (like all Java number types), and in the internal representation the most significant bit is used as a sign bit.

To hold positive numeric values larger than that you need to use a long instead:

public static long pack(int c1, int c2, int c3, int c4)
{
        return ((c1 << 24) | (c2 << 16) | (c3 << 8) | (c4)) & 0xffffffffL;
}

Note the explicit long mask operation at the end which is necessary to ensure that sign extension doesn't cause a negative integer result from being turned into a negative long.

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • Ok it seems that my original code gave me the correct answer, it was just weird looking before I converted it back to hexadecimal. I'm not sure why it gave a strange value, but using System.out.printf I was able to determine that the value was the same. Thanks for the help! – mighty_squash May 25 '11 at 01:00
  • @vaxquis I disagree. Note that the answer you've pointed out takes the OP's original function, but then uses _unsigned_ bit operations to promote the return value it to a `long` before printing it. Mine takes the alternate approach and returns a `long` directly. In any event, even though the grammar permits an (int) literal with the top bit set, the internal representation will still effectively be negative. Either way, it's a harsh downvote for code that does actually work as required and isn't actually "wrong". – Alnitak Jan 14 '16 at 20:48
  • you're splitting hairs, and reading the values in my answer as bit patterns rather than "numbers" which was my intent. The JVM spec itself says that the range is -2147483648 to 2147483647. If any clarification is required in my answer it's merely to insert the word "positive" in the sentence "To hold values with the next bit set ..." (which I've now done) – Alnitak Jan 14 '16 at 22:04
  • similarly in your first comment you've confused things by talking about "going _up_ to", implying a (positive) numeric range, when you yourself were talking about valid bit patterns, not numbers. Yes, _of course_ an `int` can use that _bit pattern_, but it cannot represent that _range_. – Alnitak Jan 14 '16 at 22:08
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/100768/discussion-between-vaxquis-and-alnitak). –  Jan 15 '16 at 13:25
0

Sign extension. In Java ints are signed, so 0xBA<<24 gets unhappy. I suggest that you pack into the last four bytes of a long.

long temp =
   ((0xFF & c1) << 24) | ((0xFF & c2) << 16) | ((0xFF & c3) << 8) | (0xFF   & c4);

return (int)(temp & 0x0FFFFFFFFL);
Andrew Lazarus
  • 18,205
  • 3
  • 35
  • 53
  • This does the exact same thing as the original code, only with a detour over a long variable. (Java does not get unhappy, it just creates a negative number, which is not what oexcz expects.) – Paŭlo Ebermann May 24 '11 at 23:19
-1

Are you getting a negative number? Perhaps an unsigned int or a longer int would work better.

Seth Robertson
  • 30,608
  • 7
  • 64
  • 57