[ Exercises | Chapter Index | Main Index ]

## Solution for Programming Exercise 4.2

This page contains a sample solution to one of the exercises from Introduction to Programming Using Java.

### Exercise 4.2:

The hexadecimal digits are the ordinary, base-10 digits '0' through '9' plus the letters 'A' through 'F'. In the hexadecimal system, these digits represent the values 0 through 15, respectively. Write a function named hexValue that uses a switch statement to find the hexadecimal value of a given character. The character is a parameter to the function, and its hexadecimal value is the return value of the function. You should count lower case letters 'a' through 'f' as having the same value as the corresponding upper case letters. If the parameter is not one of the legal hexadecimal digits, return -1 as the value of the function.

A hexadecimal integer is a sequence of hexadecimal digits, such as 34A7, ff8, 174204, or FADE. If str is a string containing a hexadecimal integer, then the corresponding base-10 integer can be computed as follows:

    value = 0;
    for ( i = 0; i < str.length();  i++ )
       value = value*16 + hexValue( str.charAt(i) );

Of course, this is not valid if str contains any characters that are not hexadecimal digits. Write a program that reads a string from the user. If all the characters in the string are hexadecimal digits, print out the corresponding base-10 value. If not, print out an error message.

Discussion

The subroutine has a parameter of type char and a return value of type int. It's easy to write the switch statement, although it's tedious because of the number of cases. A little creative cut-and-paste can help. The switch statement has a default case that covers all the characters that are not hexadecimal digits. For such characters, a value of -1 is returned. Note that the action in each case of the switch is to return a value. A return statement is not allowed after the -> in a case (only subroutine call statements, throw statements, and block statements are allowed). So the return statements in the example are enclosed between braces to make it into a block statement. For example,

    case 0 -> { return 0; }

The complete subroutine is shown in the program below. It would actually make more sense in this program to use a switch expression (Subsection 3.6.5) to compute the return value. Then the suboutine could be written:

    public static int hexValue(char ch) {
        return  switch(ch) {
           case '0' -> 0;
           case '1' -> 1;
           case '2' -> 2;
           case '3' -> 3;
           case '4' -> 4;
           case '5' -> 5;
           case '6' -> 6;
           case '7' -> 7;
           case '8' -> 8;
           case '9' -> 9;
           case 'A', 'a' -> 10;
           case 'B', 'b' -> 11;
           case 'C', 'c' -> 12;
           case 'D', 'd' -> 13;
           case 'E', 'e' -> 14;
           case 'F', 'f' -> 15;
           default  -> -1;
        };
    }

In the main program, I will use TextIO.getlnWord() to read the user's input, rather than TextIO.getln(). This has the advantage that it will return a non-empty string that is guaranteed not to contain any blanks. We still have the problem of checking whether the user's input contains only valid hexadecimal digits. One approach is to check all the characters first and use a boolean variable to record whether they are all valid. We can test whether an individual character, ch, is valid by checking whether hexValue(ch) is -1. Let hex be a String holding the user's input. Then we have:

    boolean valid; // Flag variable for testing whether the string is valid.
    valid = true;  // Assume that the input is valid, and change our
                   // mind if we find an invalid character.
    for ( i = 0; i < hex.length(); i++ ) {
        if ( hexValue(hex.charAt(i)) == -1 ) {  // Character number i is bad.
           valid = false;
           break; // Leave the for loop, since we are now sure of the answer.
        }
    }

    // At the point, valid is true if and only if we have examined all the
    // characters in the string and found that each one is a hex digit.

    if ( valid ) {  // If the input is valid, compute and print base-10 value
       dec = 0;
       for ( i = 0; i < hex.length(); i++ )
          dec = 16*dec + hexValue( hex.charAt(i) );
       System.out.println("Base-10 value is:  " + dec);
    }
    else {  // Input is not valid, print an error message
       System.out.println("Error:  Input is not a hexadecimal number.");
    }

