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
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 << 4) | hextable[*hex++];
}
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
};
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.
In C you can convert a hexadecimal number to decimal in many ways. One way is to cast the hexadecimal number to an integer. I personally found this to be simple and small.
Here is an sample code for converting a Hexadecimal number to a Decimal number with the help of casting.
#include <stdio.h>
int main(){
unsigned char Hexadecimal = 0x6D; //example hex number
int Decimal = 0; //decimal number initialized to 0
Decimal = (int) Hexadecimal; //conversion
printf("The decimal number is %d\n", Decimal); //output
return 0;
}
For larger Hex strings like in the example I needed to use strtoul.
@Eric
I was actually hoping to see a C wizard post something really cool, sort of like what I did but less verbose, while still doing it "manually".
Well, I'm no C guru, but here's what I came up with:
unsigned int parseHex(const char * str)
{
unsigned int val = 0;
char c;
while(c = *str++)
{
val <<= 4;
if (c >= '0' && c <= '9')
{
val += c & 0x0F;
continue;
}
c &= 0xDF;
if (c >= 'A' && c <= 'F')
{
val += (c & 0x07) + 9;
continue;
}
errno = EINVAL;
return 0;
}
return val;
}
I originally had more bitmasking going on instead of comparisons, but I seriously doubt bitmasking is any faster than comparison on modern hardware.