Como verifico se um número inteiro é par ou ímpar? [fechadas]


193

Como posso verificar se um determinado número é par ou ímpar em C?


5
A versão que usa bit a bit e (&) é muito mais eficiente que a versão do módulo (%). Você deve alterar o que selecionou como a resposta correta.
Stefan Rusek 02/10/08

6
Improvável que isso importe - o argumento é uma constante. Fácil para o otimizador
MSalters 2/08/08

2
Fatores de legibilidade para isso também.
Brian G

2
Em aplicativos incorporados (o mundo em que passo a maior parte do tempo em programação), alguns processadores possuem unidades aritméticas muito primitivas e não podem executar operações de divisão / módulo facilmente. Por esse motivo, geralmente uso o método bit a bit. No entanto, na CPU de um desktop moderno, esse não será o caso.
bta

3
Nunca achei a operação do módulo mais fácil de entender. Quando eu precisei determinar par ou ímpar, a máscara bit a bit foi a primeira coisa que me veio à mente. É algo natural, já que a maneira como fazemos isso manualmente é olhar o dígito menos significativo para ver se está em {0 2 4 6 8} ou {1 3 5 7 9}. Isso se traduz diretamente para olhar para o bit menos significativo para ver se é 0 ou 1.
P Daddy

Respostas:


449

Use o operador modulo (%) para verificar se há um restante ao dividir por 2:

if (x % 2) { /* x is odd */ }

Algumas pessoas criticaram minha resposta acima afirmando que usar x & 1 é "mais rápido" ou "mais eficiente". Não acredito que seja esse o caso.

Por curiosidade, criei dois programas triviais de casos de teste:

/* modulo.c */
#include <stdio.h>

int main(void)
{
    int x;
    for (x = 0; x < 10; x++)
        if (x % 2)
            printf("%d is odd\n", x);
    return 0;
}

/* and.c */
#include <stdio.h>

int main(void)
{
    int x;
    for (x = 0; x < 10; x++)
        if (x & 1)
            printf("%d is odd\n", x);
    return 0;
}

Em seguida, compilei isso com o gcc 4.1.3 em uma de minhas máquinas 5 vezes diferentes:

  • Sem sinalizadores de otimização.
  • Com -O
  • Com -Os
  • Com -O2
  • Com -O3

Examinei a saída de montagem de cada compilação (usando gcc-S) e descobri que, em cada caso, a saída para and.c e modulo.c eram idênticas (ambos usavam a instrução andl $ 1,% eax). Duvido que esse seja um "novo" recurso, e suspeito que remonta a versões antigas. Também duvido que qualquer compilador não arcano moderno (comercializado nos últimos 20 anos), de código aberto ou comercial, não tenha essa otimização. Eu testaria em outros compiladores, mas não tenho nenhum disponível no momento.

Se alguém mais gostaria de testar outros compiladores e / ou destinos de plataforma e obtiver um resultado diferente, eu ficaria muito interessado em saber.

Finalmente, a versão do módulo é garantida pelo padrão para trabalhar se o número inteiro é positivo, negativo ou zero, independentemente da representação da implementação de números inteiros assinados. A versão bit a bit e não é. Sim, eu sei que o complemento de dois é onipresente, então isso não é realmente um problema.


11
A pergunta perguntou especificamente como fazê-lo em C, então eu respondi em C, apesar de chustar mencionar que eles não conseguiram descobrir como fazê-lo em Java. Não reivindiquei ou impliquei que essa era uma resposta do Java, não sei o Java. Acho que acabei de receber meu primeiro voto negativo e estou confuso sobre o porquê. Ah bem.
Chris Young

33
Eu diria, se (x% 2! = 0) {/ * x é ímpar * /}, mas quem sabe. Também não sei java.
eugensk

9
Está recebendo muitos votos positivos para diferenciá-lo dos idiotas dos operadores bit a bit, sem ter que gastar nosso karma votando neles.
Wnoise 02/10/08

13
Eu concordo com tudo, exceto uma coisa: eu gosto de manter inteiros e valores de verdade separados, conceitualmente, então prefiro escrever "se (x% 2 == 1)". É o mesmo para o compilador, mas talvez um pouco mais claro para os seres humanos. Além disso, você pode usar o mesmo código em idiomas que não interpretam diferente de zero como verdadeiro.
Thomas Padron-McCarthy

46
Minha referência? Qual referência? Eu não fiz nenhum benchmarking. Examinei a linguagem assembly gerada. Isso não tem absolutamente nada a ver com printf.
317 Chris Young

207

Vocês são muito eficientes. O que você realmente quer é:

public boolean isOdd(int num) {
  int i = 0;
  boolean odd = false;

  while (i != num) {
    odd = !odd;
    i = i + 1;
  }

  return odd;
}

Repita para isEven .

Claro, isso não funciona para números negativos. Mas com brilho vem o sacrifício ...


17
Se você lançou uma exceção de argumento em valores negativos e observou na documentação que esta função é O (N), então eu ficaria bem com isso.
Jeffrey L Whitledge

7
A versão corporativa teria que usar XML. É claro que hoje em dia você teria um serviço da web que poderia consultar
Martin Beckett

58
Você deve otimizar isso com uma tabela de consulta.
Weeble

1
Eu sou um monge, tinha de um seu representante 6.999 em um novo milênio
Eran Medan

