Bitwise Arithmetic in C: Checking if a number is positive

0

x is an int,

I should be able to get the correct result when 0 is not involved. In attempts to account for the 0 case, I added "& x", which I believe now should return that a number is positive iff x > 0 and x is not 0 (because in c, any number other than 0 evaluates to true, correct?)

But, when running my tests, it says it has failed to evaulate 0x7fffffff as positive and I am not sure why!

Here is my code:

int mask = 0x1;
x = x >> 31;
int lsb = mask & x;
return ( (lsb) ^ 0x1) & (x) )

Edit: I have solved the problem by changing the code to the one below! Feedback is still very much appreciated, or any problems you may spot.

int mask = 0x1;
int lsb = (x >> 31) & mask;
int result = !(lsb ^ 0x1);
return !(result | !x);
c
math
bit-manipulation
asked on Stack Overflow Jun 12, 2018 by dan • edited Jun 13, 2018 by dan

5 Answers

1

https://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign

Technically, an int could be a different size on different machines; use of an C99 int32_t from inttypes.h may help with portability. It might not even be encoded in the format you expect, Are there any non-twos-complement implementations of C?.

The really portable easy way, is of course,

static int is_positive(const int a) {
    return a > 0;
}

The compiler will probably do a better job optimising it.

Edit: From comments, I came up with this; I tried to make it agnostic of the int-size. It is very much the same style as your own, checking whether the number is negative or zero, and inverting.

#include <stdio.h>  /* printf */
#include <limits.h> /* INT_ */
#include <assert.h> /* assert */

/** Assumes a is a 2's-compliment number
 and ~INT_MAX = 0b100..00, (checks if negative.) */
static int is_positive(const int a) {
    unsigned b = a;
    return !((b & ~INT_MAX) | !b);
}

static int is_really_positive(const int a) {
    return a > 0;
}

static void test(const int a) {
    printf("Number %d. Is positive %d.\n", a, is_positive(a));
    assert(is_positive(a) == is_really_positive(a));
}

int main(void) {
    test(INT_MIN);
    test(-2);
    test(-1);
    test(0);
    test(1);
    test(2);
    test(INT_MAX);
    return 0;
}

Also related, https://stackoverflow.com/a/3532331/2472827.

answered on Stack Overflow Jun 13, 2018 by Neil Edelman • edited Jun 13, 2018 by Neil Edelman
1

If you know the representation is 2's complement, then you can do:

#include <stdio.h>

#define IS_NEG(a)   (!!((1 << 31) & (a)))

int main(void)
{
    int n;

    while(1) {
        scanf("%d", &n);
        printf("negative: %d\n", IS_NEG(n));
    }
    return 0;
}

