问题
I need a function to convert a 32bit or 24bit signed (in two's complement) hexadecimal string into a long int. Needs to work on both 32bit and 64bit machines (regardless of the size of long int) and work regardless of whether the machine is a two's complement machine or not.
SOLUTION:
long int hex2li (char hexStr[], int signedHex)
{
int bits = strlen (hexStr) * 4;
char *pEnd;
long long int result = strtoll (hexStr, &pEnd, 16);
if (pEnd[0] == '\0')
{
if (signedHex)
{
if (result >= (1LL << (bits - 1))) result -= (1LL << bits);
}
return (long int) result;
}
return LONG_MIN;
}
回答1:
For a 24-bit string:
When you parse the hex string, the standard strtol
function will read it as an unsigned value in the range 0 -> 2^24 - 1
.
The range 0 -> 2^23 - 1
is correct, but the range 2^23 -> 2^24 - 1
needs to be mapped to -2^23 -> -1
which is a simple subtraction which can be performed as follows.
if (result >= (1L << 23))
result -= (1L << 24);
To convert a 32-bit string using the same technique you have to use an intermediate type that can represent a full 32-bit unsigned integer in a signed type for performing the subtraction. A long long int
is guaranteed to be 64-bits so you can use this.
E.g.
long int ParseHexStr(const char *in, int bits)
{
char* endptr;
long long int result;
result = strtoll(in, &endptr, 16);
/*
** TODO - error checking, e.g. check endptr != in
** Also check for range errors, signalled by LLONG_MIN
** LLONG_MAX and a errno == ERANGE.
*/
if (result >= (1LL << (bits - 1))
result -= (1LL << bits);
return result;
}
回答2:
We have a SIGN_EXTEND
macro, that looks like:
#define SIGN_EXTEND(X, SignBit, Type) \
(((Type) ((X) << (8 * sizeof(Type) - (SignBit) - 1))) >> \
(8 * sizeof(Type) - (SignBit) - 1))
It relies on the >>
operator 1-filling the input when the sign bit is set. Use it like:
SIGN_EXTEND(0x89abcd, 23, int32_t);
For your problem, you could use:
long int hex2li (char string[])
{
char *pEnd;
long int result = SIGN_EXTEND(strtol (string, &pEnd, 16), 23, long int);
if(pEnd[0] == '\0')
return result;
return LONG_MIN;
}
回答3:
This comparison is wrong: if (toupper (string[0]) == 'F')
You'll need to sign-extend for any value with the MSB set, so something like:
if(strchr("89ABCDEF", toupper(string[0])) != NULL)
回答4:
Is there a reason why you cannot use strtol with radix 16?
回答5:
if (toupper (string[0]) == 'F')
{
return (result | 0xFF000000);
}
this will produce number with correct sign.
if (toupper (string[0]) == 'F')
{
return ( ~(result | 0xFF000000) + 1);
}
this will always produce positive result
来源:https://stackoverflow.com/questions/2781878/signed-hexadecimal-string-to-long-int-function