7
Isto é brilhante! Meu chefe me disse que tínhamos um cliente irritado porque achava que sua licença corporativa não dava nada além da licença padrão. Agora nós adicionamos essa função em nosso programa, e só porque é executado mais lentamente, ele acha que seu software está fazendo MUITO mais trabalho !!!
Phil

97

Use aritmética de bits:

if((x & 1) == 0)
    printf("EVEN!\n");
else
    printf("ODD!\n");

Isso é mais rápido do que usar divisão ou módulo.


43
Não acho justo dizer que é mais rápido do que usar divisão ou módulo. O padrão C não diz nada sobre o desempenho dos operadores, e qualquer compilador decente produzirá código rápido para ambos. Eu pessoalmente escolher o idioma que se comunica a minha intenção, e% parece mais apropriado aqui
Chris Young

21
Eu gosto mais de (x e 1), porque verifica se o número é igual ao das pessoas: verifique se o último dígito é par ou ímpar. Na minha opinião, ele comunica sua intenção mais do que o método modulo. (Não que isso importe muito.)
Jeremy Ruten

2
Você está certo, acho que é subjetivo. Embora a definição usual de "par" seja "número inteiro divisível por 2", não "número inteiro que termina em 0, 2, 4, 6 ou 8". :-)
Chris Young

4
@TraumaPony - para ANSI padrão C e Java inicial, depende do sistema do computador. É especificado que a representação é usado para números assinados - 2 de elogio, elogio 1 do, cinza-codificado, etc. Mas módulo é sempre módulo
Aaron

9
Não funciona universalmente para números negativos. Consulte Verifique esta resposta para obter mais detalhes: stackoverflow.com/questions/160930/… para obter detalhes.
Andrew Edgecombe

36

[Modo piada = "ativado"]

public enum Evenness
{
  Unknown = 0,
  Even = 1,
  Odd = 2
}

public static Evenness AnalyzeEvenness(object o)
{

  if (o == null)
    return Evenness.Unknown;

  string foo = o.ToString();

  if (String.IsNullOrEmpty(foo))
    return Evenness.Unknown;

  char bar = foo[foo.Length - 1];

  switch (bar)
  {
     case '0':
     case '2':
     case '4':
     case '6':
     case '8':
       return Evenness.Even;
     case '1':
     case '3':
     case '5':
     case '7':
     case '9':
       return Evenness.Odd;
     default:
       return Evenness.Unknown;
  }
}

[Modo brincadeira = "desativado"]

EDIT: Adicionado valores confusos para a enumeração.


2
Uau ... isso é mais louco do que a solução do SCdF! Parabéns! Não há voto positivo ... não posso recomendar isso. Mas obrigado pelo engraçado!
Wes P

1
A vantagem dessa abordagem é que ela trabalha com mais do que apenas números. Além disso, se você substituir esta linha: char bar = foo [foo.Length - 1]; com isso: barra dupla = Char.GetNumericValue (foo [foo.Length - 1]); Então ele funcionará com qualquer sistema numérico.
Jeffrey L Whitledge

5
relatório de erro: 14.65 é relatado como ímpar quando deveria ser desconhecido.
TheSoftwareJedi 22/10/08

4
Software Jedi, é um "recurso". ;)
Sklivvz 23/10/08

31
TheSoftwareJedi: 14.65 é um dos números inteiros mais estranhos que eu já vi.
Bruce Alderman

16

Em resposta ao ffpf - eu tive exatamente o mesmo argumento com um colega anos atrás, e a resposta é não , ele não funciona com números negativos.

O padrão C estipula que números negativos podem ser representados de três maneiras:

  • Complemento 2
  • Complemento 1
  • sinal e magnitude

Verificando assim:

isEven = (x & 1);

funcionará no complemento de 2 e na representação de sinal e magnitude, mas não no complemento de 1.

No entanto, acredito que o seguinte funcionará para todos os casos:

isEven = (x & 1) ^ ((-1 & 1) | ((x < 0) ? 0 : 1)));

Agradeço ao ffpf por apontar que a caixa de texto estava comendo tudo depois do meu personagem menor que o!


Acho que seu segundo exemplo de código está faltando algum texto.
Jeff Yates

3
Vamos elogiar esses números!
thejh 30/03

14

Uma boa é:

/*forward declaration, C compiles in one pass*/
bool isOdd(unsigned int n);

bool isEven(unsigned int n)
{
  if (n == 0) 
    return true ;  // I know 0 is even
  else
    return isOdd(n-1) ; // n is even if n-1 is odd
}

bool isOdd(unsigned int n)
{
  if (n == 0)
    return false ;
  else
    return isEven(n-1) ; // n is odd if n-1 is even
}

Observe que esse método usa recursão de cauda envolvendo duas funções. Ele pode ser implementado com eficiência (transformado em um tipo de loop de tempo / até) se o seu compilador suportar recursão de cauda como um compilador Scheme. Nesse caso, a pilha não deve transbordar!


1
Isso não lida bem com isOdd (0).
Steve McLeod

1
Eu acho que você tem um loop infinito (com recursão de cauda) ou um estouro de pilha (sem recursão de cauda) para isOdd () com quaisquer valores pares ou isEven () com valores ímpares. Só termina com true. É o problema da parada novamente.
Jeffrey L Whitledge

7
Ah, claro, conserte-o sem comentários e faça-me parecer um idiota. Isso é bom.
Jeffrey L Whitledge

