Bitwise Less than or Equal to
Asked Answered
J

7

5

There seems to be some kind of misconception that this is for a contest. I'm trying to work through an assignment and I've been stuck on this for an hour now.

 /*
     * 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));

    }

I'm only able to use bitwise operators, as instructed in the comments. I cannot figure out how to solve x <= y;

My thought process is that I can set x as its two's complement (~x +1) and add it with Y. If it is negative, X is greater than Y. Therefore, by negating that I can get the opposite effect.

Similarly, I know that !(x^y) is equivalent to x==y. However, doing !(greater)|(!(x^y)) does not return the proper value.

Where am I messing up? I feel like I'm missing a small bit of logic.

Jobless answered 31/1, 2017 at 2:40 Comment(2)
@SamVarshavchik Implying this is an online contest... I'm stuck on an assignment and I don't know where to move from here.Jobless
Voters to Close: this question is too broad? Seems rather well-defined to me.Sourpuss
S
5

If x > y, then y - x or (y + (~x + 1)) will be negative, hence the high bit will be 1, otherwise it will be 0. But we want x <= y, which is the negation of this.

    /*
     * 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);
    }

Even better, drop the shift operator and use a bit mask on the high bit:

    int isLessOrEqual(int x, int y)
    {
        return !((y + (~x + 1)) & 0x80000000);
    }

EDIT:

As a commenter pointer out, the above version is susceptible to arithmetic overflow errors. Here is another version that covers the edge cases.

#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);
}

Explanation: the overall strategy is to treat the sign bit of the inputs as logically distinct from the rest of the bits, the "value bits," and perform the subtraction as in the previous example on just the value bits. In this case, we need only perform the subtraction where the two inputs are either both negative or both non-negative. This avoids the arithmetic overflow condition.

Since the size of int strictly speaking is unknown at run time, we use std::numeric_limits<int>::max() as a convenient mask for the value bits. The mask of the sign bit is simply the bit-wise negation of the value bits.

Turning to the actual expression for <=, we factor out the bit-wise mask sm of the sign bit in each of the sub-expressions and push the operation to the outside of the expression. The first term of the logical expression x & ~y is true when x is negative and y is non-negative. The first factor of the next term ~(x ^ Y) is true when both are negative or both are non-negative. The second factor ~((y & vm) + ~(x & vm) + 1)) is true when y - x is non-negative, in other words x <= y, ignoring the sign bit. The two terms are or'd, so using c++ logical expression syntax we have:

x < 0 && y >= 0 || (x < 0 && y < 0 || x >= 0 && y >= 0) && y - x >= 0

The !! outermost operators convert the raised sign bit to a 1. Finally, here is the Modern C++ templated constexpr version:

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);
}
Sourpuss answered 31/1, 2017 at 3:15 Comment(2)
This is good, but it breaks down when y is the two's complement maximum (0111...111), and x is the two's complement minimum (1000...000).Edmond
@KennethWorden, the version under the edit section should address your valid concern.Sourpuss
S
8

Those functions don't fully work because of the overflow, so that's how I solved the problem. Eh...

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;
}
Sheridansherie answered 3/2, 2017 at 3:34 Comment(0)
S
5

If x > y, then y - x or (y + (~x + 1)) will be negative, hence the high bit will be 1, otherwise it will be 0. But we want x <= y, which is the negation of this.

    /*
     * 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);
    }

Even better, drop the shift operator and use a bit mask on the high bit:

    int isLessOrEqual(int x, int y)
    {
        return !((y + (~x + 1)) & 0x80000000);
    }

EDIT:

As a commenter pointer out, the above version is susceptible to arithmetic overflow errors. Here is another version that covers the edge cases.

#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);
}

Explanation: the overall strategy is to treat the sign bit of the inputs as logically distinct from the rest of the bits, the "value bits," and perform the subtraction as in the previous example on just the value bits. In this case, we need only perform the subtraction where the two inputs are either both negative or both non-negative. This avoids the arithmetic overflow condition.

Since the size of int strictly speaking is unknown at run time, we use std::numeric_limits<int>::max() as a convenient mask for the value bits. The mask of the sign bit is simply the bit-wise negation of the value bits.

Turning to the actual expression for <=, we factor out the bit-wise mask sm of the sign bit in each of the sub-expressions and push the operation to the outside of the expression. The first term of the logical expression x & ~y is true when x is negative and y is non-negative. The first factor of the next term ~(x ^ Y) is true when both are negative or both are non-negative. The second factor ~((y & vm) + ~(x & vm) + 1)) is true when y - x is non-negative, in other words x <= y, ignoring the sign bit. The two terms are or'd, so using c++ logical expression syntax we have:

x < 0 && y >= 0 || (x < 0 && y < 0 || x >= 0 && y >= 0) && y - x >= 0

The !! outermost operators convert the raised sign bit to a 1. Finally, here is the Modern C++ templated constexpr version:

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);
}
Sourpuss answered 31/1, 2017 at 3:15 Comment(2)
This is good, but it breaks down when y is the two's complement maximum (0111...111), and x is the two's complement minimum (1000...000).Edmond
@KennethWorden, the version under the edit section should address your valid concern.Sourpuss
E
2

Really enjoyed Yanagar1's answer, which is very easy to understand.

Actually we can remove those shift operators and use De Morgan's laws, which reduce the number of operators from 15 to 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;
}
Educator answered 11/9, 2019 at 21:19 Comment(1)
Just want to say that the lab now forbids using large numbers. Only 0 through 255 (0xFF), inclusive.Haystack
O
0

Here is my implementation(spend around 3 hours...)

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 & 1 << 31 & a is simplify from !(!(a & (1 << 31)) | !a)

The logic is:

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

return y >= x

Why not just y >= x directly? because overflow may happen. So I have to early return to avoid overflow.

Oz answered 8/1, 2019 at 9:49 Comment(0)
G
0

Inspired by Yanagar1's answer, here's my implementation:

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);
}

Explanation: Adding 2's complement negation of x (~x + 1) to y is essentially calculating y - x, then logical negate the sign bit of the result, we can have 0 when y < x, and 1 when y >= x. But there are potential overflow cases (overflow cannot happen when y and -x have opposite signs, that is, when y and x have same signs):

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

so we need to be careful when the signs are different.

Gean answered 19/11, 2019 at 20:48 Comment(0)
I
0

Might my solution is stupid.

int isLessOrEqual(int x, int y) {
  /*
   * A: sign bit of x   B: sign bit of y    C:A == B  Result          Rearrange the result(Expeced)
   * 0                  0                   1         y - x >= 0      (y + (~x+1) >= 0) & 1 | 0 => (y + (~x+1) >= 0) & C | !(B | C)
   * 0                  1                   0         0               (y + (~x+1) >= 0) & 0 | 0 => (y + (~x+1) >= 0) & C | !(B | C)
   * 1                  0                   0         1               (y + (~x+1) >= 0) & 0 | 1 => (y + (~x+1) >= 0) & C | !(B | C)
   * 1                  1                   1         y - x >= 0      (y + (~x+1) >= 0) & 1 | 0 => (y + (~x+1) >= 0) & C | !(B | C)
   * But, minus operator is illegal. So (y - x) placed by (y + (-x)).
   * You know -x == (~x + 1).
   * If we know *x* and *y* have different sign bits, the answer is determinated and the (y-x >= 0) was useless.
   * finally, the work like designing digital circuit. produce a final expression.
   */
  int A = (x >> 31) & 1;
  int B = (y >> 31) & 1;
  int C = !(A ^ B);
  int greatOrEqual0 = (!(((y + (~x + 1)) >> 31) ^ 0));
  return (greatOrEqual0 & C) | !(B | C);
}
Incardination answered 29/3, 2022 at 14:30 Comment(0)
D
0

Yanagar1's answer is good! Inspired by his code, based on whether the overflow happens, we need to process some special cases (x = TMin, y = TMin) Below is my code, using 22 operators.

By the way, due to the cs-app data lab's updates, here I use the 64-bit int(long)

long isLessOrEqual(long x, long y) {
    // long msb = x ^ y;
    // use two Sign variables to preprocess the boundary cases
    long xSign = !!(x >> 63);             // give the sign of x (1 when x is -)
    long ySign = !!(y >> 63);             // same as upwards
    long Less = !!((x + (~y + 1)) >> 63); // normal cases processing

    // use formal two signal expressions to process the boundary cases
    return (!(ySign & (!xSign))) & ((xSign & (!ySign)) | (!(x ^ y)) | Less);
}
Dregs answered 17/4, 2023 at 7:17 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.