Thread: Converting hexidecimal digits to integer value

  1. #1
    SnoopFrogg KingFlippyNips's Avatar
    Join Date
    Sep 2016
    Posts
    33

    Converting hexidecimal digits to integer value

    I am working on K&R's Exercise 2-3. The problem is as followed:

    Write a function htoi(s), which converts a string of hexidecimal digits (including an optional 0x or 0X) into its equivalent integer value. The allowable digits are 0 through 9, a through f, and A through F.

    I have written the code (skipping the optional 0x or 0X) for this function and I'm pretty sure I'm correct, but I want someone to double check my work and let me know if there is anything I can improve or work on. Thank you in advance!

    Here is my code:

    Code:
    int htoi(char s[]){
    
    
        int i, n;
        i = 0;
    
        n = 0; // integer value to be returned
        for(i = 0; s[i] >= '0' && s[i] <= '9'; ++i){
    
    
            n = 16 * n + (s[i] - '0');
    
    
            if(s[i] >= 'a' && s[i] <= 'f')
                n = 16 * n + (s[i] - 'a');
            else if(s[i] >= 'A' && s[i] <= 'F')
                n = 16 * n + (s[i] - 'A');
            else
               return 0; // not a valid hex digit
        }
        return n;
    }

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    27,183
    Have you tested it with the input string "a"? Does it return an integer value of 10?
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    SnoopFrogg KingFlippyNips's Avatar
    Join Date
    Sep 2016
    Posts
    33
    Quote Originally Posted by laserlight View Post
    Have you tested it with the input string "a"? Does it return an integer value of 10?
    It returned an integer value of 0 when I tested it.

  4. #4
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    27,183
    That's good, so now you know that you are mistaken when you wrote "I'm pretty sure I'm correct". Take a second look at the loop condition and go through the process of looping with the input of "a".
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  5. #5
    SnoopFrogg KingFlippyNips's Avatar
    Join Date
    Sep 2016
    Posts
    33
    I see where I went wrong...

    I forgot to add 10 in
    Code:
    n = 16 * n + (s[i] - 'a');
    Same mistake on line 17. I have rewritten my code.
    Here's what it looks like now:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    
    int htoi(char s[]){
     
        int i, n;
        i = 0;
     
        n = 0; // integer value to be returned
        
        if(s[i] >= '0' && s[i] <= '9')
            return n = 16 * n + (s[i] - '0');
        else if(s[i] >= 'a' && s[i] <= 'f')
            return n = 16 * n + (s[i] - 'a') + 10;
        else if(s[i] >= 'A' && s[i] <= 'F')
            return n = 16 * n + (s[i] - 'A') + 10;
    }
    
    
    int main(int argc, char *argv[]) {
        
        char test[10];
        int a;
        
        printf("Enter a hexidecimal string to be converted:  ");
        scanf("%s", test);
        
        a = htoi(test);
        printf("\nThe integer value is: %d\n ", a);
        return 0;
    }

  6. #6
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    27,183
    What happened to your loop?
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  7. #7
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    27,183
    If I were you, I would write a main function like this:
    Code:
    int main(void) {
        printf("%s\n", (htoi("0") == 0) ? "pass" : "fail");
        printf("%s\n", (htoi("1") == 1) ? "pass" : "fail");
        printf("%s\n", (htoi("9") == 9) ? "pass" : "fail");
        printf("%s\n", (htoi("a") == 10) ? "pass" : "fail");
        printf("%s\n", (htoi("f") == 15) ? "pass" : "fail");
        printf("%s\n", (htoi("A") == 10) ? "pass" : "fail");
        printf("%s\n", (htoi("F") == 15) ? "pass" : "fail");
        printf("%s\n", (htoi("10") == 16) ? "pass" : "fail");
        printf("%s\n", (htoi("1d") == 29) ? "pass" : "fail");
        printf("%s\n", (htoi("e1") == 225) ? "pass" : "fail");
        printf("%s\n", (htoi("abc") == 2748) ? "pass" : "fail");
        printf("%s\n", (htoi("a2b") == 2603) ? "pass" : "fail");
        printf("%s\n", (htoi("5C7") == 1479) ? "pass" : "fail");
        printf("%s\n", (htoi("a1b2") == 41394) ? "pass" : "fail");
        return 0;
    }
    Every time I make a change to my implementation of htoi and feel that I've got it right, I would compile and run the program. If all of the lines of output read "pass", then I know that chances are, I have implemented it correctly, and so I can modify the tests and move on to implement for the optional "0x" and "0X" prefixes. If any of the lines read "fail", or the program crashes, then I know I have a bug to fix.
    Last edited by laserlight; 10-08-2017 at 08:26 PM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  8. #8
    SnoopFrogg KingFlippyNips's Avatar
    Join Date
    Sep 2016
    Posts
    33
    Quote Originally Posted by laserlight View Post
    What happened to your loop?
    When I ran the original program with the for loop I kept getting 0 as the return value. I went back an looked at the book and reread the example of char to int conversions using the function lower.

    Code:
    int lower(int c){
    if (c >= 'A' && c<= 'Z')
        return c + 'a' - 'A';
    else
       return c;
    I rewrote the function to reflect this example and was able to achieve the return value of 10 for 'a'. Was that incorrect?

  9. #9
    SnoopFrogg KingFlippyNips's Avatar
    Join Date
    Sep 2016
    Posts
    33
    It passes every test up to "F" and fails for the rest. Why is that?

  10. #10
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    27,183
    Well, let's take your code from post #1, combine it with my main function from post #7, compile and run:
    Code:
    pass
    fail
    fail
    fail
    fail
    fail
    fail
    fail
    fail
    fail
    fail
    fail
    fail
    fail
    So we see that only the very first test passed. This initially surprised me because I thought it should pass for a few more, but on closer investigation I can see why: in the body of the loop, you always check to see if the digit is a valid alphabetic hex digit, and if it isn't you return 0. Since '1' is not a valid alphabetic hex digit, you return 0 for "1" despite correctly computing n == 1. Note that this is in addition to what I pointed out in post #4.

    Now, let's take your code from post #5, combine it with my main function from post #7, compile and run:
    Code:
    pass
    pass
    pass
    pass
    pass
    pass
    pass
    fail
    fail
    fail
    fail
    fail
    fail
    fail
    Notice that all the tests with more than one hex digit fail. This is a direct result of your removal of the loop: since you are no longer looping, you only process one hex digit.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  11. #11
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    27,183
    My main function in post #7 is a very simple example of how you might write unit tests to test your function. You don't get much information out of running the tests other than whether they passed or failed. Here's a slightly more sophisticated version:
    Code:
    void test_htoi(char input[], int expected_output) {
        int actual_output = htoi(input);
        if (actual_output == expected_output) {
            printf("htoi(\"%s\") passed\n", input);
        } else {
            printf("htoi(\"%s\") failed: expected=%d; actual=%d \n",
                   input, expected_output,
                   actual_output);
        }
    }
    
    int main(void) {
        test_htoi("0", 0);
        test_htoi("1", 1);
        test_htoi("9", 9);
        test_htoi("a", 10);
        test_htoi("f", 15);
        test_htoi("A", 10);
        test_htoi("F", 15);
        test_htoi("10", 16);
        test_htoi("1d", 29);
        test_htoi("e1", 225);
        test_htoi("abc", 2748);
        test_htoi("a2b", 2603);
        test_htoi("5C7", 1479);
        test_htoi("a1b2", 41394);
        return 0;
    }
    If we run this with your code from post #1, we get:
    Code:
    htoi("0") passed
    htoi("1") failed: expected=1; actual=0
    htoi("9") failed: expected=9; actual=0
    htoi("a") failed: expected=10; actual=0
    htoi("f") failed: expected=15; actual=0
    htoi("A") failed: expected=10; actual=0
    htoi("F") failed: expected=15; actual=0
    htoi("10") failed: expected=16; actual=0
    htoi("1d") failed: expected=29; actual=0
    htoi("e1") failed: expected=225; actual=0
    htoi("abc") failed: expected=2748; actual=0
    htoi("a2b") failed: expected=2603; actual=0
    htoi("5C7") failed: expected=1479; actual=0
    htoi("a1b2") failed: expected=41394; actual=0
    and now you can see clearly that the failures are due to the actual value always being 0, which gives you a hint that you are always returning 0.

    With your code from post #5:
    Code:
    htoi("0") passed
    htoi("1") passed
    htoi("9") passed
    htoi("a") passed
    htoi("f") passed
    htoi("A") passed
    htoi("F") passed
    htoi("10") failed: expected=16; actual=1
    htoi("1d") failed: expected=29; actual=1
    htoi("e1") failed: expected=225; actual=14
    htoi("abc") failed: expected=2748; actual=10
    htoi("a2b") failed: expected=2603; actual=10
    htoi("5C7") failed: expected=1479; actual=5
    htoi("a1b2") failed: expected=41394; actual=10
    You can see that the actual values are fairly low compared to the expected values, so it is a hint that you are not handling enough of the input string. This is confirmed when you look a bit more closely and see that the actual values correspond to the first hex digit of each of the input strings.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  12. #12
    SnoopFrogg KingFlippyNips's Avatar
    Join Date
    Sep 2016
    Posts
    33
    I didn't forget about your last response! I'm a bit busy today but I think I know how to solve my problem. I will be working on it later today and I'll get back to you. Thanks again!

  13. #13
    SnoopFrogg KingFlippyNips's Avatar
    Join Date
    Sep 2016
    Posts
    33
    Sorry for the really late reply... I have been pretty busy with some Calc 2 homework. I had a quiz and upcoming exam I have been studying for

    So, I was able to solve the problem. What I did in the for loop was check to see if s[i] is not a null terminator. Then within if-else block I checked to see if the values were digits 0-9, a-f, A-F and stored the value to the corresponding hex digit to a variable called hexDigit. Finally, I converted the hex value to an integer.

    For some reason, if I tried to use my original for loop my program would not pass for the first couple inputs (0, 1, 9, a, f, A, F). I'm not sure why that is though?

    My code is as follows:
    Code:
    int htoi(char s[]){
        
        int hexDigit, i = 0, n;
            
        n = 0;// integer to be returned    
        for(; s[i] != '\0'; ++i){
            
            if(s[i] >= '0' && s[i] <= '9')
                hexDigit = s[i] - '0';
            else if(s[i] >= 'a' && s[i] <= 'f')
                hexDigit = s[i] - 'a' + 10;
            else if(s[i] >= 'A' && s[i] <= 'F')
                hexDigit = s[i] - 'A' + 10;
            else
                s[i] = 0;
            
            n = 16 * n + hexDigit;
        }
        return n;
    }
    
    
    int main(void) {
        printf("%s\n", (htoi("0") == 0) ? "pass" : "fail");
        printf("%s\n", (htoi("1") == 1) ? "pass" : "fail");
        printf("%s\n", (htoi("9") == 9) ? "pass" : "fail");
        printf("%s\n", (htoi("a") == 10) ? "pass" : "fail");
        printf("%s\n", (htoi("f") == 15) ? "pass" : "fail");
        printf("%s\n", (htoi("A") == 10) ? "pass" : "fail");
        printf("%s\n", (htoi("F") == 15) ? "pass" : "fail");
        printf("%s\n", (htoi("10") == 16) ? "pass" : "fail");
        printf("%s\n", (htoi("1d") == 29) ? "pass" : "fail");
        printf("%s\n", (htoi("e1") == 225) ? "pass" : "fail");
        printf("%s\n", (htoi("abc") == 2748) ? "pass" : "fail");
        printf("%s\n", (htoi("a2b") == 2603) ? "pass" : "fail");
        printf("%s\n", (htoi("5C7") == 1479) ? "pass" : "fail");
        printf("%s\n", (htoi("a1b2") == 41394) ? "pass" : "fail");
        return 0;
    }

  14. #14
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    27,183
    Quote Originally Posted by KingFlippyNips
    So, I was able to solve the problem. What I did in the for loop was check to see if s[i] is not a null terminator. Then within if-else block I checked to see if the values were digits 0-9, a-f, A-F and stored the value to the corresponding hex digit to a variable called hexDigit. Finally, I converted the hex value to an integer.
    Yes, that is the approach that I would have taken

    Quote Originally Posted by KingFlippyNips
    For some reason, if I tried to use my original for loop my program would not pass for the first couple inputs (0, 1, 9, a, f, A, F). I'm not sure why that is though?
    Let's take another look at the condition of your original for loop:
    Code:
    for(i = 0; s[i] >= '0' && s[i] <= '9'; ++i){
    What it says is this: loop from the first character until a character that is not in the range '0' to '9' is found. This works great if your input consists entirely of digits from '0' to '9', e.g., "123" as you would loop over the entire input string. But suppose your input consists of the non-decimal range of hexadecimal digits, e.g., "ABC". Your loop will stop immediately because 'A' is a character that is not in the range '0' to '9'. Therefore the result is 0, which is obviously wrong.

    Compounding this problem, within the loop body, you check to see if the character is a valid hex digit in the sense of whether it is a digit that is among the alphabetic hexadecimal digits 'A' to 'F'. Of course, knowing that within the loop body the character is surely in the range '0' to '9', it follows that it is surely "not a valid hex digit", therefore 0 is returned. Consequently, your function always returns 0, because either the character is not in the range of '0' to 9' hence 0 is returned, or it is in the range of '0' to '9' but not in the range of 'A' to 'F' (which is always true), hence 0 is returned.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  15. #15
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,611
    There is also an implementation with a big string - "0123456789ABCDEF" - where a subscript matches the given place value; it's portable across character sets at least.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Converting Digits to Words Help!
    By McZiploc in forum C Programming
    Replies: 6
    Last Post: 02-16-2013, 08:30 PM
  2. Sum of digits of an integer: Odd or Even?
    By Devolution in forum C Programming
    Replies: 14
    Last Post: 03-06-2009, 06:24 PM
  3. converting digits into words
    By nynicue in forum C Programming
    Replies: 12
    Last Post: 11-02-2008, 06:10 AM
  4. Replies: 5
    Last Post: 06-12-2007, 02:18 PM
  5. Replies: 1
    Last Post: 03-26-2006, 03:44 PM

Tags for this Thread