1
Agora, você tem um erro de compilação: em isEven nem todos os caminhos de código retornam um valor. Não, eu realmente não tentei esse código, é o compilador na minha cabeça que está reclamando.
Jeffrey L Whitledge

5
erro de compilação: nem todos os caminhos de retornar um valor ódio para bombardeá-lo com comentários de bugs em seu código de exemplo, mas o que acontece quando você chamar eIgual (5)
Kevin

11

Um número é par se, quando dividido por dois, o restante for 0. Um número será ímpar se, quando dividido por 2, o restante for 1.

// Java
public static boolean isOdd(int num){
    return num % 2 != 0;
}

/* C */
int isOdd(int num){
    return num % 2;
}

Métodos são ótimos!


Seu método Java está quebrado porque num% 2 == -1 para números ímpares negativos.
WMR 22/10/08

Foi por isso que você me rebaixou?
Jjnguy 22/10/08

3
Eu diminuí a votação porque sua função em C leva mais caracteres para digitar do que aquilo que faz. IE num% I tem 7 caracteres, incluindo os espaços IsOdd (I) 8 caracteres. Por que você criaria uma função que é mais longa do que apenas executar a operação?
22408 Kevin

13
@ Kevin, na minha opinião, o código não é medido por caracteres, mas pelo tempo que você leva para escrevê-lo, incluindo o tempo de reflexão + de depuração. num% 2 leva um milissegundo a mais para pensar do que isOdd. agora adicione os números globalmente e você perdeu um ano coletivo. O isOdd também pode ser testado, verificado e eventualmente certificado como livre de bugs (por exemplo, manipulação de números negativos) em que% 2 - alguns desenvolvedores sempre têm dúvidas e experimentam. código bom é o código que você não escreve, apenas reutiliza ... apenas meus 2 centavos.
Eran Medan

2
@EranMedan, a mesma lógica se aplicaria à substituição do i ++ pelo IncrementByOne (i) e é uma idéia tão ruim quanto. Se um desenvolvedor tiver dúvidas sobre o que% 2 faz, não o quero nem perto do meu código.
22412 Kevin


7

Eu diria que basta dividi-lo por 2 e se houver um 0 restante, é par; caso contrário, é estranho.

Usar o módulo (%) facilita isso.

por exemplo. 4% 2 = 0 portanto 4 é par 5% 2 = 1 portanto 5 é ímpar


6

Mais uma solução para o problema
(as crianças podem votar)

bool isEven(unsigned int x)
{
  unsigned int half1 = 0, half2 = 0;
  while (x)
  {
     if (x) { half1++; x--; }
     if (x) { half2++; x--; }

  }
  return half1 == half2;
}

Não, você não é o tipo de criança que contou com :)
eugensk

Eu ia aprovar isso, mas é um pouco lento em números negativos. :)
Chris Young

3
Todos os números são brilhantes e positivos. Ou você tem preconceito contra alguns? :))
eugensk

3
Nos computadores, todos os números negativos, eventualmente se tornam positivos. Chamamos isso de Rollover of Happiness (não aplicável a BIGNUMS, YMMY, não é válido em todos os estados).
Will Hartung

@WillHartung "rolagem de felicidade" é ótimo! : D
thejh 30/03

6

Eu criaria uma tabela de paridades (0 se 1 igual a ímpar) dos números inteiros (para que se pudesse fazer uma pesquisa: D), mas o gcc não me permite fazer matrizes de tamanhos iguais:

typedef unsigned int uint;

char parity_uint [UINT_MAX];
char parity_sint_shifted [((uint) INT_MAX) + ((uint) abs (INT_MIN))];
char* parity_sint = parity_sint_shifted - INT_MIN;

void build_parity_tables () {
    char parity = 0;
    unsigned int ui;
    for (ui = 1; ui <= UINT_MAX; ++ui) {
        parity_uint [ui - 1] = parity;
        parity = !parity;
    }
    parity = 0;
    int si;
    for (si = 1; si <= INT_MAX; ++si) {
        parity_sint [si - 1] = parity;
        parity = !parity;
    }
    parity = 1;
    for (si = -1; si >= INT_MIN; --si) {
        parity_sint [si] = parity;
        parity = !parity;
    }
}

char uparity (unsigned int n) {
    if (n == 0) {
        return 0;
    }
    return parity_uint [n - 1];
}

char sparity (int n) {
    if (n == 0) {
        return 0;
    }
    if (n < 0) {
        ++n;
    }
    return parity_sint [n - 1];
}

Então, vamos recorrer à definição matemática de par e ímpar.

Um número inteiro n é uniforme se existir um número inteiro k tal que n = 2k.

Um número inteiro n é ímpar se existir um número k tal que n = 2k + 1.

Aqui está o código para isso:

char even (int n) {
    int k;
    for (k = INT_MIN; k <= INT_MAX; ++k) {
        if (n == 2 * k) {
            return 1;
        }
    }
    return 0;
}

char odd (int n) {
    int k;
    for (k = INT_MIN; k <= INT_MAX; ++k) {
        if (n == 2 * k + 1) {
            return 1;
        }
    }
    return 0;
}

Let C-inteiros denotam os possíveis valores de intem uma dada compilação C. (Observe que C-inteiros é um subconjunto dos inteiros.)

