Convert hex string (char []) to int?
I have a char[] that contains a value such as «0x1800785» but the function I want to give the value to requires an int, how can I convert this to an int? I have searched around but cannot find an answer. Thanks.
16 Answers 16
const char *hexstring = "abcdef0"; int number = (int)strtol(hexstring, NULL, 16);
In case the string representation of the number begins with a 0x prefix, one must should use 0 as base:
const char *hexstring = "0xabcdef0"; int number = (int)strtol(hexstring, NULL, 0);
(It’s as well possible to specify an explicit base such as 16, but I wouldn’t recommend introducing redundancy.)
If the hexstring is always introduced by a «0x» as given in the question you should just use 0 instead of 16 .
As a side note strtol gives you type long which is great when dealing with a very large hex. Use strtoll for type long long for even larger hexes.
Noticed a surprising discrepancy between «begins with a 0x prefix, one must use 0 as base» and C11 §7.22.1.4 «If the value of base is 16, the characters 0x or 0X may optionally precede the sequence of letters and digits» as in printf(«%ld\n», strtol(«0x12», 0, 16)); . You may wish to review this.
@chux Changed «must» to «should», as it’s not obligatory, but better practice, to let the function detect the base (it eliminates a tiny bit of redundancy).
Technically, you should use strtoul instead of strtol, because it converts it to unsigned, which means you won’t get negative outputs from 8-digit hex values. This stumped me for quite awhile! See en.cppreference.com/w/c/string/byte/strtoul
Or if you want to have your own implementation, I wrote this quick function as an example:
/** * hex2int * take a hex string and convert it to a 32bit number (max 8 hex digits) */ uint32_t hex2int(char *hex) < uint32_t val = 0; while (*hex) < // get current character then increment uint8_t byte = *hex++; // transform hex character to the 4bit equivalent number, using the ascii table indexes if (byte >= '0' && byte = 'a' && byte <='f') byte = byte - 'a' + 10; else if (byte >= 'A' && byte <='F') byte = byte - 'A' + 10; // shift 4 to make space for new digit, and add the 4 bits of the new digit val = (val << 4) | (byte & 0xF); >return val; >
Something like this could be useful:
char str[] = "0x1800785"; int num; sscanf(str, "%x", &num); printf("0x%x %i\n", num, num);
This causes undefined behaviour, %x must receive the address of an unsigned int . And even if you fix that, if the number represented by the string is larger than UINT_MAX then it is undefined behaviour again
Use strtol if you have libc available like the top answer suggests. However if you like custom stuff or are on a microcontroller without libc or so, you may want a slightly optimized version without complex branching.
#include /** * xtou64 * Take a hex string and convert it to a 64bit number (max 16 hex digits). * The string must only contain digits and valid hex characters. */ uint64_t xtou64(const char *str) < uint64_t res = 0; char c; while ((c = *str++)) < char v = (c & 0xF) + (c >> 6) | ((c >> 3) & 0x8); res = (res return res; >
The bit shifting magic boils down to: Just use the last 4 bits, but if it is an non digit, then also add 9.
If you get a chance could you elaborate a bit more on how the «v =» line works? I managed to implement your function for my own purposes as follows: unsigned long long int hextou64(char *str) < unsigned long long int n = 0; char c, v; for (unsigned char i = 0; i < 16; i++) < c = *(str + i); v = (c & 0xF) + (c >> 6) | ((c >> 3) & 0x8); n = (n << 4) | (unsigned long long int)v; >return n; ><>
Assuming you mean it’s a string, how about strtol?
I’d originally linked to strtod (-> double) instead of strtol. I guess someone saw it while I was editing.
Well, that’s not much of a mistake. the compiler auto-casts the return value if fed to an int . +1, btw.
I don’t think a double can store all possible values a 32-bit integer can (actually, does anyone know if this is true? I’m not 100% on floating point number representation.)
Detail, «A 64-bit IEEE double has integral-accuracy to about 2^53.» and a sign bit. So it can handle int54_t and uint53_t .
One quick & dirty solution:
// makes a number from two ascii hexa characters int ahex2int(char a, char b)
You have to be sure your input is correct, no validation included (one could say it is C). Good thing it is quite compact, it works with both ‘A’ to ‘F’ and ‘a’ to ‘f’.
The approach relies on the position of alphabet characters in the ASCII table, let’s peek e.g. to Wikipedia (https://en.wikipedia.org/wiki/ASCII#/media/File:USASCII_code_chart.png). Long story short, the numbers are below the characters, so the numeric characters (0 to 9) are easily converted by subtracting the code for zero. The alphabetic characters (A to F) are read by zeroing other than last three bits (effectively making it work with either upper- or lowercase), subtracting one (because after the bit masking, the alphabet starts on position one) and adding ten (because A to F represent 10th to 15th value in hexadecimal code). Finally, we need to combine the two digits that form the lower and upper nibble of the encoded number.
Here we go with same approach (with minor variations):
#include // takes a null-terminated string of hexa characters and tries to // convert it to numbers long ahex2num(unsigned char *in) < unsigned char *pin = in; // lets use pointer to loop through the string long out = 0; // here we accumulate the result while(*pin != 0)< out return out; > // main function will test our conversion fn int main(void) < unsigned char str[] = "1800785"; // no 0x prefix, please long num; num = ahex2num(str); // call the function printf("Input: %s\n",str); // print input string printf("Output: %x\n",num); // print the converted number back as hexa printf("Check: %ld = %ld \n",num,0x1800785); // check the numeric values matches return 0; >
Hexadecimal to integer conversion function
I am working through Kernighan and Ritchie to brush up on my C skills, so I thought I would invite critiques of my code. Here is my Hex-to-int function:
int htoi(char *str, unsigned long long *result) < int c; int i; int msnibble; int digits; const int nibbles[] = <'\x0','\x1','\x2','\x3', \ '\x4','\x5','\x6','\x7', \ '\x8','\x9','\xA','\xB', \ '\xC','\xD','\xE','\xF'>; if (NULL == result) < printf("Invalid pointer\n"); return -1; >if ('x' == str[1] || 'X' == str[1]) < msnibble = 2; digits = strlen(str) - 2; >else < msnibble = 0; digits = strlen(str); >if (digits > 16) < printf("Too many hex digits\n"); return -1; >for (i=0, *result = 0; '\0' != (c = str[i+msnibble]); i++) < if ( c >= 'a' && c else if (c >= 'A' && c else if (c >= '0' && c else < printf("Non Hex Character Detected; Exiting\n"); return -1; >*result = *result return 0; >
I also tried a solution using the math library. Using pow, I would get correct output until I passed 0xffffffffffffffff and then it would be one greater than the correct answer. This is why I settled on using the array of nibbles. Edit 1:
int htoi(const char *str, unsigned long long *result) < int c; int i; int msnibble; int digits; if (NULL == result) < printf("Invalid pointer\n"); return -1; >if ('0' == str[0]) < if ('x' == str[1] || 'X' == str[1]) < msnibble = 2; digits = strlen(str) - 2; >else < msnibble = 0; digits = strlen(str); >> if (digits > 16) < printf("Too many hex digits\n"); return -1; >else if (0 == digits) return -1; for (i=0, *result = 0; '\0' != (c = str[i+msnibble]); i++) < if ( c >= 'a' && c else if (c >= 'A' && c else if (c >= '0' && c else < printf("Non hex character detected; Exiting\n"); return -1; >*result = *result return 0; >
#include #include #include enum status < NOINPUT = -1, INVPTR = -2, TOOBIG = -3, NOHEX = -4, NONHEX = -5>; int htoi(const char *str, unsigned long long *result); int main(int argc, char *argv[]) < unsigned long long result; if (argc if (0 > htoi(argv[1],&result)) return EXIT_FAILURE; else printf("Value = %llu, strtol says %lu\n", result, strtol(argv[1],NULL,16)); return EXIT_SUCCESS; > int htoi(const char *str, unsigned long long *result) < int c; int i; int msnibble = 0; int digits = 0; if (NULL == result) < printf("Invalid pointer\n"); return INVPTR; >if ('0' == str[0] && 'x' == str[1] || 'X' == str[1]) < msnibble = 2; digits = strlen(str) - 2; >else < msnibble = 0; digits = strlen(str); >if (digits > 16) < printf("Too many hex digits\n"); return TOOBIG; >else if (0 == digits) < printf("Nothing to do\n"); return NOHEX; >for (i=0, *result = 0; '\0' != (c = str[i+msnibble]); i++) < if ( c >= 'a' && c = 'A' && c = '0' && c *result = *result return EXIT_SUCCESS; >
Edit 3: I decided to simplify the error reporting and get rid of the printf s. I also fixed the precedence error. I am omitting the braces on the if s because I am following this. I am trying to follow the kernel style. Next time, I will include that with my post. I apologize for any confusion. I also got rid of i and msnibble as the pointer arithmetic is much simpler.
#include #include #include int htoi(const char *str, unsigned long *result); int main(int argc, char *argv[]) < unsigned long result; if (argc if (0 > htoi(argv[1],&result)) return EXIT_FAILURE; else printf("Value = %llu, strtol says %lu\n", result, strtol(argv[1],NULL,16)); return EXIT_SUCCESS; > int htoi(const char *str, unsigned long *result) < int c; int digits; if ('0' == str[0] && ('x' == str[1] || 'X' == str[1])) str += 2; digits = strlen(str); if (digits >sizeof(long)*2 || 0 == digits) return -1; for (*result = 0; '\0' != (c = *str); str++) < if ( c >= 'a' && c = 'A' && c = '0' && c return 0; >
#include #include #include int htoi(const char *s, unsigned long *res); int main(int argc, char *argv[]) < unsigned long res; if (argc if (0 > htoi(argv[1],&res)) return EXIT_FAILURE; else printf("Value = %llu, strtol says %lu\n", res, strtol(argv[1],NULL,16)); return EXIT_SUCCESS; > int htoi(const char *s, unsigned long *res) < int n; int digits; if ('0' == s[0] && ('x' == s[1] || 'X' == s[1])) s += 2; for (*res = 0; '\0' != *s; s++) < n = 0; if ( *s >= 'a' && *s else if (*s >= 'A' && *s else if (*s >= '0' && *s else < errno = EINVAL; return -1; >if (*res > (ULONG_MAX/16)) < errno = ERANGE; return -1; >*res *= 16; *res += (unsigned long) n; > return 0; >
#include #include #include int htoi(const char *s, unsigned long *res); int main(int argc, char *argv[]) < unsigned long res; if (argc if (0 > htoi(argv[1],&res)) return EXIT_FAILURE; else printf("Value = %llu, strtol says %lu\n", res, strtol(argv[1],NULL,16)); return EXIT_SUCCESS; > int htoi(const char *s, unsigned long *res) < if ('0' == s[0] && ('x' == s[1] || 'X' == s[1])) s += 2; int c; unsigned long rc; for (rc = 0; '\0' != (c = *s); s++) < if ( c >= 'a' && c else if (c >= 'A' && c else if (c >= '0' && c else < errno = EINVAL; return -1; >if (rc > (ULONG_MAX/16)) < errno = ERANGE; return -1; >rc *= 16; rc += (unsigned long) c; > *res = rc; return 0; >
Convert a hexadecimal string to an integer efficiently in C?
In C, what is the most efficient way to convert a string of hex digits into a binary unsigned int or unsigned long ? For example, if I have 0xFFFFFFFE , I want an int with the base10 value 4294967294 .
16 Answers 16
You want strtol or strtoul . See also the Unix man page
Edit: Now compatible with MSVC, C++ and non-GNU compilers (see end).
The question was «most efficient way.» The OP doesn’t specify platform, he could be compiling for a RISC based ATMEL chip with 256 bytes of flash storage for his code.
For the record, and for those (like me), who appreciate the difference between «the easiest way» and the «most efficient way», and who enjoy learning.
static const long hextable[] = < [0 . 255] = -1, // bit aligned access into this table is considerably ['0'] = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // faster for most modern processors, ['A'] = 10, 11, 12, 13, 14, 15, // for the space conscious, reduce to ['a'] = 10, 11, 12, 13, 14, 15 // signed char. >; /** * @brief convert a hexidecimal string to a signed long * will not produce or process negative numbers except * to signal error. * * @param hex without decoration, case insensitive. * * @return -1 on error, or result (max (sizeof(long)*8)-1 bits) */ long hexdec(unsigned const char *hex) < long ret = 0; while (*hex && ret >= 0) < ret = (ret return ret; >
It requires no external libraries, and it should be blindingly fast. It handles uppercase, lowercase, invalid characters, odd-sized hex input (eg: 0xfff), and the maximum size is limited only by the compiler.
For non-GCC or C++ compilers or compilers that will not accept the fancy hextable declaration.
Replace the first statement with this (longer, but more conforming) version:
static const long hextable[] = < -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1, 0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1,-1,10,11,12,13,14,15,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 >;
Am I right in thinking the hextable initialisation code is pseudo-code (if so it’s worth pointing that out), or is this some esoteric array initialisation syntax I’m not familiar with?
@hB0 i will respond to that incredibly vague and pointless observation by replying in kind: it compiles fine on clang. there are 22 warnings, but that’s to be expected.
I used ndk-build tool in android ndk — developer.android.com/tools/sdk/ndk/index.html and it does not compile gives me error specifically on the array declaration. Though I love the code fragment but I could not use it, so had to use other good method (but inefficient). Cant give you exact compilation error now.. (already gave you +1 last time)
@hB0 just comment out second line of code with «[0..255]» in it, and pray that you’re never passed invalid input
For AVR Microcontrollers I wrote the following function, including relevant comments to make it easy to understand:
/** * hex2int * take a hex string and convert it to a 32bit number (max 8 hex digits) */ uint32_t hex2int(char *hex) < uint32_t val = 0; while (*hex) < // get current character then increment char byte = *hex++; // transform hex character to the 4bit equivalent number, using the ascii table indexes if (byte >= '0' && byte = 'a' && byte <='f') byte = byte - 'a' + 10; else if (byte >= 'A' && byte <='F') byte = byte - 'A' + 10; // shift 4 to make space for new digit, and add the 4 bits of the new digit val = (val << 4) | (byte & 0xF); >return val; >
char *z ="82ABC1EF"; uint32_t x = hex2int(z); printf("Number is [%X]\n", x);
If you don’t have the stdlib then you have to do it manually.
unsigned long hex2int(char *a, unsigned int len)
Note: This code assumes uppercase A-F. It does not work if len is beyond your longest integer 32 or 64bits, and there is no error trapping for illegal hex characters.
a[i]-‘0’ and a[i]-‘A’+10 will also work in the rare case your system is using EBCDIC (they still exist).
As if often happens, your question suffers from a serious terminological error/ambiguity. In common speech it usually doesn’t matter, but in the context of this specific problem it is critically important.
You see, there’s no such thing as «hex value» and «decimal value» (or «hex number» and «decimal number»). «Hex» and «decimal» are properties of representations of values. Meanwhile, values (or numbers) by themselves have no representation, so they can’t be «hex» or «decimal». For example, 0xF and 15 in C syntax are two different representations of the same number.
I would guess that your question, the way it is stated, suggests that you need to convert ASCII hex representation of a value (i.e. a string) into a ASCII decimal representation of a value (another string). One way to do that is to use an integer representation as an intermediate one: first, convert ASCII hex representation to an integer of sufficient size (using functions from strto. group, like strtol ), then convert the integer into the ASCII decimal representation (using sprintf ).
If that’s not what you need to do, then you have to clarify your question, since it is impossible to figure it out from the way your question is formulated.