Самый простой способ проверить, имеют ли два целых числа одинаковый знак?

Какой самый простой способ проверить, имеют ли два целых числа одинаковый знак? Есть ли какой-нибудь короткий побитовый трюк для этого?


person Community    schedule 15.09.2008    source источник


Ответы (18)


Вот версия, которая работает в C/C++, которая не зависит от целочисленных размеров или имеет проблему переполнения (т.е. x*y>=0 не работает)

bool SameSign(int x, int y)
{
    return (x >= 0) ^ (y < 0);
}

Конечно, вы можете выдумать и шаблон:

template <typename valueType>
bool SameSign(typename valueType x, typename valueType y)
{
    return (x >= 0) ^ (y < 0);
}

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

person Torlack    schedule 15.09.2008
comment
Довольно мило, хакер C/C++ во мне полностью поддерживает этот фрагмент кода. Инженер-программист во мне задается вопросом, почему пользователь должен знать это в такой общей форме! - person user7116; 16.09.2008
comment
Разве это не терпит неудачу, если x=0 и y›0? - person Weckar E.; 11.05.2017

Что не так с
return ((x‹0) == (y‹0));
?

person Rik    schedule 15.09.2008
comment
Гм... Ничего... Печально, что мы все упустили простое решение. - person Torlack; 16.09.2008
comment
Как насчет подписанного нуля. -0,0 против нуля без знака 0,0 - person Ólafur Waage; 14.05.2011
comment
@Ólafur: OP указывает целые числа, а не числа с плавающей запятой. - person Nicholas Knight; 02.06.2011
comment
Это предполагает, что вы хотите рассматривать 0 как тот же знак, что и положительное целое число. - person SpoonMeiser; 03.03.2016
comment
@ÓlafurWaage По крайней мере, в JavaScript верно следующее -0===+0, так что это не проблема там. - person Christoph; 01.06.2017
comment
Никогда не соглашайтесь на принятый ответ! Будьте всегда любопытны! Может быть, пришло время для ТАК пересмотреть истинное значение принятого ответа. SO должен изменить истинное значение принятого ответа, чтобы наиболее релевантные из них плавали вверху. В противном случае большинство ленивых гиков, таких как я, даже не удосуживаются просмотреть альтернативы. - person taurus05; 27.04.2021

(a ^ b) >= 0

будет оцениваться как 1, если знак тот же, иначе 0.

person Community    schedule 15.09.2008
comment
О, классно! :-) Я удивлен, что пропустил это. Самое приятное в этом решении то, что оно не зависит от конкретной мощности битов в базовом целочисленном представлении. - person Daniel Spiewak; 16.09.2008
comment
В результате получается восхитительно компактный xorl %edi, %esi; устанавливает %al на x86, всего 6 байт и две инструкции. Это также интересный пример, поскольку это конкретный случай, когда возврат 'bool' вместо int дает значительно лучший код. - person John Meacham; 06.07.2014
comment
@JohnMeacham: интересно, что вы подразумеваете под «это конкретный случай, когда возврат 'bool' вместо int дает значительно лучший код» - person MK.; 19.05.2016

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

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

В комплименте двух вы можете просто проверить последний (самый левый) бит в целом числе, чтобы определить, является ли он отрицательным, поэтому вы можете сравнить только эти два бита. Это означало бы, что 0 будет иметь тот же знак, что и положительное число, что противоречит функции знака, реализованной в большинстве языков.

Лично я бы просто использовал функцию жестов выбранного вами языка. Маловероятно, что при таком расчете возникнут какие-либо проблемы с производительностью.

person SpoonMeiser    schedule 15.09.2008
comment
Стандартная библиотека C предоставляет функцию signbit(). Вероятно, трудно победить оптимизатор любыми побитовыми трюками собственного изобретения. - person ; 16.09.2008

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

bool same = ((x ^ y) >> 31) != 1;

Чуть короче:

bool same = !((x ^ y) >> 31);
person Patrick    schedule 15.09.2008
comment
Этим двум примерам кода ВСЕГДА ВСЕГДА должен предшествовать комментарий кода (в реальной жизни) - person Jorge Córdoba; 16.09.2008
comment
О Конечно. В реальной жизни я бы, вероятно, использовал что-то вроде same = Math.Sign(x) == Math.Sign(y). Я просто даю злые побитовые решения, когда люди просят их. :D - person Patrick; 16.09.2008
comment
Гм, это недействительный код... как вы ожидаете, что & >> будет работать? - person MD XF; 04.10.2017
comment
Это не так. Я понятия не имею, что я думал об этом и подписал 10 лет назад. - person Patrick; 04.10.2017