Agora, pode-se preocupar que, para um dado n em C-inteiros, o número inteiro correspondente k possa não existir em C-inteiros. Mas com uma pequena prova, pode-se mostrar que para todos os números inteiros n, | n | <= | 2n | (*), onde | n | é "n se n for positivo e -n caso contrário". Em outras palavras, para todos os n em números inteiros, pelo menos um dos itens a seguir é válido (exatamente os casos (1 e 2) ou os casos (3 e 4), de fato, mas não vou provar aqui):

Caso 1: n <= 2n.

Caso 2: -n <= -2n.

Caso 3: -n <= 2n.

Caso 4: n <= -2n.

Agora pegue 2k = n. (Tal ak existe se n for par, mas eu não vou provar aqui. Se n for par, o loop in evennão retornará mais cedo, portanto não importa.) Mas isso implica k <n se n não 0 por (*) e o fato (novamente não provado aqui) de que para todo m, z em números inteiros 2m = z implica z diferente de m dado m não é 0. No caso n é 0, 2 * 0 = 0 então 0 é o mesmo que terminamos (se n = 0 então 0 está em números inteiros C porque n está em números inteiros C na funçãoeven , portanto, k = 0 está em números inteiros C). Assim, tal ak em C-inteiros existe para n em C-inteiros se n for par.

Um argumento semelhante mostra que se n é ímpar, existe ak em números inteiros C de modo que n = 2k + 1.

Portanto, as funções evene oddapresentadas aqui funcionarão corretamente para todos os C-inteiros.


1
Não quero ofender, mas qual é o sentido dessa resposta? i % 2é muito menor e provavelmente mais eficiente.
GManNickG 27/02

2
@ GMan: Mas isso é muito mais determinístico! Isso funcionará corretamente para detectar todos os casos extremos.
P Daddy

1
... E (!!!) está correto !!!
Thomas Eding

Não sei dizer se você está brincando ou não. : X %2funciona para todos os números inteiros.
GManNickG 27/02

1
+1: eu diria "Boa resposta", mas acho que "Resposta interessante" é mais apropriada.
James Webster

5
// C#
bool isEven = ((i % 2) == 0);

2
O que? Isso não é c #! Isso é puro C! :-P
asterite 02/10/08

8
Vou jogar um WinForm em torno dele para torná-lo puro em C # ...
Michael Petrotta 2/08/08

@mateusza: Normalmente, quando você vê "bool" de alguma capitalização ou outro em C, é um typedefou #defineou algo assim.
David Thornley

2
@mateusza @ David Thornley No C99, bool é um recurso padrão ( pt.wikipedia.org/wiki/Stdbool.h )
fortran

1
Fale sobre parênteses extremamente redundantes ...
Thomas Eding

4

Aqui está uma resposta em Java:

public static boolean isEven (Integer Number) {
    Pattern number = Pattern.compile("^.*?(?:[02]|8|(?:6|4))$");
    String num = Number.toString(Number);
    Boolean numbr = new Boolean(number.matcher(num).matches());
    return numbr.booleanValue();
}

4

Tente o seguinte: return (((a>>1)<<1) == a)

Exemplo:

a     =  10101011
-----------------
a>>1 --> 01010101
a<<1 --> 10101010

b     =  10011100
-----------------
b>>1 --> 01001110
b<<1 --> 10011100

Você pode explicar isso, por favor? Estou muito familiarizado com operadores bit a bit
Abdul

Deslocar para a direita e depois para a esquerda zerará seu último bit (o mais correto). Se o novo número for igual ao original, isso significa que o último bit do número original foi 0. Portanto, é par. Dê uma olhada na minha resposta atualizada.
Kiril Aleksandrov

obrigado, eu obtê-lo agora
Abdul

Não tenho certeza de qual abordagem é mais rápida. Não tentei compará-los.
Kiril Aleksandrov

Isso também não zera o seu bit mais significativo? Um problema com ints não assinados em alguns idiomas e ints negativos na maioria ...
Troyseph

4

Lendo essa discussão bastante divertida, lembrei que tinha uma função sensível ao tempo no mundo real que testava números ímpares e pares dentro do loop principal. É uma função de potência inteira, publicada em outro lugar no StackOverflow, da seguinte maneira. Os benchmarks foram bastante surpreendentes. Pelo menos nessa função do mundo real, o módulo é mais lento e significativamente mais. O vencedor, por uma ampla margem, exigindo 67% do tempo do módulo, é uma abordagem ou (|) , e não pode ser encontrado em nenhum outro lugar nesta página.

static dbl  IntPow(dbl st0, int x)  {
    UINT OrMask = UINT_MAX -1;
    dbl  st1=1.0;
    if(0==x) return (dbl)1.0;

    while(1 != x)   {
        if (UINT_MAX == (x|OrMask)) {     //  if LSB is 1...    
        //if(x & 1) {
        //if(x % 2) {
            st1 *= st0;
        }    
        x = x >> 1;  // shift x right 1 bit...  
        st0 *= st0;
    }
    return st1 * st0;
}

Para 300 milhões de loops, os tempos de referência são os seguintes.

3.962 the | e abordagem de máscara

4.851 a abordagem

5.850 a abordagem%

Para pessoas que pensam que a teoria, ou uma listagem em linguagem assembly, resolve argumentos como esses, isso deve ser um conto de advertência. Há mais coisas no céu e na terra, Horácio, do que se sonha em sua filosofia.


