For a contiguous range of characters you can:
_Bool isbetween(int c, int start, int end){
return ((unsigned)c-start < (end-start));
}
To account for case, use tolower()
and the lower case range:
static inline int tolower(int c){
return c | ( ((unsigned)c-'A' < 26)<<5 );
}
//isbetween(tolower(x),'a','n');
For a non-contiguous range, you may need to create a mask. In this example, I will check for vowels (for brevity because there are only 5, but any combination in a range of 32 could be used or 64 with some modifications ...
in fact, a 64 bit mask on a 64 bit platform would eliminate the need for case handling).
static const unsigned vowel_mask = (1<<('a'-'a'))
|(1<<('e'-'a'))|(1<<('i'-'a'))|(1<<('o'-'a'))|(1<<('u'-'a'));
int isvowel(int c){ //checks if c is a,A,e,E,i,I,o,O,u,U
unsigned x = (c|32)-'a';
return ((x<32)<<x)&vowel_mask;
}
Note that these implementations contain no branches; however the use of unsigned comparison may prevent automatic compiler vectorization (intel intrinsics, don't have unsigned compare) ... if that is your goal, you can use 2 &
ed comparisons instead. This method may or may not work on non-ascii systems depending on the separation distance of the characters.
GCC
isvowel:
or edi, 32 # tmp95,
xor eax, eax # tmp97
sub edi, 97 # x,
cmp edi, 31 # x,
setbe al #, tmp97
shlx eax, eax, edi # tmp99, tmp97, x
and eax, 1065233 # tmp96,
ret
Clang
isvowel: # @isvowel
or edi, 32
add edi, -97
mov eax, 32
xor ecx, ecx
cmp edi, eax
setb cl
shlx eax, ecx, edi
and eax, 1065233
ret
ICC
isvowel:
xor eax, eax #15.26
or edi, 32 #14.23
add edi, -97 #14.27
cmp edi, 32 #15.26
setb al #15.26
shlx eax, eax, edi #15.23
and eax, 1065233 #15.26
ret #15.26
In addition to the standard stackoverflow license, this code is released to the Public Domain