Следующий код вычислит ваш поиск без ветвей, без LUT, за ~ 3 такта, ~ 4 полезных инструкции и ~ 13 байт машинного кода x86 с высокой степенью * *.
Это зависитдля целочисленного представления дополнения 2.
Однако вы должны убедиться, что определения типов u32
и s32
действительно указывают на 32-битные целые типы без знака и со знаком. stdint.h
типы uint32_t
и int32_t
были бы подходящими, но я не знаю, доступен ли вам заголовок.
const int lookup[16] = {-1, -1, 0, 0, 1, 1, 0, 0, -1, -1, 0, 0, 1, 1, 0, 0};
int a(int num) {
return lookup[num & 0xF];
}
int d(int num){
typedef unsigned int u32;
typedef signed int s32;
// const int lookup[16] = {-1, -1, 0, 0, 1, 1, 0, 0, -1, -1, 0, 0, 1, 1, 0, 0};
// 2-bit signed 2's complement: 11 11 00 00 01 01 00 00 11 11 00 00 01 01 00 00
// Hexadecimal: F 0 5 0 F 0 5 0
const u32 K = 0xF050F050U;
return (s32)(K<<(num+num)) >> 30;
}
int main(void){
for(int i=0;i<16;i++){
if(a(i) != d(i)){
return !0;
}
}
return 0;
}
Убедитесь сами здесь: https://godbolt.org/z/AcJWWf
При выборе константы
Ваш поиск для 16 очень маленьких констант от -1 до +1 включительно. Каждый вписывается в 2 бита, и их 16, которые мы можем расположить следующим образом:
// const int lookup[16] = {-1, -1, 0, 0, 1, 1, 0, 0, -1, -1, 0, 0, 1, 1, 0, 0};
// 2-bit signed 2's complement: 11 11 00 00 01 01 00 00 11 11 00 00 01 01 00 00
// Hexadecimal: F 0 5 0 F 0 5 0
u32 K = 0xF050F050U;
Поместив их с индексом 0, ближайшим к старшему значащему биту, один сдвиг 2*num
будетпоместите знаковый бит вашего 2-битного числа в знаковый бит регистра. Если сдвинуть вправо 2-битное число на 32-2 = 30 бит, знак расширяет его до полного int
, что завершает трюк.