1
Melhor usar unsigned xcomo x = x >> 1;é o comportamento definido pela implementação quando x < 0. Não está claro por que xe OrMaskdiferem em tipo. Simples o suficiente para reescrever usando um while(x)teste.
chux - Restabelece Monica

2
Gostaria de saber qual compilador você usou para comparar isso, já que a maioria dos compiladores deve ser inteligente o suficiente para compilar o % 2caso usando o bit a bit &. Acabei de testar isso e os resultados são os mesmos (VS2015, a versão é compilada com todas as otimizações, x86 e x64). A resposta aceita também afirma isso para o GCC (escrito em 2008).
Lou

2
O problema com este post é que a premissa de que um bit a bit orseria mais rápido que um andé altamente improvável, em qualquer plataforma / compilador. Mesmo se houvesse uma combinação estranha de plataforma / compilador (e você não publicou nem isso nem o código usado para realizar a referência), dependendo de outros compiladores que se comportam da mesma forma, seria uma má aposta de otimização. Então, como eu escrevi, me pergunto em qual plataforma / compilador isso foi testado , porque tenho quase certeza de que não foi medido corretamente.
Lou

2
Não o chamando de mentiroso, apenas afirmando com alta certeza que você não mediu corretamente. Não há necessidade de me chamar de motorista de caminhão ainda, leia meu comentário original: fiz um benchmark e os resultados foram, como esperado, completamente iguais nos três casos (certeza de ~ 3 sigma, depois de executar cada teste 10 vezes por 500.000 .000 iterações). Se você realmente tem uma longa carreira ilustre, dê um passo atrás e pense se suas reivindicações fazem sentido e publique o código real usado para fazer a referência. Caso contrário, o post é o que eu acredito que seja, apenas um erro de medição.
Lou

1
Feito .
Lou

4

Este é um acompanhamento da discussão com o @RocketRoy sobre sua resposta , mas pode ser útil para quem quiser comparar esses resultados.

