> 7) & 1; // save old top bit list[i] += add; // possibly add bit topBitNew = (list[i] >> 7) & 1; // save new top bit if((topBitOld == 1) && (topBitNew == 0)){ // check if overflow occurred add = 1; } else { add = 0; } list[i] &= 0xFF; } // rebuild hex string for(int i = 0; i < list.length; i++){ finalString += padHex(list[i], 2); } return finalString; } /** * This pads a number with zeros, after it has been converted to * hexidecimal format, sans 0x. * * @param number Number to be converted to hexidecimal * @param length Final length of padded string * @return String after padding */ public static String padHex(int number, int length){ char padding = '0'; String s = Integer.toHexString(number); while(s.length() < length){ s = padding + s; } return s; }" /> > 7) & 1; // save old top bit list[i] += add; // possibly add bit topBitNew = (list[i] >> 7) & 1; // save new top bit if((topBitOld == 1) && (topBitNew == 0)){ // check if overflow occurred add = 1; } else { add = 0; } list[i] &= 0xFF; } // rebuild hex string for(int i = 0; i < list.length; i++){ finalString += padHex(list[i], 2); } return finalString; } /** * This pads a number with zeros, after it has been converted to * hexidecimal format, sans 0x. * * @param number Number to be converted to hexidecimal * @param length Final length of padded string * @return String after padding */ public static String padHex(int number, int length){ char padding = '0'; String s = Integer.toHexString(number); while(s.length() < length){ s = padding + s; } return s; }"/> PenguinCode – Problems with Java'’s parseInt

Problems with Java'’s parseInt

Posted on 2010-04-01 21:41 in Blog

Background

I work in an industry that generates a lot of binary encode data. Primarily from the lab at my office, but occasionally we get data that was collected at a government test range. In order to understand the data, it goes through a process called, “Data Reduction”, which converts the binary/ hex encoded data (test data) into simple decimal numbers (engineering data) that can get used by excel. The data fields vary in size, from a byte up to a long, and it is usually unsigned data. A tool was written last spring (2009) with the help of a regional university to automate the data conversion process and it is currently being extended (2010).

Problem

The students discovered that negative numbers were not being parsed correctly. Sometimes giving an invalid value (byte: 0x80), other times they caused a NumberFormatException (int: 0xFFFFFFFF). I told them I would look into the problem. It quickly became apparent what the problem was; Integer.parseInt doesn’t under two’s complement values.

parseInt takes a hex encoded string and returns an int. In the case of negative numbers, parseInt looks for a negative sign at the beginning of the string. It does not examine the hex code within the string. When a negative byte or short was parsed, the method treated it as a positive int, and parsed accordingly. When a negative int was provided, the method threw an exception.

Cause of the Exception

The Java int has “two” fields: 31 bits of data, and 1 bit of sign. When a negative hex string was provided, parseInt saw 32 bits of data, determined it was an overflow condition, and threw the exception.

Solution

With the root cause of the problem in hand, I was quickly able to write a solution. I first wrote a routine that determined if the proved string was negative, based on its type (or size). If it was negative, I replace the original data string with its two’s complement and added a negative sign to the front. Now, parseInt returns the expected results.

Code

/** Test if string is negative  
 *  
 * @param number - hex string containing data  
 * @param bytes - number of bytes in data type (8 bit vs 16 bit, etc)  
 *  
 * @ return True means the number is negative  
 */  
public static boolean isNumberNegative(String number, int bytes){  
    if((number.length() / 2) < bytes ){ // check if top bit even has a value in it  
        return false;  
    }

    // extract first two characters into number  
    int bit = Integer.parseInt( number.substring(0,2), 16);  
    bit >>= 7; // down shift seven bits

    return (bit == 1); // top bit set == negative  
}

/**  
 * Make the hex string negative by performing two's complement  
 * @param hex the string to make negative  
 * @return input string after 2's complement reversal  
 */  
public static String makeNegativeInt(String hex){  
    String finalString = "";  
    int[] list = new int[ hex.length() / 2 ];

    // copy hex string into array  
    for(int i = 0; i < hex.length() / 2; i++){  
        list[i] = Integer.parseInt( hex.substring(i*2, i*2 +2), 16);  
    }

    // make each block negative  
    int add = 1;  
    int topBitOld = 0;  
    int topBitNew = 0;

    for(int i = list.length -1 ; i &gt;= 0; i--){  
        list[i] ^= 0xFF; // flip bits  
        topBitOld = (list[i] >> 7) & 1;             // save old top bit  
        list[i] += add;                             // possibly add bit  
        topBitNew = (list[i] >> 7) & 1;             // save new top bit  
        if((topBitOld == 1) && (topBitNew == 0)){   // check if overflow occurred  
            add = 1;  
        } else {  
            add = 0;  
        }  
        list[i] &= 0xFF;  
    }

    // rebuild hex string  
    for(int i = 0; i &lt; list.length; i++){  
        finalString += padHex(list[i], 2);  
    }

    return finalString;  
}

/**  
 * This pads a number with zeros, after it has been converted to  
 * hexidecimal format, sans 0x.  
 *  
 * @param number Number to be converted to hexidecimal  
 * @param length Final length of padded string  
 * @return String after padding  
 */  
public static String padHex(int number, int length){  
    char padding = '0';  
    String s = Integer.toHexString(number);  
    while(s.length() < length){  
        s = padding + s;  
    }  
    return s;  
}