Если x > y
, то y - x
или (y + (~x + 1))
будет отрицательным, следовательно, старший бит будет 1, в противном случае это будет 0. Но мы хотим x <= y
, что отрицание этого.
/*
* isLessOrEqual - if x <= y then return 1, else return 0
* Example: isLessOrEqual(4,5) = 1.
* Legal ops: ! ~ &^| + << >>
* Max ops: 24
* Rating: 3
*/
int isLessOrEqual(int x, int y)
{
return !(((y + (~x + 1)) >> 31) & 1);
}
Еще лучше, если уронить оператор сдвига и использовать битовую маску на высокой бит:
int isLessOrEqual(int x, int y)
{
return !((y + (~x + 1)) & 0x80000000);
}
EDIT:
Как комментатора указатель вне, выше версия восприимчив к арифметическим ошибкам переполнения. Вот еще одна версия, которая охватывает случаи краев.
#include <limits>
int isLessOrEqual(int x, int y)
{
static int const vm = std::numeric_limits<int>::max();
static int const sm = ~vm;
return !! ((x & ~y | ~(x^y) & ~((y & vm) + ~(x & vm) + 1)) & sm);
}
Объяснения: общая стратегия для лечения знакового бита входов логически отличается от остальных бит, «Значение бит» и выполнить вычитание, как и в предыдущем примере, только на стоимости биты. В этом случае нам нужно выполнить только вычитание, когда два входа либо отрицательные, либо оба неотрицательные. Это позволяет избежать арифметического состояния переполнения.
Поскольку размер int
, строго говоря, неизвестен во время выполнения, мы используем std::numeric_limits<int>::max()
в качестве удобной маски для битов значений. Маска знакового бита - это просто побитное отрицание битов значения.
Обращаясь к фактическому выражению для <=
, мы отбрасываем бит-мутную маску sm
знакового бита в каждом из выражений и выталкиваем операцию за пределы выражения. Первый член логического выражения x & ~y
имеет значение, когда x
отрицательный, а y
неотрицателен. Первый фактор следующего термина ~(x^Y)
имеет значение, когда оба отрицательные или оба неотрицательные. Второй фактор ~((y & vm) + ~(x & vm) + 1))
справедлив, если y - x
неотрицателен, другими словами x <= y
, игнорируя знак знака. Эти два термина OR-нут, поэтому использование C++ логический синтаксис выражения мы имеем:
x < 0 && y >= 0 || (x < 0 && y < 0 || x >= 0 && y >= 0) && y - x >= 0
В !!
внешние операторы преобразуют поднятую знаковый бит в 1
.Наконец, здесь Modern C++ шаблонных constexpr
версия:
template<typename T>
constexpr T isLessOrEqual(T x, T y)
{
using namespace std;
// compile time check that type T makes sense for this function
static_assert(is_integral<T>::value && is_signed<T>::value, "isLessOrEqual requires signed integral params");
T vm = numeric_limits<T>::max();
T sm = ~vm;
return !! ((x & ~y | ~(x^y) & ~((y & vm) + ~(x & vm) + 1)) & sm);
}
@SamVarshavchik Подразумевая это интернет-конкурс ... я застрял на уступки, и я не знаю, куда двигаться дальше. – Whatamia
Избиратели для голосования: этот вопрос слишком широк? Кажется довольно ясным для меня. – ThomasMcLeod