tl; dr Pelo que tenho visto, a abordagem de Roy ( (0xFFFFFFFF == (x | 0xFFFFFFFE)) não está totalmente otimizado para x & 1que a modabordagem, mas na prática tempos de execução deve vir iguais em todos os casos.

Então, primeiro comparei a saída compilada usando o Compiler Explorer :

Funções testadas:

int isOdd_mod(unsigned x) {
    return (x % 2);
}

int isOdd_and(unsigned x) {
    return (x & 1);
}

int isOdd_or(unsigned x) {
    return (0xFFFFFFFF == (x | 0xFFFFFFFE));
}   

CLang 3.9.0 com -O3:

isOdd_mod(unsigned int):                          # @isOdd_mod(unsigned int)
        and     edi, 1
        mov     eax, edi
        ret

isOdd_and(unsigned int):                          # @isOdd_and(unsigned int)
        and     edi, 1
        mov     eax, edi
        ret

isOdd_or(unsigned int):                           # @isOdd_or(unsigned int)
        and     edi, 1
        mov     eax, edi
        ret

GCC 6.2 com -O3:

isOdd_mod(unsigned int):
        mov     eax, edi
        and     eax, 1
        ret

isOdd_and(unsigned int):
        mov     eax, edi
        and     eax, 1
        ret

isOdd_or(unsigned int):
        or      edi, -2
        xor     eax, eax
        cmp     edi, -1
        sete    al
        ret

Com o clang, percebemos que todos os três casos são funcionalmente iguais. No entanto, a abordagem de Roy não é otimizada no GCC, portanto, YMMV.

É semelhante ao Visual Studio; inspecionando a desmontagem do Release x64 (VS2015) para essas três funções, pude ver que a parte de comparação é igual para os casos "mod" e "e" e "e um pouco maior para o caso" ou "de Roy:

// x % 2
test bl,1  
je (some address) 

// x & 1
test bl,1  
je (some address) 

// Roy's bitwise or
mov eax,ebx  
or eax,0FFFFFFFEh  
cmp eax,0FFFFFFFFh  
jne (some address)

No entanto, após executar uma referência real para comparar essas três opções (modificação simples, bit a bit ou bit a bit e), os resultados foram completamente iguais (novamente, Visual Studio 2005 x86 / x64, versão Build, sem depurador anexado).

A montagem de liberação usa as testinstruções para andemod casos, enquanto o caso de Roy usa a cmp eax,0FFFFFFFFhabordagem, mas é fortemente desenrolada e otimizada para que não haja diferença na prática.

Meus resultados após 20 execuções (i7 3610QM, plano de energia do Windows 10 definido como Alto desempenho):

[Teste: Modificação simples 2] TEMPO MÉDIO: 689,29 ms (Dif. Relativa: + 0,000%)
[Teste: Bit a bit ou] TEMPO MÉDIO: 689,63 ms (Diferença relativa: + 0,048%)
[Teste: Bit a bit e] TEMPO MÉDIO: 687,80 ms (Dif. Relativa: -0,217%)

A diferença entre essas opções é menor que 0,3%; portanto, é óbvio que a montagem é igual em todos os casos.

Aqui está o código, se alguém quiser tentar, com uma ressalva de que eu apenas o testei no Windows (verifique o #if LINUXcondicional para a get_timedefinição e implemente-o, se necessário, retirado desta resposta ).

#include <stdio.h>

#if LINUX
#include <sys/time.h>
#include <sys/resource.h>
double get_time()
{
    struct timeval t;
    struct timezone tzp;
    gettimeofday(&t, &tzp);
    return t.tv_sec + t.tv_usec*1e-6;
}
#else
#include <windows.h>
double get_time()
{
    LARGE_INTEGER t, f;
    QueryPerformanceCounter(&t);
    QueryPerformanceFrequency(&f);
    return (double)t.QuadPart / (double)f.QuadPart * 1000.0;
}
#endif

#define NUM_ITERATIONS (1000 * 1000 * 1000)

// using a macro to avoid function call overhead
#define Benchmark(accumulator, name, operation) { \
    double startTime = get_time(); \
    double dummySum = 0.0, elapsed; \
    int x; \
    for (x = 0; x < NUM_ITERATIONS; x++) { \
        if (operation) dummySum += x; \
    } \
    elapsed = get_time() - startTime; \
    accumulator += elapsed; \
    if (dummySum > 2000) \
        printf("[Test: %-12s] %0.2f ms\r\n", name, elapsed); \
}

void DumpAverage(char *test, double totalTime, double reference)
{
    printf("[Test: %-12s] AVERAGE TIME: %0.2f ms (Relative diff.: %+6.3f%%)\r\n",
        test, totalTime, (totalTime - reference) / reference * 100.0);
}

int main(void)
{
    int repeats = 20;
    double runningTimes[3] = { 0 };
    int k;

    for (k = 0; k < repeats; k++) {
        printf("Run %d of %d...\r\n", k + 1, repeats);
        Benchmark(runningTimes[0], "Plain mod 2", (x % 2));
        Benchmark(runningTimes[1], "Bitwise or", (0xFFFFFFFF == (x | 0xFFFFFFFE)));
        Benchmark(runningTimes[2], "Bitwise and", (x & 1));
    }

    {
        double reference = runningTimes[0] / repeats;
        printf("\r\n");
        DumpAverage("Plain mod 2", runningTimes[0] / repeats, reference);
        DumpAverage("Bitwise or", runningTimes[1] / repeats, reference);
        DumpAverage("Bitwise and", runningTimes[2] / repeats, reference);
    }

    getchar();

    return 0;
}

Eu acredito que você cometeu o Cardeal Sin de comparações; criando um tão específico que não representa um ambiente do mundo real. Veja a linguagem assembly e observe quantos registros você está usando. Marcas altas de esforço, mas esses resultados não se sustentam no processamento no mundo real.

@RocketRoy: como todas as saídas são exatamente iguais para os três casos (bem, um pouco pior para o seu programa em um caso), eu realmente não me importo com quantos registros foram usados. Porém, novamente, sinta-se à vontade para criar e publicar um programa / ambiente de exemplo que confunda o compilador para criar um assembly mais otimizado em um dos casos, sendo tudo o mesmo.
Lou

Eu sempre gosto de programadores arrogantes. É uma boa característica para um programador, mas em um programa do mundo real mais complexo, meu método terá um desempenho melhor que o seu, porque o compilador tem mais maneiras de resolver o problema para que as instruções se sobreponham (nas arquiteturas Intel) produzindo melhores resultados . Muito poucos programadores veteranos com boa experiência em benchmarking preferem seu benchmark, mas continuem com o bom trabalho e lembre-se de executar novamente seus benchmarks quando novos lançamentos de chips forem lançados. As coisas mudam com o tempo.

3

Eu sei que isso é apenas açúcar sintático e aplicável apenas em .net, mas e o método de extensão ...

public static class RudiGroblerExtensions
{
    public static bool IsOdd(this int i)
    {
        return ((i % 2) != 0);
    }
}

Agora você pode fazer o seguinte

int i = 5;
if (i.IsOdd())
{
    // Do something...
}

1
Código legal. Pena que reivindique que 2 é ímpar e 3 não.
Anthony

oops, desculpe ... minha lógica está errada maneira redonda ...
rudigrobler

3

Na "categoria criativa, mas confusa", ofereço:

int isOdd(int n) { return n ^ n * n ? isOdd(n * n) : n; }

Uma variante desse tema específica do Microsoft C ++:

__declspec(naked) bool __fastcall isOdd(const int x)
{
    __asm
    {
        mov eax,ecx
        mul eax
        mul eax
        mul eax
        mul eax
        mul eax
        mul eax
        ret
    }
}

2

O método bit a bit depende da representação interna do número inteiro. O módulo funcionará em qualquer lugar onde houver um operador de módulo. Por exemplo, alguns sistemas realmente usam os bits de baixo nível para marcação (como linguagens dinâmicas), para que o x & 1 bruto não funcione nesse caso.


2

IsOdd (int x) {return true; }

Prova de correção - considere o conjunto de todos os números inteiros positivos e suponha que exista um conjunto de números inteiros não vazios que não sejam ímpares. Como números inteiros positivos são bem ordenados, haverá um número menor e não ímpar, o que por si só é bastante ímpar, tão claramente que esse número não pode estar no conjunto. Portanto, este conjunto não pode estar vazio. Repita para números inteiros negativos, exceto procure o maior número não ímpar.


2

Portátil:

i % 2 ? odd : even;

Não portável:

i & 1 ? odd : even;

i << (BITS_PER_INT - 1) ? odd : even;

2

Como algumas pessoas postaram, existem várias maneiras de fazer isso. De acordo com este site , o caminho mais rápido é o operador de módulo:

if (x % 2 == 0)
               total += 1; //even number
        else
               total -= 1; //odd number

No entanto, aqui está outro código que foi marcado pelo autor mais lento que a operação de módulo comum acima:

if ((x & 1) == 0)
               total += 1; //even number
        else
               total -= 1; //odd number

System.Math.DivRem((long)x, (long)2, out outvalue);
        if ( outvalue == 0)
               total += 1; //even number
        else
               total -= 1; //odd number

if (((x / 2) * 2) == x)
               total += 1; //even number
        else
               total -= 1; //odd number

if (((x >> 1) << 1) == x)
               total += 1; //even number
        else
               total -= 1; //odd number

        while (index > 1)
               index -= 2;
        if (index == 0)
               total += 1; //even number
        else
               total -= 1; //odd number

tempstr = x.ToString();
        index = tempstr.Length - 1;
        //this assumes base 10
        if (tempstr[index] == '0' || tempstr[index] == '2' || tempstr[index] == '4' || tempstr[index] == '6' || tempstr[index] == '8')
               total += 1; //even number
        else
               total -= 1; //odd number

Quantas pessoas conheciam o método Math.System.DivRem ou por que o usariam?



1

Para dar mais detalhes sobre o método do operador bit a bit para aqueles que não fizeram muita álgebra booleana durante nossos estudos, aqui está uma explicação. Provavelmente não é de muita utilidade para o OP, mas eu queria deixar claro por que o NUMBER & 1 funciona.

Observe que, como alguém respondeu acima, a maneira como os números negativos são representados pode interromper o funcionamento desse método. De fato, ele também pode quebrar o método do operador de módulo, pois cada idioma pode diferir na maneira como lida com operandos negativos.

No entanto, se você souber que NUMBER sempre será positivo, isso funcionará bem.

Como Tooony acima mencionou, apenas o último dígito em binário (e negação) é importante.

Uma lógica AND booleana dita que ambas as entradas devem ser 1 (ou alta tensão) para que 1 seja retornado.

1 e 0 = 0.

0 e 1 = 0.

0 e 0 = 0.

1 e 1 = 1.

Se você representa qualquer número como binário (usei uma representação de 8 bits aqui), os números ímpares têm 1 no final, os números pares têm 0.

Por exemplo:

1 = 00000001

2 = 00000010

3 = 00000011

4 = 00000100

Se você pegar qualquer número e usar AND bit a bit (& em java) por 1, retornará 00000001, = 1, o que significa que o número é ímpar. Ou 00000000 = 0, significando que o número é par.

Por exemplo

É estranho?

1 & 1 =

00000001 &

00000001 =

00000001 <- Ímpar

2 & 1 =

00000010 &

00000001 =

00000000 <- Par

54 & 1 =

00000001 &

00110110 =

00000000 <- Par

É por isso que isso funciona:

if(number & 1){

   //Number is odd

} else {

   //Number is even
}

Desculpe se isso é redundante.


1

Paridade de número zero | zero http://tinyurl.com/oexhr3k

Sequência de código Python.

# defining function for number parity check
def parity(number):
    """Parity check function"""
    # if number is 0 (zero) return 'Zero neither ODD nor EVEN',
    # otherwise number&1, checking last bit, if 0, then EVEN, 
    # if 1, then ODD.
    return (number == 0 and 'Zero neither ODD nor EVEN') \
            or (number&1 and 'ODD' or 'EVEN')

# cycle trough numbers from 0 to 13 
for number in range(0, 14):
    print "{0:>4} : {0:08b} : {1:}".format(number, parity(number))

Resultado:

   0 : 00000000 : Zero neither ODD nor EVEN
   1 : 00000001 : ODD
   2 : 00000010 : EVEN
   3 : 00000011 : ODD
   4 : 00000100 : EVEN
   5 : 00000101 : ODD
   6 : 00000110 : EVEN
   7 : 00000111 : ODD
   8 : 00001000 : EVEN
   9 : 00001001 : ODD
  10 : 00001010 : EVEN
  11 : 00001011 : ODD
  12 : 00001100 : EVEN
  13 : 00001101 : ODD

@ el.pescado, obrigado. Se Zero é par, quantos pares ele tem?

@ el.pescado, Ok, concordo com você. Então, se pensar um pouco, por que dividimos para 2 (dois)? O que queremos saber quando dividimos em dois? Por que não dividir para 3 ou 5, etc.?

@ el.pescado Este artigo da Wikipedia Parity of Zero está errado. Muitas pessoas foram enganadas por este artigo. Pense antes do Wink.

1
Você está certo. Agora que eu li outras respostas que eu encontrei o seu o mais abrangente :)
el.pescado

