Возможно, я забываю гениальную побитовую операцию, но есть ли более умный способ выразить следующее?
int r = foo(); // returns an int
r -= r > 0; // same as `if(r > 0) r--;`
Я хочу объединить строки в одно выражение.
Самый читаемый способ — самый умный:
if (r > 0)
--r;
Он читабелен, и, скорее всего, он создаст самый быстрый код, потому что многие разработчики не делают тщетных попыток выглядеть умно, а люди, создающие оптимизирующие компиляторы, обнаруживают общие шаблоны и оптимизируют их.
Лично я бы предпочел if (r > 0) r--;
для удобства чтения, но если вы действительно хотите избежать условных выражений:
r = r > 0 ? r-1 : r;
который по-прежнему является условным присваиванием, либо:
r > 0 && r--;
или же:
r <= 0 || r--;
Это работает, потому что, если левая сторона уже определяет результат, правая сторона не будет выполнена.
Кстати, не ожидайте каких-либо различий в производительности. Большинство компиляторов будут генерировать более или менее одинаково производительный код из всех этих опций.
Если бы тест был r> = 0, то одной из возможностей было бы выделить бит знака, а затем вычесть (1-signBit).
В C что-то вроде этого (но я думаю, что это можно перенести на многие языки)
int r = foo();
return r - (int)((~ ((unsigned) r)) >> (sizeof(r)*8-1));
Конечно, теперь вы зависите от внутреннего представления int и сильно запутали код …
Для r> 0 это более сложно … Может быть, что-то вроде
unsigned int r = foo();
return (int) (r - ((~(r | (r-1))) >> (sizeof(r)*8-1)));
Я даже не пробовал эти фрагменты, и ответ был для шутки, но если вы отнесетесь к этому серьезно, вам придется дать массу объяснений для оправдания такого взлома, включая тесты, анализ сгенерированного кода и тесты производительности.
Если каждая такая незначительная операция, как эта, проходит через такой процесс, писать код будет больно, и даже больше для тех, кому придется его поддерживать (читать / понимать / расширять …). Я надеюсь, что это не преждевременная оптимизация, и в этом случае, возможно, правильным решением будет написать небольшой кусочек ассемблера.
Ну, скажем, для 32-битных целых чисел с 2-мя дополнениями, что-то подобное может работать (не проверено!)
const int mask = 1 << 31;
int r = foo();
r -= ((r ^ mask) >> 31);
в основном, вы проверяете, равен ли крайний левый бит нулю, и конвертируете его в 1, если он есть, в противном случае он все равно будет равен нулю, затем сдвигаете его, чтобы получить 1 для положительного и 0 для отрицательного, и вычесть