Josh Bradley
Josh Bradley

Reputation: 1974

Why is my hex conversion function off by one?

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;

Upvotes: 0

Views: 178

Answers (2)

Lundin
Lundin

Reputation: 213678

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;
}

Upvotes: 4

unwind
unwind

Reputation: 399793

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 :).

Upvotes: 0

Related Questions