【问题标题】:Bitwise Less than or Equal to按位小于或等于
【发布时间】:2017-06-16 08:59:13
【问题描述】:

似乎有某种误解,认为这是为了比赛。 我正在尝试完成一项任务,但我已经坚持了一个小时。

 /*
     * 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)
    {
        int greater = (x + (~y + 1))>>31 & 1;
        return !(greater)|(!(x^y));

    }

按照 cmets 中的说明,我只能使用按位运算符。 我不知道如何解决x &lt;= y

我的想法是,我可以将 x 设置为它的二进制补码 (~x +1) 并用Y 添加它。如果为负,则X 大于Y。因此,通过否定我可以获得相反的效果。

同样,我知道!(x^y) 等价于x==y。 然而, 执行!(greater)|(!(x^y)) 不会返回正确的值。

我在哪里搞砸了?我觉得我错过了一点逻辑。

【问题讨论】:

  • @SamVarshavchik 暗示这是一场在线比赛......我被困在一个任务中,我不知道从哪里开始。
  • 投票结束:这个问题太宽泛了?对我来说似乎相当明确。

标签: c++ logic bit-manipulation


【解决方案1】:

由于溢出,这些函数不能完全工作,所以我就是这样解决问题的。呃……

int isLessOrEqual(int x, int y) {
int diff_sgn = !(x>>31)^!(y>>31);      //is 1 when signs are different
int a = diff_sgn & (x>>31);            //diff signs and x is neg, gives 1
int b = !diff_sgn & !((y+(~x+1))>>31); //same signs and difference is pos or = 0, gives 1
int f = a | b;
return f;
}

【讨论】:

    【解决方案2】:

    如果x &gt; y,则y - x(y + (~x + 1))为负数,则高位为1,否则为0。但我们要x &lt;= 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&lt;int&gt;::max() 作为值位的方便掩码。符号位的掩码只是值位的逐位取反。

    转到&lt;= 的实际表达式,我们在每个子表达式中分解出符号位的位掩码sm,并将操作推到表达式的外部。当x 为负且y 为非负时,逻辑表达式x &amp; ~y 的第一项为真。下一项~(x ^ Y) 的第一个因素是当两者都是负数或两者都是非负数时为真。当y - x 为非负数时,第二个因素~((y &amp; vm) + ~(x &amp; vm) + 1)) 为真,换句话说x &lt;= 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);
    }
    

    【讨论】:

    • 这很好,但是当 y 是二进制补码最大值 (0111...111) 并且 x 是二进制补码最小值 (1000...000) 时,它就会崩溃。
    • @KennethWorden,编辑部分下的版本应该可以解决您的问题。
    【解决方案3】:

    真的很喜欢 Yanagar1 的回答,很容易理解。

    实际上,我们可以删除那些班次运算符并使用德摩根定律,将运算符的数量从 15 个减少到 11 个。

    long isLessOrEqual(long x, long y) {
      long sign = (x ^ y);               // highest bit will be 1 if different sign
      long diff = sign & x;              // highest bit will be 1 if diff sign and neg x
      long same = sign | (y + (~x + 1)); // De Morgan's Law with the following ~same
                                         // highest bit will be 0 if same sign and y >= x
      long result = !!((diff | ~same) & 0x8000000000000000L); // take highest bit(sign) here
      return result;
    }
    

    【讨论】:

      【解决方案4】:

      这是我的实现(大约花费 3 小时...)

      int
      isLessOrEqual(int x, int y)
      {
          int a = y + ~x + 1;
          int b = a & 1 << 31 & a; // !b => y >= x, but maybe overflow
          int c = !!(x & (1 << 31)) & !(y & (1 << 31)); // y > 0, x < 0
          int d = !(x & (1 << 31)) & !!(y & (1 << 31)); // x > 0, y < 0
      
          int mask1 = !c + ~0;
      
          // if y > 0 && x < 0, return 1. else return !b
          int ans = ~mask1 & !b | mask1 & 1;
      
          int mask2 = !d + ~0;
      
        // if y < 0 && x > 0, return 0, else return ans
          return ~mask2 & ans | mask2 & 0;
      }
      
      • y - x == y + ~x + 1

      • a &amp; 1 &lt;&lt; 31 &amp; a!(!(a &amp; (1 &lt;&lt; 31)) | !a)的简化版

      逻辑是:

      if `y > 0 && x < 0`
          return true  
      if `x > 0 && y < 0`
          return false
      
      return y >= x
      

      为什么不直接y &gt;= x?因为可能会发生溢出。所以我必须早点返回以避免溢出。

      【讨论】:

        【解决方案5】:

        Yanagar1's answer 的启发,这是我的实现:

        int isLessOrEqual(int x, int y) {
        
            int indicator = !((y + (~x + 1)) >> 31); // negation of the result of y - x, 0 when y < x, -1 when y >= x
            int xsign = x >> 31; // -1 when x < 0, 0 when x >= 0
            int ysign = y >> 31; // -1 when y < 0, 0 when y >= 0
            int xbool = !xsign; // 0 when x < 0, 1 when x >= 0
            int ybool = !ysign; // 0 when y < 0, 1 when y >= 0
            int result = (!(xbool ^ ybool)) & indicator;
            return result | (ybool & !xbool);
        }
        

        解释:将x(~x + 1)的2的补数否定加到y上,本质上是计算y - x,然后将结果的符号位逻辑取反,我们可以在y &lt; x时得到0,在@时得到1 987654328@。但也存在潜在的溢出情况(y-x 符号相反,即yx 符号相同时,不会发生溢出):

        |-----------|------------------------|------------------------|
        |           |         y > 0          |         y < 0          |
        |-----------|------------------------|------------------------|
        |   x > 0   |           ok           | overflow when y = TMin |
        |-----------|------------------------|------------------------|
        |   x < 0   | overflow when x = TMin |           ok           |
        |-----------|------------------------|------------------------|
        

        所以当标志不同时我们需要小心。

        【讨论】:

          猜你喜欢
          • 2022-09-24
          • 2011-01-25
          • 2013-09-02
          • 2023-04-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-06-18
          • 2012-08-21
          相关资源
          最近更新 更多