@ el.pescado. Obrigado. :) Agora você é o melhor amigo de Zero. (abraço)

1
I execute this code for ODD & EVEN:

#include <stdio.h>
int main()
{
    int number;
    printf("Enter an integer: ");
    scanf("%d", &number);

    if(number % 2 == 0)
        printf("%d is even.", number);
    else
        printf("%d is odd.", number);
}

0

Por uma questão de discussão ...

Você só precisa olhar para o último dígito em qualquer número para ver se é par ou ímpar. Assinado, sem sinal, positivo, negativo - são todos iguais no que diz respeito a isso. Portanto, isso deve funcionar o tempo todo: -

void tellMeIfItIsAnOddNumberPlease(int iToTest){
  int iLastDigit;
  iLastDigit = iToTest - (iToTest / 10 * 10);
  if (iLastDigit % 2 == 0){
    printf("The number %d is even!\n", iToTest);
  } else {
    printf("The number %d is odd!\n", iToTest);
  }
}

A chave aqui está na terceira linha do código, o operador de divisão executa uma divisão inteira, para que o resultado esteja faltando a parte da fração do resultado. Por exemplo, 222/10 dará 22 como resultado. Em seguida, multiplique novamente por 10 e você terá 220. Subtraia isso do 222 original e você terminará com 2, que por mágica é o mesmo número que o último dígito no número original. ;-) Os parênteses estão aí para nos lembrar da ordem em que o cálculo é feito. Primeiro faça a divisão e a multiplicação, depois subtraia o resultado do número original. Poderíamos deixá-los de fora, pois a prioridade é mais alta para divisão e multiplicação do que para subtração, mas isso nos dá um código "mais legível".

