如果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);
}
编辑:
作为评论者指针,上述版本容易出现算术溢出错误。这是涵盖边缘情况的另一个版本。
#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 - x 为非负数时,第二个因素~((y & vm) + ~(x & vm) + 1)) 为真,换句话说x <= y,忽略符号位。这两个术语是 or'd,所以使用 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);
}