-2

I have a task to practice and I just can't get the solution. It should be noted that I am a total beginner. There is 1min for the exercise, so it should be child's play. But I am racking my brains over it.

I am supposed to write an expression for the following :

Let x and k be variables of type int. Formulate a C condition with bitwise operator that is true exactly when x is less than 2^(k-1).

I would have done this with the ?-operator. So something like

( ) ? 1 : 0

But which condition must be in the brackets? I can't think of a bitwise operator that can be used to compare less than?

I really don't have any Idea :/

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
Paul
  • 1
  • Are comparison operators (specifically `<` and `>`) allowed? I am asking because it _can_ be done without them; and that _could_ be part of the exercise. – Ruud Helderman Dec 01 '22 at 19:58

3 Answers3

2

It seems you mean something like the following

if ( x < 1 << k - 1 ) { /*...*/ }

provided that the expression k - 1 is not negative and the expression 1 << k - 1 is representable as a positive value in an object of the type int.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

Let x and k be variables of type int.

int x;
int k;

Formulate a C condition with bitwise operator that is true exactly when x is less than 2^(k-1).

Start with directly translating it:

if ( x < pow(2,k-1) ) { ... }

Then remove the call to pow using bitwise operators

2-to-the-power-of k-1 is: 1<<(k-1)

Examples: to help clarify

2 ^ 3 == 1 << 3 == 8
2 ^ 5 == 1 << 5 == 32

So the final expression is:

( x < 1<<(k-1) ); // Result will either be 1 or 0

There is no need to use ternary ?: operator.
The boolean expression < will evaluate to 1 or 0 by itself!

