A maneira mais simples de verificar se dois números inteiros têm o mesmo sinal?
-
09-06-2019 - |
Pergunta
Qual é a maneira mais simples de verificar se dois números inteiros têm o mesmo sinal?Existe algum truque bit a bit para fazer isso?
Solução
Aqui está uma versão que funciona em C/C++ que não depende de tamanhos inteiros ou tem o problema de overflow (ou seja,x*y>=0 não funciona)
bool SameSign(int x, int y)
{
return (x >= 0) ^ (y < 0);
}
Claro, você pode criar um modelo:
template <typename valueType>
bool SameSign(typename valueType x, typename valueType y)
{
return (x >= 0) ^ (y < 0);
}
Observação:Como estamos usando ou exclusivo, queremos que LHS e RHS sejam diferentes quando os sinais forem iguais, portanto, a verificação diferente em relação a zero.
Outras dicas
O que há de errado com
return ((x<0) == (y<0));
?
(a ^ b) >= 0
será avaliado como 1 se o sinal for o mesmo, 0 caso contrário.
Eu ficaria cauteloso com qualquer truque bit a bit para determinar o sinal de números inteiros, pois então você terá que fazer suposições sobre como esses números são representados internamente.
Quase 100% do tempo, os números inteiros serão armazenados como elogio a dois, mas não é uma boa prática fazer suposições sobre os componentes internos de um sistema, a menos que você esteja usando um tipo de dados que garanta um formato de armazenamento específico.
No elogio de dois, você pode apenas verificar o último bit (mais à esquerda) do número inteiro para determinar se é negativo, para poder comparar apenas esses dois bits.Isso significaria que 0 teria o mesmo sinal que um número positivo, o que está em desacordo com a função de sinal implementada na maioria dos idiomas.
Pessoalmente, eu usaria apenas a função de sinal do idioma escolhido.É improvável que haja problemas de desempenho com um cálculo como este.
Supondo entradas de 32 bits:
bool same = ((x ^ y) >> 31) != 1;
Um pouco mais conciso:
bool same = !((x ^ y) >> 31);
Não tenho certeza se consideraria "truque bit a bit" e "mais simples" sinônimos.Vejo muitas respostas que assumem números inteiros assinados de 32 bits (embora seria seja bobo em pedir sem assinatura);Não tenho certeza se eles se aplicariam a valores de ponto flutuante.
Parece que a verificação "mais simples" seria comparar como ambos os valores se comparam a 0;isso é bastante genérico, assumindo que os tipos podem ser comparados:
bool compare(T left, T right)
{
return (left < 0) == (right < 0);
}
Se os sinais forem opostos, você será falso.Se os sinais forem iguais, você acerta.
(inteiro1 * inteiro2) > 0
Porque quando dois inteiros compartilham um sinal, o resultado da multiplicação será sempre positivo.
Você também pode definir >= 0 se quiser tratar 0 como sendo o mesmo sinal, não importa o que aconteça.
Supondo a aritmética do complemento de dois (http://en.wikipedia.org/wiki/Two_complement):
inline bool same_sign(int x, int y) {
return (x^y) >= 0;
}
Isso pode levar apenas duas instruções e menos de 1ns em um processador moderno com otimização.
Não assumindo a aritmética do complemento de dois:
inline bool same_sign(int x, int y) {
return (x<0) == (y<0);
}
Isso pode exigir uma ou duas instruções extras e demorar um pouco mais.
Usar a multiplicação é uma má ideia porque é vulnerável ao estouro.
se (x * y) > 0...
assumindo diferente de zero e tal.
Como observação técnica, soluções pouco complicadas serão muito mais eficientes do que a multiplicação, mesmo em arquiteturas modernas.São apenas cerca de 3 ciclos que você está economizando, mas você sabe o que dizem sobre um "centavo economizado"...
Apenas fora do topo da minha cabeça...
int mask = 1 << 31;
(a & mask) ^ (b & mask) < 0;
versão C sem ramificação:
int sameSign(int a, int b) {
return ~(a^b) & (1<<(sizeof(int)*8-1));
}
Modelo C++ para tipos inteiros:
template <typename T> T sameSign(T a, T b) {
return ~(a^b) & (1<<(sizeof(T)*8-1));
}
Para qualquer tamanho de int com aritmética de complemento de dois:
#define SIGNBIT (~((unsigned int)-1 >> 1))
if ((x & SIGNBIT) == (y & SIGNBIT))
// signs are the same
assumindo 32 bits
if(((x^y) & 0x80000000) == 0)
...a resposta if(x*y>0)
está ruim devido ao transbordamento
se o sinal (a*b < 0) for diferente, caso contrário o sinal será o mesmo (ou a ou b for zero)
Pensando nos meus tempos de universidade, na maioria das representações de máquinas, o bit mais à esquerda de um número inteiro não é 1 quando o número é negativo e 0 quando é positivo?
Imagino que isso dependa bastante da máquina.
int mesmo_sinal = !( (x >> 31) ^ (y >> 31) );
se (mesmo_sinal) ...outro ...
Melhor maneira de usar std::signbit do seguinte modo:
std::signbit(firstNumber) == std::signbit(secondNumber);
Ele também oferece suporte a outros tipos básicos (double
, float
, char
etc).