Какой самый простой способ проверить, имеют ли два целых числа одинаковый знак? Есть ли какой-нибудь короткий побитовый трюк для этого?
Самый простой способ проверить, имеют ли два целых числа одинаковый знак?
Ответы (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);
}
Примечание. Поскольку мы используем исключающее или, мы хотим, чтобы левая и правая стороны были разными, когда знаки одинаковы, поэтому разные проверки против нуля.
Что не так с
return ((x‹0) == (y‹0));
?
-0===+0
, так что это не проблема там.
- person Christoph; 01.06.2017
(a ^ b) >= 0
будет оцениваться как 1, если знак тот же, иначе 0.
Я бы с осторожностью относился к любым побитовым трюкам для определения знака целых чисел, так как тогда вам придется делать предположения о том, как эти числа представлены внутри.
Почти в 100% случаев целые числа будут храниться как дополнение двух, но не рекомендуется делать предположения о внутреннем устройстве системы, если вы не используете тип данных, который гарантирует определенный формат хранения.
В комплименте двух вы можете просто проверить последний (самый левый) бит в целом числе, чтобы определить, является ли он отрицательным, поэтому вы можете сравнить только эти два бита. Это означало бы, что 0 будет иметь тот же знак, что и положительное число, что противоречит функции знака, реализованной в большинстве языков.
Лично я бы просто использовал функцию жестов выбранного вами языка. Маловероятно, что при таком расчете возникнут какие-либо проблемы с производительностью.
Предполагая 32-битные целые числа:
bool same = ((x ^ y) >> 31) != 1;
Чуть короче:
bool same = !((x ^ y) >> 31);
& >>
будет работать?
- person MD XF; 04.10.2017
(целое1 * целое2) > 0
Потому что, когда два целых числа имеют общий знак, результат умножения всегда будет положительным.
Вы также можете сделать его >= 0, если хотите рассматривать 0 как один и тот же знак, несмотря ни на что.
Я не совсем уверен, что считаю «побитовый трюк» и «самый простой» синонимами. Я вижу много ответов, которые предполагают 32-битные целые числа со знаком (хотя было бы глупо запрашивать беззнаковые); Я не уверен, что они применимы к значениям с плавающей запятой.
Кажется, что «самой простой» проверкой будет сравнение того, как оба значения сравниваются с 0; это довольно общее, если предположить, что типы можно сравнивать:
bool compare(T left, T right)
{
return (left < 0) == (right < 0);
}
Если знаки противоположны, вы получаете ложь. Если признаки совпадают, вы получаете истину.
Предполагая арифметику дополнения до двух (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);
}
Это может потребовать одной или двух дополнительных инструкций и занять немного больше времени.
Использование умножения — плохая идея, потому что оно уязвимо для переполнения.
if (x * y) > 0...
предполагая ненулевое значение и т.п.
В качестве технического примечания можно сказать, что побитовые решения будут намного эффективнее, чем умножение, даже на современных архитектурах. Вы экономите всего около 3 циклов, но вы знаете, что говорят о «сэкономленной копейке»…
Просто из головы...
int mask = 1 << 31;
(a & mask) ^ (b & mask) < 0;
безветвистая версия 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));
}
Для любого размера int с арифметикой дополнения до двух:
#define SIGNBIT (~((unsigned int)-1 >> 1))
if ((x & SIGNBIT) == (y & SIGNBIT))
// signs are the same
предполагая 32 бит
if(((x^y) & 0x80000000) == 0)
... ответ if(x*y>0)
неверный из-за переполнения
если (a*b ‹ 0) знак другой, иначе знак тот же (или a или b равен нулю)
Вспоминая свои студенческие годы, в большинстве машинных представлений, разве самый левый бит целого числа не равен 1, когда число отрицательное, и 0, когда оно положительное?
Я полагаю, что это скорее зависит от машины.
int same_sign = !( (x >> 31) ^ (y >> 31) );
если ( тот же_знак ) ... иначе ...
Лучше использовать std::signbit следующим образом:
std::signbit(firstNumber) == std::signbit(secondNumber);
Он также поддерживает другие основные типы (double
, float
, char
и т. д.).