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);
dan
  • 347
  • 2
  • 14
  • You can't test for a positive value with a single bit test; not with any of the standard methods of representing signed binary integers anyway. What your code in that image will do (assuming twos complement) is return 1 on a positive odd number, or 0 on any other positive number. – Mike Housky Jun 13 '18 at 00:17
  • @RoryDaulton done! – dan Jun 13 '18 at 00:21
  • Try that code on a positive even number, 42 for example. You'll compute 1 at the "positive sign" bit after inverting `lsb`; and then `1 & 42` returns a zero. – Mike Housky Jun 13 '18 at 00:24
  • @MikeHousky I thought (from lectures) that only 31 bits are used for magnitude and the last bit is used for the sign, so I used the right shift to move this to the very right so that could determine whether it is a 0 or 1, 0 being positive and 1 being negative. From there, I then used the exclusive or with 1, which would return 1 if lsb is 0 or 0 if lsb is 1. So at this point, it is 1 if x is positive and 0 if x is negative, correct? Now I add the & x in order to take care of the 0 case. At least, this is the logic I have in my mind, where have I gone wrong? – dan Jun 13 '18 at 00:25
  • 1
    Never mind; you modified x in the process of computing lsb. The final value of x is just the non-inverted sign bit of the original number and lsb is the inverted value of that bit. Your result is *always* zero. – Mike Housky Jun 13 '18 at 00:27
  • @MikeHousky I was also under the impression that 1 & 42 should return 1 in C? Is it not the case that any number that is not zero is true? – dan Jun 13 '18 at 00:28
  • "Positive" means "greater than zero", and in most formats both zero and positive values have 0 as a sign bit. – Mike Housky Jun 13 '18 at 00:30
  • 1
    That the definition of &&, not bitwise &. – Mike Housky Jun 13 '18 at 00:31
  • If you're allowed to use && (returns 1 if both operands are nonzero, or 0 otherwise) then simply using `return x && (x>>31);` will get your positive test done in one statement. But && is not a bitwise operation. – Mike Housky Jun 13 '18 at 00:33
  • @MikeHousky Ah... Ok, yes && is not an acceptable operation in this case for the assignment, so I will have to figure out either a completely new process or some other way to account for x being 0. It is my first day so most problems are very challenging to me, sorry for the trouble! I had seen a solution for checking if any integer x is zero which simply returned !x and that is why I was under the impression that 0 --> false, all other integers --> true. – dan Jun 13 '18 at 00:38
  • Xor-invert and not-invert, gives you the same, `lsb == result`. This could be simplified (I think) to `return !((x >> 31) | !x)`, but this invokes undefined, (er, implementation-defined,) behaviour if not cast, https://stackoverflow.com/a/4009954/2472827. – Neil Jun 13 '18 at 22:11
  • If `x` is a `signed int` - it ought to be ;) - shifting it right is implementation specific if `x` happens to become negative. – Michael Beer Jun 16 '18 at 00:58

5 Answers5

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.

