tags:

views:

271

answers:

3

I am converting a C++ program to Java and got completely stuck in the following method which blew my mind. Would you be kind enough to explain what this method is doing?

long TSBCA::GetSignedValue(const NDataString &value)
    {
       static NDataString s;    
       s = value;

       long multiplier(1);
       size_t len(s.Len());
       if (len != 0)
       {
       if (s[0] >= (char)0xB0 && s[0] <= (char)0xB9)
       {
       s[0] &= 0x7F; //Bit Pattern: 0111 1111
       multiplier = -1;
       }
       else if (s[len - 1] >= (char)0xB0 && s[len - 1] <= (char)0xB9)
       {
       s[len - 1] &= 0x7F; //Bit Pattern: 0111 1111
       multiplier = -1;
       }
       else
       multiplier = 1;
       }
       else
       multiplier = 1;
       return s.ToLong() * multiplier;
    }

EDIT:

My initial Java version:

private long getSignedValue(final String value){

       byte[] bytes = value.getBytes();
       int length = bytes.length;
       long multiplier = 1L;

       if (bytes.length > 0){
       if (bytes[0] >= (char)0xB0 && bytes[0] <= (char)0xB9){


       bytes[0] &= 0x7F; //Bit Pattern: 0111 1111
       multiplier = -1;
       }
       else if (bytes[length - 1] >= (char)0xB0 && bytes[length - 1] <= (char)0xB9)
       {
        bytes[length - 1] &= 0x7F; //Bit Pattern: 0111 1111
       multiplier = -1;
       }
       else
       multiplier = 1;
       }
       else
       multiplier = 1;
       return Long.parseLong(Arrays.toString(bytes))* multiplier;
}

Did I do it right?

+1  A: 

It's taking a byte string (i.e. not text) and converting to it a long. It relies on many implementation specific things, and appears broken: it's extracting the sign bit from two different places. Another issue is the needless non-reentrancy (caused by the static variable).

Roger Pate
If this is code being converted, it's fair to assume that it works and is not necessarily "broken". The sign implementation may be a strange protocol that you're not expecting, but that doesn't mean it's broken, just awkward.
Tenner
This is why I said it appears broken, obviously I don't have the exact spec to which it's written.
Roger Pate
Could you please take a look at the EDIT if I have done right? Thank you.
Alex Groulx
Alex: Sorry, I'm not familiar enough with Java to know if you've run into any corner cases. At a glance, it does look right, since you copied everything down to the multiple superfluous `else multipler = 1`, but you should have someone else look at it.
Roger Pate
That's very ok. Thanks for your initial overview.
Alex Groulx
+1  A: 
s[0] &= 0x7F;

means bit-and s[0] with hex 7F or in other words, strip the sign bit off the byte value. same with s[len-1], so it:

  • takes a numerical string, where the first or the last digit has a sign bit added (0x30 - 0x39 == '0' - '9' and 0xB0 - 0xB9 is the same range with the 0x80 bit set.)
  • strips that sign bit, remembering it as multiplier
  • interprets the numerical string argument using the multiplier to set the sign
  • returns that value

Edit:

Reviewing your code leads me to the following remarks:

  • it does not work as intended, be sure to write some JUnit tests for new code to check that they do what you expect
  • place the magic numbers in separate constants
  • use byte constants when comparing to bytes (sign issues)
  • the dangling else's should get braces and in this case are unneeded
  • use new String(byte[]) to reconstruct the string, not the Arrays utility class.

This leads me to this version:

// Bit Pattern: 0111 1111
private static final int BYTE_7F = 0x7F;

// '0' with sign bit set
private static final byte BYTE_NEGATIVE_0 = (byte) 0xB0;

// '9' with sign bit set
private static final byte BYTE_NEGATIVE_9 = (byte) 0xB9;


private long getSignedValue(String value) {

 byte[] bytes = value.getBytes();
 final int length = bytes.length;
 long multiplier = 1;

 if (0 < length) {
  if (bytes[0] >= BYTE_NEGATIVE_0 && bytes[0] <= BYTE_NEGATIVE_9) {

   bytes[0] &= BYTE_7F; 
   multiplier = -1;

  } else if (bytes[length - 1] >= BYTE_NEGATIVE_0 && bytes[length - 1] <= BYTE_NEGATIVE_9) {
   bytes[length - 1] &= BYTE_7F;
   multiplier = -1;
  }
 }

 return Long.parseLong(new String(bytes)) * multiplier;
}

You still have to pay attention to adding correct comments and update the constant names to bring them in line with your documentation terminology.

rsp
Could you please take a look at the EDIT if I have done right? Thank you.
Alex Groulx
A: 

Looks like it's testing for some weird version of sign (positive or negative). If the first or last (but preferably first) character is between 0xB0 and 0xB9, then hack off the highest order bit of whichever character it was (making it between 0x30 and 0x39, the digits '0' through '9'). Then return the number with a negative sign as normal humans know it.

Tenner
Could you please take a look at the EDIT if I have done right? Thank you.
Alex Groulx