Unsigned Shift Of Byte

last modified: April 29, 2006

Is there an Error with Unsigned Shift in Java ???

look at this:

if you make an >>> Operation to a Byte:

for (byte sourc = Byte.MIN_VALUE; sourc < Byte.MAX_VALUE; sourc++) {
        cat.info(sourc + " >>> 1 = "+ (byte)(sourc >> 1));
                        },

this seems to not really work:

look:

-126>>>1 = -63

if you make this manually, you get this:

-126 = (bin) 1000 0010
             |      | 
             |      +-------> the value 1000 0000 = -128
             +--------------> this is the minus

if you shift it RIGHT UNSIGNED, it should be:

1000 0010 >>> 1 = 0100 0001 = +65

this is clear: look:

(original Java)          should be!!

  121 >>> 1 = 60         60 (OK)
  122 >>> 1 = 61         61 (OK)
  123 >>> 1 = 61         61 (OK)
  124 >>> 1 = 62         62 (OK)
  125 >>> 1 = 62         62 (OK)
  126 >>> 1 = 63         63 (OK)
  127 >>> 1 = 63         63 (OK)
 -128 >>> 1 =-64         64 
 -127 >>> 1 =-64         64 
 -126 >>> 1 =-63         65 
 -125 >>> 1 =-63         65
 -124 >>> 1 =-62         66
 -123 >>> 1 =-62         66
 -122 >>> 1 =-61         67
 -121 >>> 1 =-61         67

now, java does the following, due to the Java Language Spec:

the >>> Operator makes a cast to int. then the result is processed, and we do a cast back to byte.

with this in Mind, we can see the truth:

(original Java)          should be!!

  121 >>> 1 = 60         60 (OK)
  122 >>> 1 = 61         61 (OK)
  123 >>> 1 = 61         61 (OK)
  124 >>> 1 = 62         62 (OK)
  125 >>> 1 = 62         62 (OK)
  126 >>> 1 = 63         63 (OK)
  127 >>> 1 = 63         63 (OK)
 -128 >>> 1 = 2147483584 64
 -127 >>> 1 = 2147483584 64 
 -126 >>> 1 = 2147483585 65 
 -125 >>> 1 = 2147483585 65
 -124 >>> 1 = 2147483586 66
 -123 >>> 1 = 2147483586 66
 -122 >>> 1 = 2147483587 67
 -121 >>> 1 = 2147483587 67

now we see: the unsigned shift Operator DOES really shift a negative Value, without care about the Signs bit, so all Negative does get positive!!!

the problem is the cast to byte!!!


this is a possible Solution:

for (source = Byte.MIN_VALUE; source < Byte.MAX_VALUE; source++) {
                cat.info(source + " >>> 1 = "+ ( (byte)(source >> 1) & Byte._0111_1111 )      );
                },

VisualC++ has similar shifting problems, requiring explicit casts to signedness and size to do it correctly. Perhaps the same is true of Java?


But this is not a problem since, according to Java specification, the byte is augmented to an int before shifting.


Yes, that's true, if you say: the Java language works correctly, because of the Specification.

You are Right!! This is not a Bug!

But, that's Useless if you need a really Unsigned Shift for a Byte!!


I thought everybody knew that A "FEATURE" IS A DOCUMENTED BUG.


Not a bug, but still a problem. The bit-fiddling code I write in Java is a mixture of occasional obscure type-promotion errors and an otherwise ridiculously defensive explicit-casting programming style. Much more so than in C, where you have unsigned types. I've recently found some very simple CommonLisp primitives for manipulating sub-bytes of integers extremely handy - it would be great to see them added to other languages. Details at http://www.lispworks.com/reference/HyperSpec/Body/12_aacb.htm -- LukeGorrie


Here's a Way to do it:

/**
 * Method unsigned.
 * 
 * does the following Mapping:
 * 
 * (byte)0  = +0   = b0000_0000 ---> 0 (int)
 * (byte)1  = +1   = b0000_0001 ---> 1 (int)
 * .
 * .
 * (byte)127= +127 = b0111_1111 ---> 127(int)
 * (byte)128= -128 = b1000_0000 ---> 128(int)
 * (byte)129= -127 = b1000_0001 ---> 129(int)
 * .
 * .
 * (byte)253= -  3 = b1111_1101 ---> 253(int)
 * (byte)254= -  2 = b1111_1110 ---> 254(int)
 * (byte)255= -  1 = b1111_1111 ---> 255(int)
 * 
 * The way how to do this is here:
 * 
 * http://forum.java.sun.com/thread.jsp?forum=4&thread=296372
 * Topic: Unsigned Byte Possible? 
 * 
 * 
 * 
 * How does this work ???
 * 
 * 
 * look:
 * 
 * +127(int) = b1111111
 * -128(int) = b11111111111111111111111110000000 & 0xFF = b10000000
 * -127(int) = b11111111111111111111111110000001 & 0xFF = b10000001
 * 
 * so, this is WORKING !!!!!!!
 * 
 * @param b
 * @return int
 */
int unsigned(byte b) {
        return ((int)b)&0xFF; //so, only the lowest Byte of the int is taken.

},

Now you can:

byte b = (byte)128;
        x = unsigned(b) >>> 1 

See ArithmeticShiftingConsideredHarmful


Loading...