So for an assignment I have to convert a character ( 0
- F
) to an integer ( 0
- 15
), 0
- 9
works fine, but if any letter is given, it prints a random number: For C
for instance, it gives 19
, for D
is returns 20
.
This is my method:
int char2int(char digit) {
int i = 0;
if (digit == 0 || 1 || 2 || 3 || 4 || 5 || 6 || 7 || 8 || 9)
i = digit - '0';
else
if (digit == 'A' || 'B' || 'C' || 'D' || 'E' || 'F')
i = digit - '9';
else
i = -1;
return i;
}
At first my if statements were like this:
if (digit => 0 && =< 9)
if (digit => A && =< F)
But that gave a number of errors. You can tell I don't know C very well. My current If statement works but I'm sure it's unnecessarily long.
if (digit == 0 || 1 || 2 || 3 || 4 || 5 || 6 || 7 || 8 || 9)
This is not how conditional expressions work in C.
You either need to compare digit
against each of the numbers individually
if (digit == '0' || digit == '1' || digit == '2' ...
or do it the clever way:
if(digit >= '0' && digit <= '9')
^^ not =<
Notice that I put '
around the numbers because you want to compare the digit with the letter 0 and not the number (which is not the same see here for all the ASCII character values).
You were on the right path when you started, but wandered off a bit. try this
#include <ctype.h>
int char2int(char d) {
if (!isxdigit(d)) {
return -1;
}
if (isdigit(d)) {
return d - '0';
}
return (tolower(d) - 'a') + 10;
}
If you'd prefer an approach closer to your range testing, you could do it like this:
int char2int(char d) {
if (d >= '0' && d <= '9') {
return d - '0';
}
d = tolower(d);
if (d >= 'a' && d <= 'f') {
return (d - 'a') + 10;
}
return -1;
}
Assuming ASCII the following converts from a character (0-9, af, AF) to the associated unsigned integer (0-15). Any other character will also be converted to... some random value in the 0-15 range. Garbage in, garbage out.
unsigned hexToUnsigned(char ch) {
return ((ch | 432) * 239'217'992 & 0xffff'ffff) >> 28;
}
CPUs with 32-bit integers will generally be able to elide the 0xffff'ffff masking. On my machine the compiler turns this function into:
hexToUnsigned PROC
movsx eax, cl
or eax,1B0h
imul eax, eax, 0E422D48h
shr eax, 1ch
ret 0
hexToUnsigned ENDP
Another common way to do this has fewer apparent operations (just three), returns total garbage on invalid characters (which is probably okay), but also requires division (which takes it out of the top spot):
return ((ch | ('A' ^ 'a')) - '0') % 39;
To illustrate how compilers feel about division, they (at least on x64) change it into a multiply of the reciprocal to get the product and then one more multiply and subtract if you need the remainder:
hexToUnsigned PROC
; return ((ch | ('A' ^ 'a')) - '0') % 39;
movsx r8d, cl
mov eax, -770891565
or r8d, 32
sub r8d, 48
imul r8d
add edx, r8d
sar edx, 5
mov ecx, edx
shr ecx, 31
add edx, ecx
imul ecx, edx, 39
sub r8d, ecx
mov eax, r8d
ret 0
hexToUnsigned ENDP
The return value is not random. Every ascii character is represented in the memory by a value. The value of each ascii character can be found in the Ascii Table .
The other responses tell you what you are doing wrong with the conditional expressions, but another mistake is that if the character is A, B, C, D, E or F you need to convert it to int like this i = ( digit - 'A' ) + 10
which means take the value of A, B, C, D, E or F subtract the min value which is A and add to that 10.
Moreover, you can see that if you don't need the exact value of a character you can do without the ascii table, using the property that letters are continuous.
If you are willing to make assumptions such as char
are encoded as ASCII and 2's complement, the following is quite efficient.
This code is not meant for readability. Use other solutions if that is a concern. This is for tight encoding. With a given processor, it is about 10 instructions. Your results will vary.
Subtract 1. This shifts the char
values down 1. In particular, AZ is now 64-89 and az in the range 96-121.
Test if a bit (64's place) is clear: in the range of '0' - '9'. If so, increment by 7 and mask to keep that bit (64's place) cleared.
Otherwise mask a bit to fold az into the AZ range.
Now '0' to '9' and 'A' to 'Z' are in a continues range. Just subtract 54. All unsigned char
values other than 0-9
, AZ
and az
will have a value > 35. This is useful for any base use to base 36.
int Value(char ch) {
if (!(--ch & 64)) { // decrement, if ch in the '0' to '9' area ...
ch = (ch + 7) & (~64); // move 0-9 next to A-Z codes
} else {
ch &= ~32;
}
ch -= 54; // -= 'A' - 10 - 1
if ((unsigned char)ch > 15) {
; // handle error
}
return (unsigned char)ch;
}
In redis
https://github.com/antirez/redis/blob/3.2.8/src/sds.c#L892
int hex_digit_to_int(char c) {
switch(c) {
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
case 'a': case 'A': return 10;
case 'b': case 'B': return 11;
case 'c': case 'C': return 12;
case 'd': case 'D': return 13;
case 'e': case 'E': return 14;
case 'f': case 'F': return 15;
default: return 0;
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.