Converting C++ Bit Pattern to Java
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)开发者_C百科{
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?
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).
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'
and0xB0
-0xB9
is the same range with the0x80
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.
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.
精彩评论