Побитовая арифметика в C: проверка, является ли число положительным - PullRequest
0 голосов
/ 13 июня 2018

x - это int,

Я должен быть в состоянии получить правильный результат, когда 0 не задействован.В попытках учесть случай 0 я добавил «& x», который, я считаю, теперь должен возвращать, что число положительное, если x> 0 и x не равно 0 (потому что в c любое число, кроме 0, оценивается как true,правильно?)

Но при запуске моих тестов он говорит, что не смог оценить 0x7fffffff как положительный, и я не уверен, почему!

Вот мой код:

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

Редактировать: я решил проблему, изменив код на приведенный ниже!Обратная связь все еще очень ценится, или любые проблемы, которые вы можете заметить.

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

Ответы [ 5 ]

0 голосов
/ 13 июня 2018

Учитывая разрешенные операторы из вашего комментария, ! ~ & ^ | + << >>, отредактируйте: с более поздним ограничением отсутствия бросков подходит только вторая альтернатива:

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

Вот предложение: преобразование в unsigned очень тщательно определенов C: если подписанное значение непредставимо в неподписанном типе, к входящему значению добавляется единица плюс максимальное значение, представляемое в неподписанном типе (или вычитается, не зная, что побудило их включить эту возможность), до результата является представимым.

Таким образом, результат зависит только от входящего значения, а не от его представления.-1 преобразуется в UINT_MAX, несмотря ни на что.Это правильно, так как сама вселенная работает в нотации двойного дополнения .То, что это также делает преобразование простой неинтерпретацией на большинстве процессоров, является просто бонусом.

0 голосов
/ 13 июня 2018

Чтобы проверить, является ли данное число положительным или отрицательным.Как вы упомянули, x - это int, и я предполагаю, что его 32-битное длинное со знаком int.например,

int x = 0x7fffffff;

Как указано выше x, представленное в двоичном виде

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

Теперь, чтобы проверить, является ли данное число положительным или отрицательным с помощью побитового оператора, просто выясните состояние последнего (MSBили 31-й (longint) или 15-й (short int)) статус бита, будь то 0 или 1, если последний бит найден как 0 означает, что данное число положительно, иначе отрицательно.

Теперь Какпроверить статус последнего бита (31-го)?Сдвиньте последний (31-й) бит до 0th бита и выполните побитовое И & операцию с 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

Теперь, как запрограммировать выше

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

И вызовите sign_bit_check() как

int main(void) {
        int x = 0x7fffffff;
        int ret = sign_bit_check(x);
        if(ret) {
                printf("Negative\n");
        }
        else {
                printf("positive \n");
        }
        return 0;
}
0 голосов
/ 13 июня 2018

Если вы знаете, что представление является 2 дополнением , то вы можете сделать:

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

Объяснение:

  1. (1 << 31) примет число1 и сдвиньте его 31 раз влево, что даст вам 1000 0000 0000 0000 0000 0000 0000 0000.Если вы не хотите использовать сдвиг, вы также можете использовать 0x80000000.
  2. & (a) выполняет побитовый тест с этим большим двоичным числом.Поскольку операция И возвращает TRUE только тогда, когда оба операнда TRUE, из этого следует, что только если ваше число отрицательное ( в дополнительном представлении 2 * ), это вернет TRUE.
  3. !!(...) Это двойное отрицание объясняет тот факт, что при выполнении этого побитового И возвращаемое значение по выражению будет (1 << 31), если число действительно отрицательно.Таким образом, мы инвертируем это (давая нам ноль), чем инвертируем это снова (давая нам 1).Следовательно, это гарантирует, что мы получим НОЛЬ или ЕДИНИЦУ в качестве конечного результата.
  4. IS_NEG вернет 0 для положительных чисел И 0 и вернет 1 для всех отрицательных чисел.

Поскольку MSB будет равен единице, когда число отрицательное, просто проверьте этот бит.Обратите внимание, что это будет работать только для 32-битных целых чисел (так что вы должны проверить это с sizeof(int). В примере возвращается 1, если число равно отрицательно , но не должно возникнуть никаких проблем, переработав его, чтобы вернуть 1 дляположительные числа.

Дайте мне знать, если это не решит проблему. Как я понимаю, вы просто хотите проверить, является ли любой заданный int положительным / отрицательным.


Редактировать : Из комментариев я сделал программу, которая поможет вам увидеть, что происходит.
#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;
}
0 голосов
/ 13 июня 2018

Вы можете получить 32-битный нулевой или ненулевой тест шириной, используя побитовое или и сдвиг следующим образом:

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

Это устанавливает t в "ИЛИ" младших 32 битовх и будет 0 тогда и только тогда, когда младшие 32 бита равны нулю.Если тип int равен 32 битам или меньше, это будет эквивалентно (x != 0).Вы можете комбинировать это с тестом знака бит:

return t & (~x >> 31);
0 голосов
/ 13 июня 2018

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

Технически, int может иметь разные размеры на разных машинах;использование C99 int32_t от inttypes.h может помочь с переносимостью.Возможно, он даже не будет закодирован в ожидаемом вами формате, Существуют ли какие-либо реализации, не являющиеся дополнением к двум, C? .

Действительно простой способ переноса, конечно,

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

Компилятор, вероятно, будет лучше оптимизировать его.

Редактировать: Из комментариев я придумал это;Я пытался сделать его агностиком размером int.Это очень похоже на ваш собственный, проверяя, является ли число отрицательным или нулевым, и инвертируя.

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

Также связано, https://stackoverflow.com/a/3532331/2472827.

...