This works, but we have to process the string twice. We can avoid this by checking the input at the same time that we do the conversion. If the input is illegal, I want to end the program. I use the fact that a return statement in the main() routine will end the program, since it returns control back to the system:

    dec = 0;
    for ( i = 0; i < hex.length(); i++) {
       int digit = hexValue( hex.charAt(i) );
       if (digit == -1) {
           System.out.println("Error:  Input is not a hexadecimal number.");
           return;  // Ends the main() routine.
       }
       dec = 16*dec + digit;
    }
    System.out.println("Base-10 value:  " + dec);

This is the code that is used in the main() routine of the program to do the conversion. Note that I declared dec to be of type long to allow bigger values than would fit in a variable of type int. The program still has a problem if the user enters too many characters. (It gets the wrong answer.)

It would probably be better to write a function to do the conversion of a string to base-10. It could return a -1 if the string is not a legal hexadecimal number. The main() routine could then call the function and check its return value to find out whether there was an error.

In this exercise, a special value, -1, is returned by a subroutine to indicate that its parameter value is not valid. An alternative approach would be to throw an exception if the parameter value is not legal. Instead of saying "return -1", the default case of the switch statement in the subroutine could read:

    default:
       throw new IllegalArgumentException("Not a legal hexadecimal digit: '" + ch + "'.");

The main program would then do the conversion from string to hexadecimal in a try..catch statement. If an exception occurs, the catch part of the statement can print an error message:

    try {    // IF WE WERE USING AN EXCEPTION-THROWING VERSION OF hexValue
        dec = 0;
        for ( i = 0; i < hex.length(); i++ ) {
           int digit = hexValue( hex.charAt(i) );  // Might throw an exception.
           dec = 16*dec + digit;
        }
        // We get here only if no exception occurred.
        System.out.println("Base-10 value:  " + dec);
    }
    catch ( IllegalArgumentException e ) {
       System.out.println("Error:  Input is not a hexadecimal number.");
    }

I think that this approach is much neater!

The Solution

    import textio.TextIO;

    /**
     * This program reads a hexadecimal number input by the user and prints the
     * base-10 equivalent.  If the input contains characters that are not
     * hexadecimal numbers, then an error message is printed.
     */

    public class Hex2Dec {

        public static void main(String[] args) {
           String hex;  // Input from user, containing a hexadecimal number.
           long dec;    // Decimal (base-10) equivalent of hexadecimal number.
           int i;       // A position in hex, from 0 to hex.length()-1.
           System.out.print("Enter a hexadecimal number: ");
           hex = TextIO.getlnWord();
           dec = 0;
           for ( i = 0; i < hex.length(); i++ ) {
              int digit = hexValue( hex.charAt(i) );
              if (digit == -1) {
                  System.out.println("Error:  Input is not a hexadecimal number.");
                  return;  // Ends the main() routine.
              }
              dec = 16*dec + digit;
           }
           System.out.println("Base-10 value:  " + dec);
        }  // end main

        /**
         * Returns the hexadecimal value of a given character, or -1 if it is not
         * a valid hexadecimal digit.
         * @param ch the character that is to be converted into a hexadecimal digit
         * @return the hexadecimal value of ch, or -1 if ch is not
         *     a legal hexadecimal digit
         */
        public static int hexValue(char ch) {
           switch (ch) {
              case '0' -> { return 0; }
              case '1' -> { return 1; }
              case '2' -> { return 2; }
              case '3' -> { return 3; }
              case '4' -> { return 4; }
              case '5' -> { return 5; }
              case '6' -> { return 6; }
              case '7' -> { return 7; }
              case '8' -> { return 8; }
              case '9' -> { return 9; }
              case 'A', 'a' -> { return 10; } // Note: Handle both upper and lower case.
              case 'B', 'b' -> { return 11; }
              case 'C', 'c' -> { return 12; }
              case 'D', 'd' -> { return 13; }
              case 'E', 'e' -> { return 14; }
              case 'F', 'f' -> { return 15; }
              default  -> { return -1; }
           }
        }  // end hexValue

    } // end class Hex2Dec

[ Exercises | Chapter Index | Main Index ]