2

I have a peculiar problem for which I am looking for an efficient solution. I have a byte array which contains the most significant n bytes of an unsigned 4 byte integer (most sig byte first). The value of the remaining bytes (if any) are unknown. I need to check whether the partially known integer value could fall within a certain range (+ or - x) of a known integer. It's also valid for the integer represented by the byte array under test to wrap around.

I have a solution which works (below). The problem is that this solution performs way more comparisons than I believe is necessary and a whole load of comparisons will be duplicated in the scenario in which least sig bytes are unknown. I'm pretty sure it can be done more efficiently but can't figure out how. The scenario in which least significant bytes are unknown is an edge case so I might be able to live with it but it forms part of a system which needs to have low latency so if anyone could help with this that would be great.

Thanks in advance.

static final int BYTES_IN_INT = 4;
static final int BYTE_SHIFT = 010;

// partial integer byte array length guaranteed to be 1-4 so no checking necessary
static boolean checkPartialIntegerInRange(byte[] partialIntegerBytes, int expectedValue, int range)
{
   boolean inRange = false;

   if(partialIntegerBytes.length == BYTES_IN_INT)
   {
      // normal scenario, all bytes known
      inRange = Math.abs(ByteBuffer.wrap(partialIntegerBytes).getInt() - expectedValue) <= range;
   }
   else
   {
      // we don't know least significant bytes, could have any value
      // need to check if partially known int could lie in the range
      int partialInteger = 0;
      int mask = 0;

      // build partial int and mask
      for (int i = 0; i < partialIntegerBytes.length; i++)
      {
         int shift = ((BYTES_IN_INT - 1) - i) * BYTE_SHIFT;
         // shift bytes to correct position
         partialInteger |= (partialIntegerBytes[i] << shift);

         // build up mask to mask off expected value for comparison
         mask |= (0xFF << shift);
      }

      // check partial int falls in range
      for (int i = -(range); i <= range; i++)
      {
         if (partialInteger == ((expectedValue + i) & mask))
         {
            inRange = true;
            break;
         }
      }
   }
   return inRange;
}

EDIT: Thanks to the contributors below. Here is my new solution. Comments welcome.

static final int  BYTES_IN_INT = 4;
static final int  BYTE_SHIFT = 010;
static final int  UBYTE_MASK = 0xFF;
static final long UINT_MASK = 0xFFFFFFFFl;

public static boolean checkPartialIntegerInRange(byte[] partialIntegerBytes, int expectedValue, int range)
{
  boolean inRange;

  if(partialIntegerBytes.length == BYTES_IN_INT)
  {
    inRange = Math.abs(ByteBuffer.wrap(partialIntegerBytes).getInt() - expectedValue) <= range;
  }
  else
  {
    int partialIntegerMin = 0;
    int partialIntegerMax = 0;

    for(int i=0; i < BYTES_IN_INT; i++)
    {
      int shift = ((BYTES_IN_INT - 1) - i) * BYTE_SHIFT;
      if(i < partialIntegerBytes.length)
      {
        partialIntegerMin |= (((partialIntegerBytes[i] & UBYTE_MASK) << shift));
        partialIntegerMax = partialIntegerMin;
      }
      else
      {
        partialIntegerMax |=(UBYTE_MASK << shift);
      }
    }

    long partialMinUnsigned = partialIntegerMin & UINT_MASK;
    long partialMaxUnsigned = partialIntegerMax & UINT_MASK;
    long rangeMinUnsigned = (expectedValue - range) & UINT_MASK;
    long rangeMaxUnsigned = (expectedValue + range) & UINT_MASK;

    if(rangeMinUnsigned <= rangeMaxUnsigned)
    {
      inRange = partialMinUnsigned <= rangeMaxUnsigned && partialMaxUnsigned >= rangeMinUnsigned;
    }
    else
    {
      inRange = partialMinUnsigned <= rangeMaxUnsigned || partialMaxUnsigned >= rangeMinUnsigned;
    }
  }
  return inRange;
}
PaddyD
  • 1,097
  • 1
  • 9
  • 19
  • 1
    You have two ranges; the range of possible values of your int, and the "allowable" range. Each range is defined by two endpoints. All you have to do is determine [whether these ranges intersect](http://stackoverflow.com/questions/224878/find-number-range-intersection). – Oliver Charlesworth Aug 14 '14 at 10:45
  • 2
    Couldn't you just fill the unknown bytes with `0x00` and `0xff` and use the two possible numbers as the min/max of the number it might become? Just check both of those numbers are in your range and you're done. – OldCurmudgeon Aug 14 '14 at 10:46
  • I think I need to use a combination of those two answers. @OldCurmudgeon, I want to know whether it's possible, not certain, that the unknown number lies within the range. It's possible that the min and max possible values of the unknown number both lie outside the range but that it could still have a value which lies inside the range, in which case I want my method to return true. I will use the min and max values as you suggest to determine whether there is an intersection of ranges as per the other suggestion. Thank you both. – PaddyD Aug 14 '14 at 12:10
  • The problem to solve now is dealing with integer wraparound and dealing with unsigned values in java... – PaddyD Aug 14 '14 at 12:18
  • 1
    @PaddyD with wraparound, those intervals are known as "clockwise intervals", and you can still intersect them. It's just a little trickier. I'll find it and get back to you. – harold Aug 14 '14 at 12:54

2 Answers2

1

Convert your unsigned bytes to an integer. Right-shift by 32-n (so your meaningful bytes are the min bytes). Right-shift your min/max integers by the same amount. If your shifted test value is equal to either shifted integer, it might be in the range. If it's between them, it's definitely in the range.

Presumably the sign bit on your integers is always zero (if not, just forcibly convert the negative to zero, since your test value can't be negative). But because that's only one bit, unless you were given all 32 bits as n, that shouldn't matter (it's not much of a problem in that special case).

user1676075
  • 3,056
  • 1
  • 19
  • 26
1

Suppose you have one clockwise interval (x, y) and one normal interval (low, high) (each including their endpoints), determining whether they intersect can be done as (not tested):

if (x <= y) {
    // (x, y) is a normal interval, use normal interval intersect
    return low <= y && high >= x;
}
else {
    // (x, y) wraps
    return low <= y || high >= x;
}

To compare as unsigned integers, you can use longs (cast up with x & 0xffffffffL to counteract sign-extension) or Integer.compareUnsigned (in newer versions of Java) or, if you prefer you can add/subtract/xor both operands with Integer.MIN_VALUE.

harold
  • 61,398
  • 6
  • 86
  • 164