Explanation:

  1. (1 << 31) will take the number 1 and shift it 31 times to the left, thus giving you 1000 0000 0000 0000 0000 0000 0000 0000. If you don't want to use the shift, you could use 0x80000000 too.
  2. & (a) does a bitwise test with that big binary number. Since an AND operation only returns TRUE when both operands are TRUE, it follows that only if your number is negative (in 2's complement representation) that this will return TRUE.
  3. !!(...) This double negation accounts for the fact that when you do that bitwise AND, the returned value by the expression will be (1 << 31) if the number is really negative. So we invert it (giving us zero), than invert it again (giving us 1). Therefore, this ensures that we get a ZERO or a ONE as a final result.
  4. IS_NEG will return 0 on positive numbers AND 0, and returns 1 on all negative numbers.

Since the MSB will be a one when the number is negative, just test that bit. Note that this will only work for 32 bit integers (so you have to check that with a sizeof(int). The example returns 1 if a number is negative, but should be no problem reworking it to return 1 for positive numbers.

Let me know if this doesn't solve the problem. As I understand, you just want to test if any given int is positive/negative.


Edit: From the comments, I made a program to help you see what's going on.

#include <stdio.h>

#define IS_NEG(a)   (!!(0x80000000 & (a)))

char buf[65];
/* converts an integer @n to binary represention of @bits bits */
char *bin(int n, unsigned int bits)
{
    char *s = buf;
    for(bits = (1 << (bits - 1)); bits > 0; bits = bits >> 1)
        /* look! double negation again! Why this? :) */
        *s++ = !!(n & bits) + 48;

    *s = 0;
    return buf;
}

int main(void)
{
    /* R will be our partial result through-out the loop */
    int r, n;

    while(1) {
        /* get the number */
        scanf("%d", &n);

        /* this is the inner part of the macro
         * after this, we could say IS_NEG "becomes"
         * (!!(r))
         */
        r = n & 0x80000000;
        printf("n & 0x80000000: 0x%x\n", r);
        printf("  n = %s\n", bin(n, 32));
        printf("  r = %s\n", bin(r, 32));

        /* now we print what R is, so you see that the bitwise AND will
         * return 0x80000000 on negative numbers. It will also print
         * the NEGATION of R...
         * 
         * After the printf(), we just assign the negated value to R.
         */
        printf("r = 0x%x, !r = 0x%x\n", r, !r);
        r = !r;
        printf("  r = %s\n", bin(r, 32));
        /* After this, IS_NEG "becomes" (!(r)) */

        /* In the MACRO, this would be the second negation. */
        printf("r = 0x%x, !r = 0x%x\n", r, !r);
        r = !r;
        printf("  r = %s\n", bin(r, 32));

        /* Now, if R is 0, it means the number is either ZERO or 
         * POSITIVE.
         *
         * If R is 1, then the number is negative
         */
    }
    return 0;
}
answered on Stack Overflow Jun 13, 2018 by Enzo Ferber • edited Jun 13, 2018 by Enzo Ferber
1

Given the allowed operators from your comment, ! ~ & ^ | + << >>, edit: with the later constraint of no casts only the second alternative fits:

static int is_positive(unsigned x)
{
        return ~x+1 >> (CHAR_BIT*sizeof x-1);
}

Here's the deal: conversion to unsigned is very carefully specified in C: if the signed value is unrepresentable in the unsigned type, one plus the maximum value representable in the unsigned type is added (or subtracted, no idea what prompted them to include this possibility) to the incoming value until the result is representable.

So the result depends only on the incoming value, not its representation. -1 is converted to UINT_MAX, no matter what. This is correct, since the universe itself runs on twos-complement notation . That it also makes the conversion a simple no-op reinterpretation on most CPUs is just a bonus.

answered on Stack Overflow Jun 13, 2018 by jthill • edited Jun 17, 2018 by jthill
0

You can get a 32-bit wide zero-or-nonzero test using bitwise or and shifts as follows:

int t;
t = x | (x>>16);
t = t | (t >> 8);
t = t | (t >> 4);
t = t | (t >> 2)
t = (t | (t>>1)) & 1;

This sets t to the "OR" of the low 32 bits of x and will 0 if and only if the low 32 bits are all zero. If the int type is 32 bits or less, this will be equivalent to (x != 0). You can combine that with your sign bit test:

return t & (~x >> 31);
answered on Stack Overflow Jun 13, 2018 by Mike Housky
0

To check whether given number is positive or negative. As you mentioned x is an int & I assume its 32-bit long signed int. for e.g

int x = 0x7fffffff;

How above x represented in binary

 x  =>  0111 1111 | 1111 1111 | 1111 1111 | 1111 1111 
        |                                           |
        MSB                                         LSB

Now to check given number is positive or negative using bitwise opaertor, just find out the status of last(MSB or 31st(longint) or 15th(short int)) bit status whether it's 0 or 1, if last bit is found as 0 means given number is positive otherwise negative.

Now How to check last bit(31st) status ? Shift last(31st) bit to 0th bit and perform bitwise AND & operation with 1.

x     =>  0111 1111 | 1111 1111 | 1111 1111 | 1111 1111
x>>31 =>  0000 0000 | 0000 0000 | 0000 0000 | 0000 0000  
          --------------------------------------------- 
                                                      &
  1   =>  0000 0000 | 0000 0000 | 0000 0000 | 0000 0001
          ---------------------------------------------
          0000 0000 | 0000 0000 | 0000 0000 | 0000 0000 => its binary of zero( 0 ) so its a positive number

Now how to program above

static inline int sign_bit_check(int x) { 
        return (x>>31) & 1;
}

And call the sign_bit_check() like

int main(void) {
        int x = 0x7fffffff;
        int ret = sign_bit_check(x);
        if(ret) {
                printf("Negative\n");
        }
        else {
                printf("positive \n");
        }
        return 0;
}
answered on Stack Overflow Jun 13, 2018 by Achal • edited Jun 13, 2018 by Achal

User contributions licensed under CC BY-SA 3.0