2010-09-23 2 views
25

Я могу проверить, является ли число нечетным/четным с помощью побитовых операторов. Могу ли я проверить, является ли число положительным/нулевым/отрицательным без использования каких-либо условных операторов/операторов, например if/ternary и т. Д.Проверка того, является ли число положительным или отрицательным с помощью побитовых операторов

Можно ли это сделать с помощью побитовых операторов и некоторого трюка на C или на C++?

+3

Как этот «код-гольф» связан? – meagar

+0

Когда вы проверяете нечетность/даже вы можете выполнить побитную проверку, чтобы исключить деление (в случае, если ваш компилятор настолько тупой). Но ** почему ** проверить знак таким образом ??? – ybungalobill

+2

Укажите, пожалуйста, формат номера. Целые? Плавает? Какие целые числа/поплавки? Два дополнения? Знак + Magnitude? IEEE-754? Быть конкретной. – sellibitze

ответ

14

Если высокий бит установлен на целое число со знаком (байт, длинный и т. Д., Но не число с плавающей запятой), это число отрицательно.

int x = -2300; // assuming a 32-bit int 

if ((x & 0x80000000) != 0) 
{ 
    // number is negative 
} 

ДОБАВЛЕНО:

Вы сказали, что вы не хотите использовать какие-либо условными. Я полагаю, вы могли бы сделать это:

int isNegative = (x & 0x80000000); 

И через некоторое время вы можете проверить его с if (isNegative).

+5

Числа с плавающей запятой в настоящее время, как правило, находятся в формате IEEE, который имеет явный бит знака. –

+0

@ Давид: Я этого не знал. Вы знаете, какой бит является битом знака? –

+0

@ Jim Mischel: согласно этой странице: http://en.wikipedia.org/wiki/Double_precision_floating-point_format, это также высокий бит. –

24

Могу ли я проверить, является ли число положительное/нулевое/отрицательное без использования каких-либо условных операторов/операторов, как если/тройная и т.д.

Конечно:

bool is_positive = number > 0; 
bool is_negative = number < 0; 
bool is_zero = number == 0; 
+17

Ну, я думаю, он спрашивает о побитовых операторах ... –

+8

На самом деле это единственный независимый от платформы способ. Более того, он будет более эффективен, чем побитовая проверка на ** всех ** платформах. – ybungalobill

+0

Фактически там * есть * другой независимый от платформы способ: 'signbit()'. См. Мой ответ http://stackoverflow.com/a/19279266/72176 для получения дополнительной информации. –

3

Подписанные целых и плавающие точки обычно используют самый старший бит для хранения знака, поэтому, если вы знаете размер, вы можете извлечь информацию из наиболее значимого бита.

В целом это мало пользы в этом, поскольку для использования этой информации необходимо будет провести какое-то сравнение, и для процессора так же легко проверить, что-то отрицательное, поскольку оно проверяет, является ли оно не равен нулю. Если факт на процессорах ARM, проверка наиболее значимого бита будет, как правило, БОЛЬШЕ дорогой, чем проверка того, является ли она отрицательной.

0

Вы можете отличить отрицательный/неотрицательный, посмотрев на самый значительный бит. Во всех представлениях для целых чисел со знаком этот бит будет установлен в 1, если число отрицательно.

Существует ни один тест не различать между нулем и положительными, для непосредственного испытания, за исключением против 0.

Для проверки отрицательный, вы можете использовать

#define IS_NEGATIVE(x) ((x) & (1U << ((sizeof(x)*CHAR_BIT)-1))) 
+0

Размер и ширина целочисленного типа не имеют строгого отношения в том смысле, который вы предлагаете. Во-вторых, самый старший бит типа unsigned не обязательно является битом знака: ширина соответствующего подписанного типа может быть меньше или может быть даже больше, чем тип unsigned. –

+0

@Jens: Педантично, вы правы, и невозможно определить, какой бит содержит знак. Но можете ли вы назвать платформу, где этот код не работает? –

+0

Они редки, признаюсь, но существуют. К сожалению, на немецком языке, но вы можете получить эту идею: http://www.schellong.de/c_padding_bits.htm. Также в этом обсуждении есть интересный пример: http://www.rhinocerus.net/forum/lang-c/2007-padding-bits.html –

11

Существует подробное обсуждение на Bit Twiddling Hacks page ,

int v;  // we want to find the sign of v 
int sign; // the result goes here 

// CHAR_BIT is the number of bits per byte (normally 8). 
sign = -(v < 0); // if v < 0 then -1, else 0. 
// or, to avoid branching on CPUs with flag registers (IA32): 
sign = -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1)); 
// or, for one less instruction (but not portable): 
sign = v >> (sizeof(int) * CHAR_BIT - 1); 

// The last expression above evaluates to sign = v >> 31 for 32-bit integers. 
// This is one operation faster than the obvious way, sign = -(v < 0). This 
// trick works because when signed integers are shifted right, the value of the 
// far left bit is copied to the other bits. The far left bit is 1 when the value 
// is negative and 0 otherwise; all 1 bits gives -1. Unfortunately, this behavior 
// is architecture-specific. 

// Alternatively, if you prefer the result be either -1 or +1, then use: 

sign = +1 | (v >> (sizeof(int) * CHAR_BIT - 1)); // if v < 0 then -1, else +1 

// On the other hand, if you prefer the result be either -1, 0, or +1, then use: 

sign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1)); 
// Or, for more speed but less portability: 
sign = (v != 0) | (v >> (sizeof(int) * CHAR_BIT - 1)); // -1, 0, or +1 
// Or, for portability, brevity, and (perhaps) speed: 
sign = (v > 0) - (v < 0); // -1, 0, or +1 