Neil
  • 1,767
  • 2
  • 16
  • 22
  • 1
    For these particular problems, we are given restrictions on the operators we may use, the legal ones being: ! ~ & ^ | + << >> – dan Jun 13 '18 at 00:40
  • Are you specifically on a machine where `int` is 32 bits and two's-complement and you are allowed to use the do casting? – Neil Jun 13 '18 at 01:22
  • In which case, one would probably start by casting `int` to `unsigned` for ease of bit manipulation. `and` it with -1 cast to unsigned and take the negation. – Neil Jun 13 '18 at 01:30
  • 1
    No casting I believe, but I have since posting solved the problem by returning !(result | !x); Yes, the machine we are testing under should be int at 32 bits and two's complement. No casting of any form is allowed for the solutions however. – dan Jun 13 '18 at 01:46
  • One can use `INT_MAX` in `limits.h` to get bit-length agnostic tests, but I'm not sure how portable it is. I'd be sure to test the edge cases and parity flips: `INT_MIN, -2, -1, 0, 1, 2, INT_MAX`. – Neil Jun 13 '18 at 03:05
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;
}
Enzo Ferber
  • 3,029
  • 1
  • 14
  • 24
  • Hello, I was giving your solution some thought because it looked interesting and I wanted to understand it; I think (if you don't mind), it would help me to see what is happening to the bits as we are manipulating it through the code, more specifically what is going on during the double negation. I also have a few questions: Is it not that the 0x80000000 & a returns 0x80000000 if it is negative? What exactly do you do with this after? And so this result is true because it is non-zero correct when you use the ! operator, correct? – dan Jun 13 '18 at 02:07
  • Also, your method may have given me an idea for another solution; roughly speaking, I would start with 0x80000000 and compare x with this, returning 0x80000000 again if it is negative. I would then shift it >> 31 (call this b), then use a int 0x1 and the & operator to determine the value of the shifted bit. If b & 0x1 returns 1, the original value was negative and if it returns 0 then i know it was positive. But I guess this still runs into the issue of when the value is zero; for this assignment, positive is x > 0, so 0 should actually return the same value as what a negative value would. – dan Jun 13 '18 at 02:10
  • @dan You are right about the `0x80000000` returned by the bitwise-and. To overcome that fact, I double negate the result. So, what is `!0x80000000`? It is `0`. Than, that result gets negated again, and what's the result of `!0`? It's `1`. And this also accounts for the zero case. If you negate zero twice, what do you get? `!0 = 1`, and then `!1 = 0`. Note that the zero case and the positive case are the same thing, since the bitwise-and will return zero on positive numbers and zero. All clear? – Enzo Ferber Jun 13 '18 at 11:13
  • @dan I added a code to the answer to help you visualize what is happening. Tell me if that helps or if you have any other doubts. – Enzo Ferber Jun 13 '18 at 11:32
  • That's the most elegant answer given the assumptions that its 32 bits and two's complement wer are dealing with - I actually don't really understand why other answers fiddle around with shifting back and forth when it all boils down to checking if the MSB is set or not...? – Michael Beer Jun 16 '18 at 01:16
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.

jthill
  • 55,082
  • 5
  • 77
  • 137
  • Or just the other way around, test is_negative with `return x & (1U<<(CHAR_BIT*sizeof x-1))`, which hides the unsigned conversion (it is implicit). – aka.nice Jun 13 '18 at 16:14
  • @MichaelBeer Not a rhetorical question: so what? Is there some representation and value for which this gives a wrong result? I might be about to scrape egg off my face, but high bit zero, some nonzero other bits defines nonzero positive in every value representation I know of and I think this detects them all and no others. – jthill Jun 16 '18 at 01:45
  • @jthill I just wonder... reading 6.2.6.2 of the c11 standard, I can't currently even see that it is stating that the sign bit needs to be the MSB? – Michael Beer Jun 16 '18 at 02:09
  • @MichaelBeer Again: so what? Instead of spouting yet another random paragraph, how about answering the question: so, what? Work out the actual consequences. – jthill Jun 16 '18 at 02:45
  • I did not want to bash your answer, I was just keen to know what I asked. If I apply your first function onto a 16 bit/ 2 byte signed magnitude encoded int (sign bit MSB) the function seems to fail - it returns true if the bit is set? Hence my question: Did you design then for all allowed encodings? Did I just misapply your function? – Michael Beer Jun 16 '18 at 10:03
  • The root problem I wonder about is: Why do you bother coing up with a clumsy way like this? As you said, (and if I understand correctly): In all 3 representations allowed the sign bit determines the wether the int is negative or not. And if you assume that the sign bit always resides at a specific position (the MSB ?), it all boils down to check for that particular bit being set. Why not just do `return x & (1 << (CHAR_BIT * sizeof(x) - 1))` ? You would also circumvent the necessity of casting the `x` because you wont shift it righ, right? – Michael Beer Jun 16 '18 at 10:47
  • ... Forgot the `!`. – Michael Beer Jun 16 '18 at 10:55
  • @MichaelBeer your method fails on zero. – jthill Jun 16 '18 at 11:26
  • If you think so... The entire point I was interested in were the question I posed. Ostensibly I fail in explain my question to you. Sorry. – Michael Beer Jun 16 '18 at 11:32
  • If you have a question, there's this website built for getting answers. Your questions are utterly irrelevant to _this_ answer to _this_ question. Please, stop the ignorant posturing. – jthill Jun 16 '18 at 12:47
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);
Mike Housky
  • 3,959
  • 1
  • 17
  • 31
  • I will make sure to go over this solution! I usually have to write it all out and trace the code to make sense of it all. Thank You! One more note, although not really that important (and I have since our previous exchanges solved the problem), we are restricted to only 8 total operators to solve the problem. My solution was to return !(result | !x); it appears to pass all the tests as of now, and I got full marks for the function. – dan Jun 13 '18 at 01:42
  • @dan Careful. That ! operator isn't a bitwise operator either. (...but it will give you a "not zero" test.) – Mike Housky Jun 13 '18 at 03:34
  • Thanks, I had the operators mixed up; it is one of the logical operators that were allowed for the problem. I'll make sure to review up on exactly which operators are logical and which are bitwise so I don't mix them up in the future. – dan Jun 13 '18 at 06:04
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;
}
Achal
  • 11,821
  • 2
  • 15
  • 37
  • Hi, so in your solution, is the case where x is 0 accounted for? As this should not return that it is positive for my assignment. Also, we are restricted to only bit manipulation, meaning we cannot use the if-else statement that you included at the end! I have however, since my original post, found a solution that uses all that I had originally written, however my return statement now makes use of the | operator instead. – dan Jun 13 '18 at 01:37
  • Well then @dan -- it looks like you have some further work to do on *your assignment*. – David C. Rankin Jun 13 '18 at 02:06
  • @DavidC.Rankin Indeed, I am really now just exploring the other suggestions and see what techniques people use, I have already moved on to the next problem! :) – dan Jun 13 '18 at 02:12