The signed Java madness: A little workaround to fake unsigned types in Java

As everyone probably knows, there is no unsigned types in the java programming language. The primitive types java provides us are:
Byte: one byte signed (-128…+127)
Short: two bytes signed (-32,768…+32,767)
Int: four bytes signed (-2147483648…+2147483647)
Long: eight bytes signed (-9223372036854775808…+9223372036854775807)

Ok, I admit short, int and long as signed values is not as bad as it seems as most of the time you would use it signed. But signed byte is something it escapes from my understanding. I read the main designers of the java language were looking for simplicity but removing the “unsigned” keyword just cripples the language in my humble opinion.

When the problem is the range is usually not a big deal if there is at least one bigger type available to cast. It is a waste of space though and sometimes that matters.

There is another problem more uncommon but it freaked me out (that’s why i am rambling here :))

Dealing with signed types would add complexity to the project
I see this problem very common if you are working with low level apis (a java wrapper of libusb, a Bluetooth library…) which returns data in form of raw bytes and handling them as signed will cause pain.

For example, let’s say we are getting a stream of bytes representing a LBA from a connected mass storage device

byte[] lbaData = usb.getLba(); // 4-bytes Little-Endian
int lba = (lbaData[3] << 24) +  (lbaData[2] << 16) + (lbaData[1] << 8) + lbaData[0];
// Example of data:
// lbaData: 0x80, 0x1F, 0x00, 0x00
// int lba = 7808
// As a unsigned int would be 8064

Using this code we are going to deal with an amazing LBA range which contains negative values and although it could be handled, it is not “natural” (addresses are always represented as positive) and adds more complexity and proneness to error. A simple cast between types does not solve anything because byte type is signed so we are stuck with the same negative values.

To solve this little but annoying problem we need use the simple solution used when range was not enough and dust off our notes from University to remember how negative numbers are represented at bit level.

  private final static long MAX_UNSIGNED_INT_VALUE = 4294967296L;

  public long intToUlong(int value)
  {
      if(value < 0)
      {
          long complement2Value = (~((long) value) + 1);
          return MAX_UNSIGNED_INT_VALUE - complement2Value;
      }else
      {
          return value;
      }
  }

  public int UlongToInt(long value)
  {
      if(value <= MAX_UNSIGNED_INT_VALUE)
      {
          if(value >= MAX_UNSIGNED_INT_VALUE / 2)
          {
              return (int) (~(MAX_UNSIGNED_INT_VALUE - value) + 1);
          }else
          {
              return (int) value;
          }
      }else
      {
          throw new IllegalArgumentException("Value out of range for a int");
      }
  }

This is a little example of two methods to fake correctly an int to a unsigned long an vice versa. Here it is the whole class with more conversions although algorithmically are the same with the exception of the max unsigned value

It seems Java 8 added support for some unsigned types and that is really nice. Unfortunately (or Fortunately) my java coding is limited to Android platform so those niceties are not yet available.

Hope you find it useful and happy coding! 🙂

Advertisements