tags:

views:

221

answers:

1

Alright, so I have 4 integers I want to wrap in a long. The 4 integers all contains 3 values, positioned in the first 2 bytes:

 +--------+--------+
 |xxpppppp|hdcsrrrr|
 +--------+--------+

{pppppp} represents one value, {hdcs} represents the second and {rrrr} the last.

I want to pack 4 of these integers, in a long. I've tried the following:

ordinal = (c1.ordinal() << (14*3) | c2.ordinal() << (14*2) | c3.ordinal() << 14 | c4.ordinal());

where c1.ordinal()...c4.ordinal() is the integers to wrap.

This does not seem to work if I run a test. Lets say I want to look up the values of the last integer in the long, c4.ordinal(), where {pppppp} = 41, {hdcs} = 8 and {rrrr} = 14, I get the following results:

System.out.println(c4.ordinal() & 0xf); //Prints 14
System.out.println(hand.ordinal() & 0xf); // Prints 14 - correct

System.out.println(c4.ordinal() >> 4 & 0xf); // Prints 8
System.out.println(hand.ordinal() >> 4 & 0xf); // Prints 8 - correct

System.out.println(c4.ordinal() >> 8 & 0x3f); // Prints 41
System.out.println(hand.ordinal() >> 8 & 0x3f); // Prints 61 - NOT correct!

Now, the following is weird to me. If I remove the first two integers, and only wrap the last two, like this:

ordinal = (c3.ordinal() << 14 | c4.ordinal());

And run the same test, I get the correct result:

System.out.println(c4.ordinal() >> 8 & 0x3f); // Prints 41
System.out.println(hand.ordinal() >> 8 & 0x3f); // Prints 41 - correct!

I have no idea whats wrong. And it does not make any sense to me, that I get the correct answer if I remove the first two integers. I'm starting to thing this might have to do with the long datatype, but I've not found anything yet, that supports this theory.

+4  A: 

Even though you are assigning the result to a long, all of the operations are performed with int values, and so the high-order bits are lost. Force "promotion" to a long by explicitly widening the values to a long.

long ordinal = (long) c1.ordinal() << (14*3) | 
               (long) c2.ordinal() << (14*2) | 
               (long) c3.ordinal() <<    14  | 
               (long) c4.ordinal();

Also, unless you are positive that the top two bits of each value are zero, you could run into other problems. You may wish to mask these off for safety's sake:

long ordinal = (c1.ordinal() & 0x3FFFL) << (14*3) | 
               (c2.ordinal() & 0x3FFFL) << (14*2) | 
               (c3.ordinal() & 0x3FFFL) <<    14  | 
               (c4.ordinal() & 0x3FFFL);
erickson
You are brilliant! Thank you :)
Frederik Wordenskjold
Is this common knowledge!? Sun doesnt really state this anywhere I've come across...
Frederik Wordenskjold
Pretty much -- and its not java-specific. When working on operators with one type and assigning to a different type, you usually need to convert the source type before any calculations...This makes more sense as a simple example, which could be java, C or C++:int x1=1;int x2=2;float y = x1/x2;y==0float z = ((float)x1)/((float)x2); z==0.5f(ack, comments don't have newlines)
CuriousPanda
Yes, it is stated in the Java Language Specification.
erickson