// If instead you want to know if something is non-negative, resulting in +1 
// or else 0, then use: 

sign = 1^((unsigned int)v >> (sizeof(int) * CHAR_BIT - 1)); // if v < 0 then 0, else 1 

// Caveat: On March 7, 2003, Angus Duggan pointed out that the 1989 ANSI C 
// specification leaves the result of signed right-shift implementation-defined, 
// so on some systems this hack might not work. For greater portability, Toby 
// Speight suggested on September 28, 2005 that CHAR_BIT be used here and 
// throughout rather than assuming bytes were 8 bits long. Angus recommended 
// the more portable versions above, involving casting on March 4, 2006. 
// Rohit Garg suggested the version for non-negative integers on September 12, 2009. 
1

Это не может быть сделано переносимым способом с битовыми операциями в С. Представления для знакопеременных целочисленных типов, что стандарт позволяет может быть гораздо страннее, чем вы могли бы подозревать. В частности, значение с битом знака включено и в противном случае нулевое значение не обязательно должно быть допустимым значением для подписанного типа или неподписанного типа, а так называемым ловушечным представлением для обоих типов.

Все вычисления с битовыми операторами, которые вы можете таким образом сделать, могут иметь результат, который приводит к неопределенному поведению.


В любом случае, как некоторые другие ответы предположить, что это на самом деле не нужно, и сравнение с < или > должно хватить в любом практическом контексте, является более эффективным, более удобным для чтения ... так просто это сделать сюда.

0

Предположим, ваш номер a=10 (положительный). Если вы сдвинете aa раз, он даст нуль.

т.е.

10>>10 == 0 

Таким образом, вы можете проверить, если число положительное, но в случае a=-10 (отрицательный):

-10>>-10 == -1 

Таким образом, вы можете объединить тех, кто в if:

if(!(a>>a)) 
    print number is positive 
else 
    print no. is negative 
+1

'-10 >> -10', я считаю, я буду -formed, а '-10 >> 10' - это реализация. –

+1

Отрицательный сдвиг имеет * неопределенное * поведение в C. И если a больше ширины int (или типа данных a), это также неопределенное поведение. –

4
#include<stdio.h> 

void main() 
{ 
    int n; // assuming int to be 32 bit long 

    //shift it right 31 times so that MSB comes to LSB's position 
    //and then and it with 0x1 
    if ((n>>31) & 0x1 == 1) { 
     printf("negative number\n"); 
    } else { 
     printf("positive number\n"); 
    } 

    getch(); 
} 
2
// if (x < 0) return -1 
// else if (x == 0) return 0 
// else return 1 
int sign(int x) { 
    // x_is_not_zero = 0 if x is 0 else x_is_not_zero = 1 
    int x_is_not_zero = ((x | (~x + 1)) >> 31) & 0x1; 
    return (x & 0x01 << 31) >> 31 | x_is_not_zero; // for minux x, don't care the last operand 
} 

Вот именно то, что вы waht!

3

Это довольно просто

Это можно легко сделать с помощью

return ((!!x) | (x >> 31)); 

возвращает

  • 1 для положительного числа,
  • -1 для отрицательного, и
  • 0 за ноль
+0

Предполагая 32-битные целые числа :) – AndyG

+0

@AndyG 'return ((!! x) | (x >> (8 * sizeof (int) -1)));' будет лучшим способом, я думаю. – noufal

+0

Когда я оценил это выражение для отрицательных значений x, он вернул 1 вместо -1. Можете ли вы исправить мою ошибку: если x = -ve, то (! X) = 0 => (!! x) = 1; x >> 31 = 1; Поэтому ((! X) | (x >> 31)) = (1) | (1) = (1) – piyukr

7

Или вы можете использовать signbit(), и работа выполнена для вас.

Я предполагаю, что под капотом реализация math.h - эффективная побитовая проверка (возможно, решение вашей исходной цели).

Ссылка: http://en.cppreference.com/w/cpp/numeric/math/signbit

0

Когда вы будете уверены, что о размере целого (предполагая, что 16-разрядное целое):

bool is_negative = (unsigned) signed_int_value >> 15; 

Когда вы не уверены в размере целых чисел:

bool is_negative = (unsigned) signed_int_value >> (sizeof(int)*8)-1; //where 8 is bits 

Ключевое слово unsigned не является обязательным.

1
if((num>>sizeof(int)*8 - 1) == 0) 
    // number is positive 
else 
    // number is negative 

Если значение равно 0, то число положительное еще отрицательное

1

Более простой способ выяснить, является ли число положительным или отрицательным: Пусть число будет х проверка, если [х * (-1)]> x. если истина x отрицательна, то положительная.

0

Это обновление, связанное с C++ 11 для этого старого вопроса.Также стоит рассмотреть std::signbit.

На Compiler проводнике с помощью GCC 7.3 64bit с -O3 оптимизации, этот код

bool s1(double d) 
{ 
    return d < 0.0; 
} 

генерирует

s1(double): 
    pxor xmm1, xmm1 
    ucomisd xmm1, xmm0 
    seta al 
    ret 

И этот код

bool s2(double d) 
{ 
    return std::signbit(d); 
} 

генерирует

s2(double): 
    movmskpd eax, xmm0 
    and eax, 1 
    ret 

Вам понадобится профиль, чтобы обеспечить разницу в скорости, но версия signbit использует 1 меньше кода операции.

Смежные вопросы