2

So this is what I have so far but it prints out the hexadecimal backwards. How can I switch it to be forwards or is there an easier way to convert the integer to HexDecimal than I'm doing. This is a homework assignment and I'm not allowed to use arrays or predefined methods (I.E .toHexString()).

import java.util.Scanner;

public class Main {
  public static void main(String[] args) {
    Scanner scn = new Scanner(System.in);
    int num = scn.nextInt();
    int remainder;

    while (num > 0) {

      remainder = num % 16;
      if (remainder == 10) {
        System.out.print("a");
      } else if (remainder == 11) {
        System.out.print("b");
      } else if (remainder == 12) {
        System.out.print("c");
      } else if (remainder == 13) {
        System.out.print("d");
      } else if (remainder == 14) {
        System.out.print("e");
      } else if (remainder == 15) {
        System.out.print("f");
      } else {
        System.out.print(remainder);
      }
      num = num / 16;
    }
  }
}
shmosel
  • 49,289
  • 6
  • 73
  • 138
  • 2
    Instead of printing to `System.out` on every iteration of the loop, have you considered storing the output as you loop through and doing a single print at the end? This would allow you to do last minute manipulation to what you are printing. Something to think about. – Matthew Diana Sep 23 '16 at 19:21
  • I did think about that but can't think of how to do it given that I can't use arrays and there will be anywhere between 1-8 characters in the answer. –  Sep 23 '16 at 20:10
  • I'll add a solution to expand on the point I made. The recursive solution that is already posted seems like a good route to go down as well. – Matthew Diana Sep 23 '16 at 20:19

3 Answers3

4

You can use recursion.

public static void printNum(int curr) {
    if (curr == 0) {
        return;
    }
    else {
        int divided = curr / 16;
        int remainder = curr % 16;
        printNum(divided);
        printDigit(remainder);
    }
}

// print a digit where digit is in range [0, 16)
public static void printDigit(int digit) { ... }

The idea here is that you print the largest digit last. To do this you call the same function over and over again, each time reducing the number by one digit. Every time you enter the function you check to see if the end condition is satisfied (that there are no more digits). This is called tail-first recursion. Once this condition is met you will bubble back up the chain of method calls, each time printing the next highest digit.

For example, the input 1337 would flow like this:

depth 0: divided = 1337 / 16 = 83, remainder = 9
    depth 1: divided = 83 / 16 = 5, remainder = 3
        depth 2: divided = 5 / 16 = 0, remainder = 5
            depth 3: end condition met
        print remainder = 5
    print remainder = 3
print remainder = 9

which prints 539

Community
  • 1
  • 1
flakes
  • 21,558
  • 8
  • 41
  • 88
2

If you want to go the non-recursive route, declare a String before you get into your loop and build onto it, rather than printing to System.out every loop iteration:

String output = "";
while (num > 0) {
  remainder = num % 16;
  if (remainder == 10) {
    output = "a" + output;
  } else if (remainder == 11) {
    output = "b" + output;
  } else if (remainder == 12) {
    output = "c" + output;
  } else if (remainder == 13) {
    output = "d" + output;
  } else if (remainder == 14) {
    output = "e" + output;
  } else if (remainder == 15) {
    output = "f" + output;
  } else {
    output = remainder + output;
  }
  num = num / 16;
}
System.out.println(output);
Matthew Diana
  • 1,106
  • 7
  • 14
  • Perfect! Thank you. Didn't realize that you could treat strings like that and just add on letters. –  Sep 23 '16 at 20:23
  • To be more efficient, you would want to use a StringBuilder object and append to that, then do a sb.toString() at the end. Strings are immutable so you would be creating one new String for each loop. – Mark Stewart Sep 23 '16 at 20:29
  • That is true. I just wanted to maintain his assignment's rule of not using predefined methods, so I was assuming `StringBuilder.append()` was out of the question. If that rule is only for the hex conversion itself, than by all means I would look into using a `StringBuilder`. – Matthew Diana Sep 23 '16 at 20:30
  • That's why I went straight for the recursive solution. Building up the string using concatenation or `StringBuilder` seems very similar to an approach using containers-- Although I suppose the question doesn't have enough constraints to say that for sure :) – flakes Sep 23 '16 at 20:47
0

Iterative solution (for positive integers only):

public static String toHex(int value) {
    if (value == 0)
        return "0";
    StringBuffer result = new StringBuffer();
    while (value > 0) {
        int x = value & 0b1111;
        char hex = (char) (x < 10 ? '0' + x : 'A' + x - 10);
        result.insert(0, hex);
        value >>= 4;
    }
    return result.toString();
}

Explanation:

  • Remainder of division by 16 yields the lowest hex digit. Instead of dividing by 16, you can also perform a binary bit masking (&15, which is 0b1111) to extract the lowest 4 bits.
  • To convert this number (0..15) into a hex digit (0..F), add it to the char '0' if it's lower than 10, otherwise subtract 10 and add it to 'A'.
  • After each iteration, divide by 16, or better: bit shift right by 4.
  • StringBuffer helps concatenating strings and characters. It allows to preprend by inserting at the first postion (insert at 0).
  • When the number is 0, the resulting string would be empty. Instead we return the digit "0".

This should be the most efficient solution. If you need to support negative integers as well, you need to also implement Two's complement.

By the way, never forget to test: in that case, a Monte Carlo JUnit can probe random integers and compare the framework implementation (Integer.toHexString(value).toUpperCase()) with the custom implementation:

@Test
public void testHex() {
    Random random = ThreadLocalRandom.current();
    for (int i = 0; i < 1000000; i++) {
        int value = random.nextInt();
        if (value >= 0) {
            Assert.assertEquals(Integer.toHexString(value).toUpperCase(), toHex(value));
        }
    }
}
Peter Walser
  • 15,208
  • 4
  • 51
  • 78