(целое1 * целое2) > 0

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

Вы также можете сделать его >= 0, если хотите рассматривать 0 как один и тот же знак, несмотря ни на что.

person Benjamin Autin    schedule 15.09.2008
comment
Если одно из целых чисел равно 0, то у вас проблема. - person TatiOverflow; 10.12.2016
comment
0 не является ни отрицательным, ни положительным, поэтому технически это не один и тот же знак. - person Benjamin Autin; 12.12.2016

Я не совсем уверен, что считаю «побитовый трюк» и «самый простой» синонимами. Я вижу много ответов, которые предполагают 32-битные целые числа со знаком (хотя было бы глупо запрашивать беззнаковые); Я не уверен, что они применимы к значениям с плавающей запятой.

Кажется, что «самой простой» проверкой будет сравнение того, как оба значения сравниваются с 0; это довольно общее, если предположить, что типы можно сравнивать:

bool compare(T left, T right)
{
    return (left < 0) == (right < 0);
}

Если знаки противоположны, вы получаете ложь. Если признаки совпадают, вы получаете истину.

person OwenP    schedule 15.09.2008
comment
false && false == false Боюсь, вам нужно будет сделать это отрицанием XOR, чтобы сделать его правильным. - person Daniel Spiewak; 16.09.2008

Предполагая арифметику дополнения до двух (http://en.wikipedia.org/wiki/Two_complement):

inline bool same_sign(int x, int y) {
    return (x^y) >= 0;
}

Это может занять всего две инструкции и менее 1 нс на современном процессоре с оптимизацией.

Не предполагая арифметику дополнения до двух:

inline bool same_sign(int x, int y) {
    return (x<0) == (y<0);
}

Это может потребовать одной или двух дополнительных инструкций и занять немного больше времени.

Использование умножения — плохая идея, потому что оно уязвимо для переполнения.

person user10315    schedule 15.09.2008
comment
@matias Как и должно быть. 0 и 1 имеют одинаковый знак. Целые числа не имеют отрицательного 0. - person Cody Gray; 14.08.2014

if (x * y) > 0...

предполагая ненулевое значение и т.п.

person Yes - that Jake.    schedule 15.09.2008

В качестве технического примечания можно сказать, что побитовые решения будут намного эффективнее, чем умножение, даже на современных архитектурах. Вы экономите всего около 3 циклов, но вы знаете, что говорят о «сэкономленной копейке»…

person Daniel Spiewak    schedule 15.09.2008
comment
сэкономленная копейка является корнем всех зол, скажем, в 97% случаев? - person ysth; 16.08.2009

Просто из головы...

int mask = 1 << 31;
(a & mask) ^ (b & mask) < 0;
person Daniel Spiewak    schedule 15.09.2008
comment
работает только для 32-битных целых чисел, что не было указано в вопросе - person Larry Gritz; 17.08.2009

безветвистая версия C:

int sameSign(int a, int b) {
    return ~(a^b) & (1<<(sizeof(int)*8-1));
}

Шаблон С++ для целочисленных типов:

template <typename T> T sameSign(T a, T b) {
    return ~(a^b) & (1<<(sizeof(T)*8-1));
}
person CAFxX    schedule 02.06.2011

Для любого размера int с арифметикой дополнения до двух:

#define SIGNBIT (~((unsigned int)-1 >> 1))
if ((x & SIGNBIT) == (y & SIGNBIT))
    // signs are the same
person Mark Ransom    schedule 15.09.2008

предполагая 32 бит

if(((x^y) & 0x80000000) == 0)

... ответ if(x*y>0) неверный из-за переполнения

person ugasoft    schedule 15.09.2008

если (a*b ‹ 0) знак другой, иначе знак тот же (или a или b равен нулю)

person Community    schedule 15.09.2008

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

Я полагаю, что это скорее зависит от машины.

person Dana    schedule 15.09.2008

int same_sign = !( (x >> 31) ^ (y >> 31) );

если ( тот же_знак ) ... иначе ...

person Community    schedule 15.09.2008

Лучше использовать std::signbit следующим образом:

std::signbit(firstNumber) == std::signbit(secondNumber);

Он также поддерживает другие основные типы (double, float, char и т. д.).

person ashiquzzaman33    schedule 01.12.2015