Хорошо, я наконец понял, как это сделать эффективно:
Java:
int f(int x, int y) {
return (((x >> 31) | ((unsigned)-x >> 31)) ^ y) - y;
}
C / C ++:
int f(int x, int y) {
return (((x > 0) - (x < 0)) ^ y) - y;
}
Эти функции возвращают -sgn(x)
y, равное -1 и sgn(x)
в противном случае.
Или, если нам просто нужно работать для каждого значения, отличного от -2 ^ 31 (минимальное значение типа unsigned int), с преимуществом сохранения абсолютного значения, это функция, которая переворачивает знак в зависимости от переменной y :
int f(int x, int y) {
return (x ^ y) - y; // returns -x for y == -1, x otherwise
}
Вывод :
-x == ~ x + 1 == (x ^ 0xFFFFFFFF) + 1 == (x ^ -1) + 1 == (x ^ -1) - (-1). Подставляя -1 с y, мы получаем формулу с двумя переменными, которая имеет интересное свойство, которое возвращает неизменный x, если y установлен в 0, потому что ни (x ^ 0), ни вычитание 0 не изменяют результат. Теперь угловой случай, если x равен 0x8000000, когда эта формула не работает. Это можно исправить, применив функцию sgn (x), поэтому у нас есть (sgn(x) ^ y) - y)
. Наконец, мы заменим функции sgn (x) на известные формулы, которые не используют ветвления.