Самый простой способ проверить, имеют ли два целых числа одинаковый знак?
-
09-06-2019 - |
Вопрос
Какой самый простой способ проверить, имеют ли два целых числа одинаковый знак?Есть ли какой-нибудь короткий побитовый трюк для этого?
Решение
Вот версия, работающая на 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));
?
(a ^ b) >= 0
будет иметь значение 1, если знак тот же, и 0 в противном случае.
Я бы с осторожностью относился к любым побитовым трюкам для определения знака целых чисел, поскольку тогда вам придется делать предположения о том, как эти числа представляются внутри.
Почти в 100% случаев целые числа будут храниться как два комплимента, но не рекомендуется делать предположения о внутреннем устройстве системы, если только вы не используете тип данных, гарантирующий определенный формат хранения.
В комплименте двух вы можете просто проверить последний (крайний левый) бит целого числа, чтобы определить, является ли оно отрицательным, поэтому вы можете сравнить только эти два бита.Это означало бы, что 0 будет иметь тот же знак, что и положительное число, что противоречит знаковой функции, реализованной в большинстве языков.
Лично я бы просто использовал функцию жестов выбранного вами языка.Маловероятно, что при таком расчете возникнут какие-либо проблемы с производительностью.
Предполагая 32-битные целые числа:
bool same = ((x ^ y) >> 31) != 1;
Чуть более лаконично:
bool same = !((x ^ y) >> 31);
Я не совсем уверен, что считаю слова «побитовый трюк» и «простейший» синонимами.Я вижу много ответов, которые предполагают 32-битные целые числа со знаком (хотя это бы глупо просить без подписи);Я не уверен, что они применимы к значениям с плавающей запятой.
Кажется, что «самой простой» проверкой было бы сравнить, как оба значения сравниваются с 0;это довольно общий пример, если предположить, что типы можно сравнивать:
bool compare(T left, T right)
{
return (left < 0) == (right < 0);
}
Если знаки противоположны, то получается ложь.Если признаки совпадают, значит, вы правы.
(целое число1 * целое число2) > 0
Потому что, когда два целых числа имеют общий знак, результат умножения всегда будет положительным.
Вы также можете сделать его >= 0, если хотите рассматривать 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);
}
Это может потребовать одной или двух дополнительных инструкций и занять немного больше времени.
Использование умножения — плохая идея, поскольку оно уязвимо к переполнению.
если (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));
}
Шаблон C++ для целочисленных типов:
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(firstNumber) == std::signbit(secondNumber);
Он также поддерживает другие базовые типы (double
, float
, char
и т. д).