Poderíamos tornar tudo completamente ilegível, se quiséssemos. Não faria nenhuma diferença para um compilador moderno:

printf("%d%s\n",iToTest,0==(iToTest-iToTest/10*10)%2?" is even":" is odd");

Mas isso tornaria o código muito mais difícil de manter no futuro. Imagine que você gostaria de alterar o texto dos números ímpares para "não é par". Então alguém mais tarde quer descobrir quais alterações você fez e executar um svn diff ou similar ...

Se você não está preocupado com a portabilidade, mas mais com a velocidade, pode dar uma olhada no bit menos significativo. Se esse bit estiver definido como 1, é um número ímpar; se for 0, é um número par. Em um pequeno sistema endian, como a arquitetura x86 da Intel, seria algo como isto: -

if (iToTest & 1) {
  // Even
} else {
  // Odd
}

O que exatamente há de errado em apenas ir ao iToTest% 2 == 0? Você está desperdiçando uma divisão extraindo o último dígito, portanto o seu é duas vezes mais lento que o necessário.
Freespace

@ espaço: eu desperdiço mais do que isso, não é? :-) Uma multiplicação e uma subtração também. Mas o que é mais eficiente entre as duas soluções que não ouso dizer. Nunca reivindiquei que essa seja a solução mais rápida, muito pelo contrário, se você ler a primeira linha do meu post novamente.
Tooony

@Tooony, ah, meu chapéu de humor caiu. É formly volta agora: D Desculpe por isso :)
freespace

0

Se você quer ser eficiente, use operadores bit a bit ( x & 1), mas se quiser ser legível, use o módulo 2 ( x % 2)


-1: se você quiser ser eficiente, use qualquer um deles. Se você quiser que seja portátil, use %. Se você quiser que seja legível, use %. Hmmm, eu vejo um padrão aqui.
Thomas Eding

@ Trinithis, não há padrão e esta solução é muito melhor que a sua.
Subs

0

Verificar par ou ímpar é uma tarefa simples.

Sabemos que qualquer número exatamente divisível por 2 é o número par mais ímpar.

Só precisamos verificar a divisibilidade de qualquer número e, para verificar a divisibilidade, usamos o %operador

Verificando ímpares usando se mais

if(num%2 ==0)  
{
    printf("Even");
}
else
{
    printf("Odd");
}

Programa C para verificar par ou ímpar usando se mais

Usando o operador Condicional / Ternário

(num%2 ==0) printf("Even") : printf("Odd");

Programa C para verificar par ou ímpar usando o operador condicional .

Usando o operador Bitwise

if(num & 1)  
{
    printf("Odd");
}
else 
{
    printf("Even");
}

e onde exatamente é o operador ternário?
Beyondo

0

+ 66% mais rápido>!(i%2) / i%2 == 0

int isOdd(int n)
{
    return n & 1;
}

O código verifica o último bit do número inteiro se é 1 em binário

Explicação

Binary  :   Decimal
-------------------
0000    =   0
0001    =   1
0010    =   2
0011    =   3
0100    =   4
0101    =   5
0110    =   6
0111    =   7
1000    =   8
1001    =   9
and so on...

Observe que o bit mais à direita é sempre 1 para números ímpares .

os & bit a bit E operador verifica o bit mais à direita em nosso retorno linha Se for 1

Pense nisso como verdadeiro e falso

Quando comparamos n com 1, o que significa 0001em binário (o número de zeros não importa).
então vamos apenas imaginar que temos o número inteiro n com tamanho de 1 byte.

Seria representado por dígitos de 8 bits / 8 binários.

Se o int n era 7 e o comparamos com 1 , é como

7 (1-byte int)|    0  0  0  0    0  1  1  1
       &
1 (1-byte int)|    0  0  0  0    0  0  0  1
********************************************
Result        |    F  F  F  F    F  F  F  T

Qual F significa falso e T como verdadeiro.

Ele compara apenas o bit mais à direita se ambos forem verdadeiros. Então, automagicamente 7 & 1é T rue.

E se eu quiser verificar a parte antes da direita?

Simplesmente mude n & 1para n & 2qual 2 representa 0010em Binário e assim por diante.

Sugiro usar notação hexadecimal se você é iniciante em operações bit a bit
return n & 1;>> return n & 0x01;.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.