abelenky
  • 63,815
  • 23
  • 109
  • 159
  • Ahh it's a bitwise shift to the left. That's the whole story? – Paul Dec 01 '22 at 19:39
  • Why don't I have to clamp? The expression is calculated from left to right, isn't it? Why does it not have to be like that? x < (1 << (k - 1)) If I omit the parenthesis, x < 1 would be evaluated first and then the result of this with << (k - 1), wouldn't it? – Paul Dec 01 '22 at 19:56
  • Math is ***NOT*** done left-to-right. Math is done by Order of Operations [PEMDAS](https://www.cuemath.com/numbers/pemdas/) – abelenky Dec 01 '22 at 20:05
  • If you think math is done left-to-right, how would you evaluate `8+3*2`? Would you get `11*2 == 22`? Or would you get `8+6 == 14`?? – abelenky Dec 01 '22 at 20:20
0

TL;DR

!(x >> k-1)

This is simple enough to be a viable answer that someone might come up with given one minute of time. It only works within certain domains for x and k. Coming up with a solution that covers the full integer domain, is a lot tougher.


The question lacks requirements, so I'll have to make a few assumptions. For one, "a C condition with bitwise operator" could mean:

  1. at least one bitwise operator
  2. only bitwise operators (i.e. no other operators)
  3. as many bitwise operators as possible (i.e. as few other operators as possible)

Most answers seem to go for #1, and use a comparison operator (<) to do the actual 'less than' test. Assuming this is an assignment to teach you about the binary representation of integer numbers, comparison operators should be out of scope. Not in the least because comparison with a power of two is a simple bit mask test. For example, this works for k-1 = 8:

(x & ~0xFF) == 0

As demonstrated by other posters, a bit shift operator can be used to dynamically construct the bit mask from k, but it's actually a lot smarter to bit-shift x itself in the opposite direction. Basically, we divide x by 2^(k-1). If the (truncated) result is zero, then obviously, x is less than 2^(k-1).

(x >> k-1) == 0

or:

!(x >> k-1)

or (avoiding the arithmetic operator -):

!(x << 1 >> k)

or (avoiding the logical operator ! as well):

(~(x<<1 | x | x>>1 | x>>2 | x>>3 | ... | x>>31) >> k) & 1

The latter demands knowledge of the width (in bits) of type int; assuming 32 bits here.

All of the above work only within certain domains for both x and k.

  • x must be non-negative; my expression will incorrectly return false for negative numbers.
  • k must be non-negative, and less than the width (in bits) of int (the type of x); my expression suffers from UB otherwise.

It is possible to make it work for wider domains. I doubt it is on-topic for a one-minute assignment, but nevertheless it makes an interesting exercise.

To support negative x, just use INT_MIN as a bit mask to test the sign bit.

(x & INT_MIN) | ~((x<<1 | x | x>>1 | x>>2 | x>>3 | ... | x>>31) >> k) & 1

Or if you insist on always having 'true' represented by 1:

(x >> 31 | ~(x<<1 | x | x>>1 | x>>2 | x>>3 | ... | x>>31) >> k) & 1

(Again assuming 32 bits; if different, change the literal 31 accordingly.)

If k is negative, then every positive x should yield false. Please note that this is identical to the behavior for zero k. So just replace k with (k & ~k>>31) (the bitwise equivalent of (k < 0 ? 0 : k) in 32-bits architecture).

(x>>31 | ~(x<<1 | x | x>>1 | x>>2 | x>>3 | ... | x>>31) >> (k & ~k>>31)) & 1

For k greater than or equal to the size (in bits) of type int, every x should yield true:

(((k>>5 | k>>6 | k>>7 | ... | k>>30) & ~k>>31) | x>>31 | ~(x<<1 | x | x>>1 | x>>2 | x>>3 | ... | x>>31) >> (k & ~k>>31)) & 1

k>>5 as the first term may seem obscure. 5 is the logarithm of 32; in a 64-bits architecture we'd start with k>>6:

(((k>>6 | k>>7 | k>>8 | ... | k>>62) & ~k>>63) | x>>63 | ~(x<<1 | x | x>>1 | x>>2 | x>>3 | ... | x>>63) >> (k & ~k>>63)) & 1

If it would be acceptable to use not only bitwise, but also logical operators, then the expression (here 32 bits) could be simplified to:

((k & ~31) && ~k>>31) || (x >> 31) || !(x << 1 >> (k & ~k>>31))

Here is a simple test (32 bits):

#include <stdio.h>
#include <limits.h>

static int purely_bitwise(int x, int k)
{
  return (
    ((
      k>>5 | k>>6 | k>>7 | k>>8 | k>>9 |
      k>>10 | k>>11 | k>>12 | k>>13 | k>>14 | k>>15 | k>>16 | k>>17 | k>>18 | k>>19 |
      k>>20 | k>>21 | k>>22 | k>>23 | k>>24 | k>>25 | k>>26 | k>>27 | k>>28 | k>>29 |
      k>>30
    ) & ~k>>31) |
    x>>31 |
    ~(
      x<<1 | x | x>>1 | x>>2 | x>>3 | x>>4 | x>>5 | x>>6 | x>>7 | x>>8 | x>>9 |
      x>>10 | x>>11 | x>>12 | x>>13 | x>>14 | x>>15 | x>>16 | x>>17 | x>>18 | x>>19 |
      x>>20 | x>>21 | x>>22 | x>>23 | x>>24 | x>>25 | x>>26 | x>>27 | x>>28 | x>>29 |
      x>>30 | x>>31
    ) >> (k & ~k>>31)
  ) & 1;
}

static int bitwise_and_logical(int x, int k)
{
  return ((k & ~31) && ~k>>31) || (x >> 31) || !(x << 1 >> (k & ~k>>31));
}

static int comparison(int x, int k)
{
  return k >= 32 || x < (k < 1 ? 1 : 1 << (k-1));
}

static int values[] = {
  INT_MIN, INT_MIN+1, INT_MIN/2-1, INT_MIN/2, INT_MIN/2+1, -99, -9, -3, -2, -1,
  0, 1, 2, 3, 9, 99, INT_MAX/2-1, INT_MAX/2, INT_MAX/2+1, INT_MAX-1, INT_MAX
};
#define LEN_VALUES (sizeof values / sizeof *values)

int main()
{
  for (int ik = 0; ik < LEN_VALUES; ik++)
  {
    for (int ix = 0; ix < LEN_VALUES; ix++)
    {
      int x = values[ix];
      int k = values[ik];
      int actual1 = purely_bitwise(x, k);
      int actual2 = bitwise_and_logical(x, k);
      int expected = comparison(x, k);
      if (actual1 != expected || actual2 != expected)
      {
        printf("x = %11d, k = %11d: expected = %d, actual = %d, %d\n",
          x, k, expected, actual1, actual2);
      }
    }
  }
  return 0;
}

The program outputs nothing, which is good, as every line of output would be a failed test case.

Ruud Helderman
  • 10,563
  • 1
  • 26
  • 45