0

I'm trying to learn c and am confused why my hex to int conversion function returns a value that is off by one.

Note: 0XAAAA == 46390

#include <stdio.h>
#include <math.h>

unsigned int hex_to_int(char hex[4]);

int main()
{
    char hex[4] = "AAAA";

    unsigned int result = hex_to_int(hex);
    printf("%d 0X%X\n", result, result);
    return 0;
}    

unsigned int hex_to_int(char input[4])
{
    unsigned int sum, i, exponent;

    for(i = 0, exponent = 3; i < 4; i++, exponent--) {
        unsigned int n = (int)input[i] - 55;
        n *= pow(16, exponent);
        sum += n;
    }   
    return sum;
}

Output:

46391 0XAAAB

Update: I now realize "- 55" is ridiculous, I was going off memory from seeing this:

if (input[i] >= '0' && input[i] <= '9')
    val = input[i] - 48;
else if (input[i] >= 'a' && input[i] <= 'f')
    val = input[i] - 87;
else if (input[i] >= 'A' && input[i] <= 'F')
    val = input[i] - 55;
moooeeeep
  • 31,622
  • 22
  • 98
  • 187
Josh Bradley
  • 1,854
  • 1
  • 15
  • 31

2 Answers2

4

You have several bugs such as the string not getting null terminated, and the ASCII to decimal conversion being nonsense (value 55?), you don't initialize sum and so on. Just do this instead:

#include <stdio.h>
#include <stdlib.h>

int main()
{
  char x[] = "AAAA";

  unsigned int sum = strtoul(x, NULL, 16);
  printf("%d 0X%X\n", sum, sum);
  return 0;
}    

EDIT

If you insist on doing this manually:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

unsigned int hexstr_to_uint(const char* str);

int main()
{
  char x[] = "AAAA";

  unsigned int sum = hexstr_to_uint (x);
  printf("%d 0X%X\n", sum, sum);
  return 0;
}    

unsigned int hexstr_to_uint(const char* str)
{
  unsigned int sum = 0;

  for(; *str != '\0'; str++)
  {
    sum *= 16;
    if(isdigit(*str))
    {
      sum += *str - '0';
    }
    else
    {
      char digit = toupper(*str);
      _Static_assert('Z'-'A'==25, "Trash systems not supported.");
      if(digit >= 'A' && digit <= 'F')
      {
        sum += digit - 'A' + 0xA;
      }
    }
  }
  return sum;
}
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Still doesn't answer how the logic can be corrected to achieve the requirement. – CinCout Dec 15 '17 at 09:04
  • There it is, thank you! Not sure where I got 55 from, just shooting from the hip really. It worked for single character hex values. – Josh Bradley Dec 15 '17 at 09:08
  • 2
    @JoshuaBradley Well, it works for upper case digits above 9. This is 6 out of 22 possible characters. – Gerhardh Dec 15 '17 at 09:13
  • Maybe you should mention the UB in OP's code and make this a complete answer. I will delete mine then. – CinCout Dec 15 '17 at 09:18
  • Also, thank you for the char pointer example, I had been wondering about that. It's worlds better than hard coded limits as @CinCout pointed out. – Josh Bradley Dec 15 '17 at 09:38
0

You're just making up logic, there isn't a single value you can subtract from a hexadecimal digit character to convert it into the corresponding number.

If you want to be portable, all that C requires is that the symbols 0 through 9 are consecutive in their encoding. There's no such guarantee for the letters A through F.

Also involving pow() which is a double-precision floating point function in this low-level integer work, is a bit jarring. The typical way to do this is by multiplication or bitwise shifting.

If you're hell-bent on doing the conversion yourself, I usually do something like this:

unsigned int hex2int(const char *a)
{
  unsigned int v = 0;
  while(isxdigit((unsigned int) *a))
  {
    v *= 16;
    if(isdigit((unsigned int) *a))
      v += *a - '0';
    else
    {
      const char highs[] = "abcdef";
      const char * const h = strchr(highs, tolower(*a));
      v += 10 + (unsigned int) (h - highs);
    }
    ++a;
  }
  return v;
}

The above is a bit verbose, you can for instance fold the decimal digits into the string used for the letters too, I just tried to be clear. The above should work for any valid C character set encoding, not just ASCII (and it's less passive-aggressive than @Lundin's code, hih :).

unwind
  • 391,730
  • 64
  • 469
  • 606