¿Cómo verifico si un número entero es par o impar? [cerrado]


193

¿Cómo puedo verificar si un número dado es par o impar en C?


55
La versión que usa bitwise y (&) es mucho más eficiente que la versión de módulo (%). Debe cambiar la que seleccionó como la respuesta correcta.
Stefan Rusek

66
Es poco probable que importe: el argumento es una constante. Fácil para el optimizador
MSalters

2
Los factores de legibilidad también influyen en esto.
Brian G

2
En aplicaciones integradas (el mundo donde paso la mayor parte de mi tiempo de programación), algunos procesadores tienen unidades aritméticas muy primitivas y no pueden realizar operaciones de división / módulo fácilmente. Por esta razón, generalmente uso el método bit a bit en su lugar. Sin embargo, en la CPU de un escritorio moderno, este no será el caso.
bta

3
Nunca he encontrado que la operación de módulo sea más fácil de entender. Cuando primero necesité determinar pares o impares, la máscara bit a bit fue lo primero que me vino a la mente. Es algo natural, ya que la forma en que solemos hacer esto a mano es mirar el dígito menos significativo para ver si está en {0 2 4 6 8} o {1 3 5 7 9}. Eso se traduce directamente en mirar el bit menos significativo para ver si es 0 o 1.
P Daddy

Respuestas:


449

Use el operador de módulo (%) para verificar si hay un resto al dividir por 2:

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

Algunas personas han criticado mi respuesta anterior afirmando que usar x & 1 es "más rápido" o "más eficiente". No creo que este sea el caso.

Por curiosidad, creé dos programas triviales de casos de prueba:

/* 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;
}

Luego los compilé con gcc 4.1.3 en una de mis máquinas 5 veces diferentes:

  • Sin banderas de optimización.
  • Con -O
  • Con -Os
  • Con -O2
  • Con -O3

Examiné la salida del ensamblaje de cada compilación (usando gcc -S) y descubrí que en cada caso, la salida para and.c y modulo.c eran idénticas (ambas usaban la instrucción andl $ 1,% eax). Dudo que esta sea una característica "nueva", y sospecho que se remonta a versiones antiguas. También dudo que cualquier compilador moderno (hecho en los últimos 20 años) no arcano, comercial o de código abierto, carezca de tal optimización. Probaría en otros compiladores, pero no tengo ninguno disponible en este momento.

Si a alguien más le gustaría probar otros compiladores y / o objetivos de plataforma, y ​​obtiene un resultado diferente, estaría muy interesado en saberlo.

Finalmente, la versión del módulo está garantizada por el estándar para trabajar si el entero es positivo, negativo o cero, independientemente de la representación de la implementación de enteros con signo. La versión bit a bit no lo es. Sí, me doy cuenta de que el complemento de dos es algo omnipresente, por lo que esto no es realmente un problema.


11
La pregunta específicamente preguntaba cómo hacerlo en C, así que la respondí en C, a pesar de que Chustar mencionó que no podían resolver cómo hacerlo en Java. No reclamé ni insinué que esta fuera una respuesta de Java, no conozco Java. Creo que acabo de recibir mi primer voto negativo y estoy confundido sobre por qué. Oh bien.
Chris Young

33
Yo diría que si (x% 2! = 0) {/ * x es impar * /}, pero quién sabe. No sé java tampoco.
Eugensk

9
Está obteniendo muchos votos positivos para distinguirlo de los imbéciles del operador bit a bit, sin tener que gastar nuestro karma en rechazarlos.
wnoise

13
Estoy de acuerdo con todo, excepto una cosa: me gusta mantener enteros y valores de verdad separados, conceptualmente, así que prefiero escribir "if (x% 2 == 1)". Es lo mismo para el compilador, pero quizás un poco más claro para los humanos. Además, puede usar el mismo código en idiomas que no interpretan que no es cero como verdadero.
Thomas Padron-McCarthy

46
Mi punto de referencia? ¿Qué punto de referencia? No hice ninguna evaluación comparativa. Examiné el lenguaje ensamblador generado. Esto no tiene absolutamente nada que ver con printf.
Chris Young

207

Ustedes son muuuuuuy demasiado eficientes. Lo que realmente quieres es:

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

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

  return odd;
}

Repita para isEven.

Por supuesto, eso no funciona para números negativos. Pero con el brillo viene el sacrificio ...


17
Si arrojó una excepción de argumento sobre valores negativos y observó en la documentación que esta función es O (N), entonces estaría bien con esto.
Jeffrey L Whitledge

77
La versión empresarial tendría que usar XML. Por supuesto, hoy en día tendría un servicio web que podría consultar
Martin Beckett,

58
Debe optimizar esto con una tabla de consulta.
Weeble

1
Soy un monje, tuve que hacer +1 con tu representante de 6,999 en un nuevo milenio
Eran Medan

77
¡Esto es brillante! Mi jefe me dijo que teníamos un cliente que estaba enojado porque sentía que su Licencia Enterprise no estaba dando nada más que la Licencia Estándar. ¡Ahora hemos agregado esta función en nuestro programa, y ​​solo porque se ejecuta más lentamente, él piensa que su software está haciendo MUCHO más trabajo!
Phil

97

Use aritmética de bits:

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

Esto es más rápido que usar división o módulo.


43
No creo que sea justo decir que es más rápido que usar división o módulo. El estándar C no dice nada sobre el rendimiento de los operadores, y cualquier compilador decente producirá un código rápido para cualquiera de los dos. Yo personalmente elegiría el idioma que comunica mi intención, y el% parece más apropiado aquí
Chris Young

21
Me gusta (x & 1) mejor, porque comprueba si el número es par de la misma manera que la gente: verifica si el último dígito es par o impar. En mi opinión, comunica su intención más que el método de módulo. (No es que importe mucho.)
Jeremy Ruten

2
Tienes razón, supongo que es subjetivo. Aunque la definición habitual de "par" es "entero que es divisible por 2", no "entero que termina en 0, 2, 4, 6 u 8". :-)
Chris Young

44
@TraumaPony: para ANSI estándar C y Java inicial, depende del sistema informático. No se especifica qué representación se usa para los números con signo: el cumplido de 2, el cumplido de 1, codificado en gris, etc. Pero el módulo siempre es módulo
Aaron

9
No funciona universalmente para números negativos. Consulte Verificar esta respuesta para obtener más detalles: stackoverflow.com/questions/160930/… para más detalles.
Andrew Edgecombe

36

[Modo broma = "activado"]

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 broma = "apagado"]

EDITAR: Se agregaron valores confusos a la enumeración.


2
Wow ... esto es más demente que la solución de SCdF! ¡Prestigio! Sin embargo, no hay voto a favor ... no puedo recomendar esto. Pero gracias por lo gracioso!
Wes P

1
La ventaja de este enfoque es que funciona con más que solo números. Además, si reemplaza esta línea: char bar = foo [foo.Length - 1]; con esto: doble barra = Char.GetNumericValue (foo [foo.Length - 1]); Entonces funcionará con cualquier sistema numérico.
Jeffrey L Whitledge

55
informe de error: 14.65 se informa como impar cuando debería ser desconocido.
TheSoftwareJedi

44
Software Jedi, es una "característica". ;)
Sklivvz

31
TheSoftwareJedi: 14.65 es uno de los enteros más extraños que he visto.
Bruce Alderman el

16

En respuesta a ffpf : tuve exactamente el mismo argumento con un colega hace años, y la respuesta es no , no funciona con números negativos.

El estándar C estipula que los números negativos se pueden representar de 3 maneras:

  • Complemento de 2
  • Complemento de 1
  • signo y magnitud

Comprobando así:

isEven = (x & 1);

funcionará para el complemento de 2 y la representación de signo y magnitud, pero no para el complemento de 1.

Sin embargo, creo que lo siguiente funcionará para todos los casos:

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

¡Gracias a ffpf por señalar que el cuadro de texto estaba comiendo todo después de mi carácter menos que!


Creo que a su segundo ejemplo de código le falta algo de texto.
Jeff Yates

3
¡Felicitemos esos números!
Thejh

14

Una buena es:

/*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
}

Tenga en cuenta que este método utiliza la recursividad de cola que involucra dos funciones. Se puede implementar de manera eficiente (se convierte en un ciclo while / while) si su compilador admite la recursividad de cola como un compilador de Scheme. ¡En este caso, la pila no debería desbordarse!


1
Esto no maneja bien isOdd (0).
Steve McLeod

1
Creo que tiene un bucle infinito (con recursión de cola) o un desbordamiento de pila (sin recursión de cola) para isOdd () con valores pares o isEven () con valores impares. Solo termina con verdadero. Es el problema de nuevo de nuevo.
Jeffrey L Whitledge

77
Oh, claro, arréglalo sin comentarios y hazme parecer un idiota. Esta bien.
Jeffrey L Whitledge

1
Ahora, tiene un error de compilación: en isEven no todas las rutas de código devuelven un valor. No, en realidad no he probado este código, es el compilador en mi cabeza el que se queja.
Jeffrey L Whitledge

55
error de compilación: no todas las rutas devuelven un valor de odio para bombardearlo con comentarios de errores en su código de muestra, pero qué sucede cuando llama a isEven (5)
Kevin

11

Un número es par si, cuando se divide por dos, el resto es 0. Un número es impar si, cuando se divide por 2, el resto es 1.

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

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

Los métodos son geniales!


Su método Java está roto porque num% 2 == -1 para números impares negativos.
WMR

¿Es por eso que me rechazaste?
jjnguy

3
Lo rechacé porque su función en C requiere más caracteres para escribir que lo que hace. IE num% I tiene 7 caracteres, incluidos los espacios IsOdd (I) tiene 8 caracteres. ¿Por qué crearía una función que es más larga que simplemente hacer la operación?
Kevin

13
@Kevin, en mi opinión, el código no se mide por caracteres, sino por el tiempo que le lleva escribirlo, incluido el tiempo de pensar + depurar. num% 2 tarda un milisegundo más en pensar que isOdd. ahora agregue los números a nivel mundial y perdió un año colectivo. también isOdd puede probarse y verificarse y, finalmente, certificarse sin errores (por ejemplo, manejar números negativos) donde, como num% 2, algunos desarrolladores siempre tendrán dudas y experimentarán. buen código es código que no escribes, solo reutiliza ... solo mis 2 centavos.
Eran Medan

2
@EranMedan, la misma lógica se aplicaría para reemplazar i ++ con IncrementByOne (i) y es una idea tan mala. Si un desarrollador tiene dudas sobre lo que hace num% 2, no quiero que esté cerca de mi código.
Kevin


7

Yo diría simplemente dividirlo por 2 y si hay un resto 0, es par, de lo contrario es impar.

Usar el módulo (%) lo hace fácil.

p.ej. 4% 2 = 0 por lo tanto 4 es par 5% 2 = 1 por lo tanto 5 es impar


6

Una solución más al problema
(los niños pueden 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;
}

No, no eres el tipo de niño con el que contaba :)
eugensk

Iba a votar esto, pero es un poco lento en números negativos. :)
Chris Young

3
Todos los números son brillantes y positivos. ¿O tienes prejuicios contra algunos? :))
eugensk

3
En las computadoras, todos los números una vez negativos, eventualmente se vuelven positivos. Lo llamamos Rollover of Happiness (no aplicable a BIGNUMS, YMMY, no válido en todos los estados).
Will Hartung

@WillHartung "rollover de felicidad" es genial! : D
thejh

6

Construiría una tabla de las paridades (0 si incluso 1 si es impar) de los enteros (para que uno pueda hacer una búsqueda: D), pero gcc no me deja hacer matrices de tales tamaños:

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];
}

Entonces, recurramos a la definición matemática de pares e impares.

Un número entero n es incluso si existe un número entero k tal que n = 2k.

Un número entero n es impar si existe un número entero k tal que n = 2k + 1.

Aquí está el código para ello:

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;
}

Deje que los enteros en C denoten los posibles valores de int en una compilación de C dada. (Tenga en cuenta que los enteros en C son un subconjunto de los enteros).

Ahora, uno podría preocuparse de que para un n dado en C-enteros, el entero k correspondiente podría no existir dentro de C-enteros. Pero con una pequeña prueba se puede demostrar que para todos los enteros n, | n | <= | 2n | (*), donde | n | es "n si n es positivo y -n de lo contrario". En otras palabras, para todos los n en enteros, al menos una de las siguientes retenciones (exactamente los casos (1 y 2) o los casos (3 y 4) de hecho, pero no lo probaré aquí):

Caso 1: n <= 2n.

Caso 2: -n <= -2n.

Caso 3: -n <= 2n.

Caso 4: n <= -2n.

Ahora toma 2k = n. (Tal ak existe si n es par, pero no lo probaré aquí. Si n no lo es, entonces el ciclo evenno regresa temprano de todos modos, por lo que no importa). Pero esto implica k <n si n no 0 por (*) y el hecho (nuevamente no comprobado aquí) de que para todos m, z en enteros 2m = z implica z no igual a m dado m no es 0. En el caso n es 0, 2 * 0 = 0 así que 0 es incluso que hemos terminado (si n = 0, entonces 0 está en C-enteros porque n está en C-entero en la función even, por lo tanto, k = 0 está en C-enteros). Así, tal ak en C-enteros existe para n en C-enteros si n es par.

Un argumento similar muestra que si n es impar, existe ak en enteros en C tal que n = 2k + 1.

Por lo tanto, las funciones eveny las oddpresentadas aquí funcionarán correctamente para todos los C-enteros.


1
No me refiero a ofensa, pero ¿cuál es el punto de esta respuesta? i % 2es mucho más pequeño y probablemente más eficiente.
GManNickG

2
@GMan: ¡Pero esto es mucho más determinista! Esto funcionará correctamente detectar todos los casos extremos.
P Daddy

1
... Y (!!!) es correcto !!!
Thomas Eding

No puedo decir si estás bromeando o no. : X %2funciona para todos los enteros.
GManNickG

1
+1: Iba a decir "Buena respuesta", pero creo que "Respuesta interesante" es más apropiado.
James Webster

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

2
¿Qué? Eso no es C #! Eso es puro C! :-P
asterita

8
Lanzaré un WinForm a su alrededor para que sea puro C # ...
Michael Petrotta

@mateusza: Por lo general, cuando se ve "bool" de alguna u otra capitalización en C, que es un typedefo #defineo algo así.
David Thornley

2
@mateusza @David Thornley En C99 bool es una característica estándar ( en.wikipedia.org/wiki/Stdbool.h )
fortran

1
Habla sobre paréntesis muy redundantes ...
Thomas Eding

4

Aquí hay una respuesta en 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

Prueba esto: return (((a>>1)<<1) == a)

Ejemplo:

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

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

¿Puedes explicar esto por favor? No estoy muy familiarizado con los operadores bit a bit
Abdul

Desplazar a la derecha y luego a la izquierda pondrá a cero su último bit (el más correcto). Si el nuevo número es el mismo que el original, esto significa que el último bit del número original fue 0. Entonces es par. Echa un vistazo a mi respuesta actualizada.
Kiril Aleksandrov

gracias, lo entiendo ahora
Abdul

No estoy seguro de qué enfoque es más rápido. No he tratado de compararlos.
Kiril Aleksandrov

¿No es esto también cero tu parte más significativa? Un problema con las entradas sin firmar en algunos idiomas y las entradas negativas en la mayoría ...
Troyseph

4

Al leer esta discusión bastante entretenida, recordé que tenía una función sensible al tiempo en el mundo real que evaluaba los números pares e impares dentro del bucle principal. Es una función de potencia entera, publicada en otro lugar en StackOverflow, de la siguiente manera. Los puntos de referencia fueron bastante sorprendentes. Al menos en esta función del mundo real, el módulo es más lento y significativamente más. El ganador, por un amplio margen, que requiere el 67% del tiempo del módulo, es un enfoque o (|) , y no se encuentra en ninguna otra parte de esta 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 millones de bucles, los tiempos de referencia son los siguientes.

3.962 el | y enfoque de máscara

4.851 el enfoque

5.850 el enfoque%

Para las personas que piensan que la teoría, o una lista de lenguaje ensamblador, resuelve argumentos como estos, esta debería ser una historia de advertencia. Horatio, hay más cosas en el cielo y en la tierra de las que sueñas en tu filosofía.


1
Mejor usar unsigned xcomo x = x >> 1;es el comportamiento definido por la implementación cuando x < 0. No está claro por qué xy OrMaskdifiere en tipo. Suficientemente simple para reescribir usando una while(x)prueba.
chux - Restablece Monica

2
Me pregunto qué compilador usó para comparar esto, ya que la mayoría de los compiladores deberían ser lo suficientemente inteligentes como para compilar el % 2caso utilizando bitwise &. Acabo de probar esto y los resultados son completamente iguales (VS2015, versiones de lanzamiento con todas las optimizaciones, tanto x86 como x64). La respuesta aceptada también establece esto para GCC (escrito en 2008).
Lou

2
El problema con esta publicación es que la premisa de que un bit a bit orsería más rápido que un andes altamente improbable en cualquier plataforma / compilador. Incluso si hubiera un combo de plataforma / compilador tan extraño (y no haya publicado ni eso ni el código utilizado para realizar el punto de referencia), depender de que otros compiladores se comporten de la misma manera sería una mala apuesta de optimización. Entonces, como escribí, me pregunto en qué plataforma / compilador se probó esto , porque estoy casi seguro de que no se midió correctamente.
Lou

2
No te llamé mentiroso, solo dije con gran certeza que no mediste correctamente. Todavía no es necesario llamarme camionero, lea mi comentario original: hice un punto de referencia, y los resultados fueron, como se esperaba, completamente iguales en los tres casos (certeza de ~ 3 sigma, después de ejecutar cada prueba 10 veces por 500,000 .000 iteraciones). Si realmente tiene una larga e ilustre carrera, dé un paso atrás y piense si sus afirmaciones tienen sentido, luego publique el código real utilizado para hacer el punto de referencia. De lo contrario, la publicación es lo que creo que es, solo un error en la medición.
Lou

1
Hecho .
Lou

4

Este es un seguimiento de la discusión con @RocketRoy con respecto a su respuesta , pero podría ser útil para cualquiera que quiera comparar estos resultados.

tl; dr Por lo que he visto, el enfoque de Roy ( (0xFFFFFFFF == (x | 0xFFFFFFFE)) no está completamente optimizado x & 1como el modenfoque, pero en la práctica los tiempos de ejecución deberían ser iguales en todos los casos.

Entonces, primero comparé la salida compilada usando Compiler Explorer :

Funciones probadas:

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 con -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 con -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

Gracias a CLang, se dio cuenta de que los tres casos son funcionalmente iguales. Sin embargo, el enfoque de Roy no está optimizado en GCC, por lo que YMMV.

Es similar con Visual Studio; Al inspeccionar la versión de desensamblaje x64 (VS2015) para estas tres funciones, pude ver que la parte de comparación es igual para "mod" y "y" casos, y un poco más grande para el caso de Roy "o":

// 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)

Sin embargo, después de ejecutar un punto de referencia real para comparar estas tres opciones (mod simple, bit a bit o bit a bit y), los resultados fueron completamente iguales (de nuevo, Visual Studio 2005 x86 / x64, versión de compilación, sin depurador adjunto).

El ensamblaje de lanzamiento usa las testinstrucciones andy los modcasos, mientras que el caso de Roy usa el cmp eax,0FFFFFFFFhenfoque, pero está muy desenrollado y optimizado, por lo que no hay diferencia en la práctica.

Mis resultados después de 20 ejecuciones (i7 3610QM, plan de energía de Windows 10 establecido en Alto rendimiento):

[Prueba: Mod 2 simple] TIEMPO PROMEDIO: 689.29 ms (Diferencia relativa: + 0.000%)
[Prueba: Bitwise o] TIEMPO PROMEDIO: 689,63 ms (Diferencia relativa: + 0,048%)
[Prueba: a nivel de bits y] TIEMPO PROMEDIO: 687,80 ms (diferencia relativa: -0,217%)

La diferencia entre estas opciones es inferior al 0.3%, por lo que es bastante obvio que el ensamblaje es igual en todos los casos.

Aquí está el código si alguien quiere probar, con una advertencia de que solo lo probé en Windows (verifique el #if LINUXcondicional para la get_timedefinición e impleméntelo si es necesario, tomado de esta respuesta ).

#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;
}

Creo que has cometido el Cardenal Sin de la evaluación comparativa; creando uno tan específico que no represente un entorno del mundo real. Mire su lenguaje ensamblador y observe los pocos registros que está utilizando. Calificaciones altas para el esfuerzo, pero estos resultados no se mantendrán en el procesamiento del mundo real.

@RocketRoy: dado que todas las salidas son exactamente iguales para los tres casos (bueno, un poco peor para su programa en un caso), realmente no me importa cuántos registros se usaron. Pero de nuevo, siéntase libre de crear y publicar un programa / entorno de ejemplo que confundirá al compilador para crear un ensamblaje más optimizado en uno de los casos, siendo todo lo demás igual.
Lou

Siempre me gustan los programadores engreídos. Es un buen rasgo para un programador, pero en un programa más complejo del mundo real, mi método funcionará mejor que el suyo porque el compilador tiene más formas de resolver el problema para que las instrucciones se superpongan (en arquitecturas Intel) produciendo mejores resultados . Muy pocos programadores veteranos con buena experiencia en benchmarking preferirían su benchmark, pero sigan con el buen trabajo y recuerden volver a ejecutar sus benchmarks cuando salgan nuevos lanzamientos de chips. Las cosas cambian con el tiempo.

3

Sé que esto es solo azúcar sintáctico y solo aplicable en .net, pero qué pasa con el método de extensión ...

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

Ahora puedes hacer lo siguiente

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

1
Buen código Lástima que afirme que 2 es impar y 3 no lo es.
Anthony

oops, lo siento ... mi lógica es incorrecta ...
rudigrobler

3

En la "categoría creativa pero confusa" ofrezco:

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

Una variante sobre este tema que es específica de 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

El método bit a bit depende de la representación interna del número entero. El módulo funcionará en cualquier lugar donde haya un operador de módulo. Por ejemplo, algunos sistemas realmente usan los bits de bajo nivel para el etiquetado (como los lenguajes dinámicos), por lo que el x & 1 sin procesar en realidad no funcionará en ese caso.


2

IsOdd (int x) {return true; }

Prueba de corrección: considere el conjunto de todos los enteros positivos y suponga que hay un conjunto de enteros no vacío que no es impar. Debido a que los enteros positivos están bien ordenados, habrá un número más pequeño, no impar, que en sí mismo es bastante extraño, por lo que claramente ese número no puede estar en el conjunto. Por lo tanto, este conjunto no puede estar no vacío. Repita para enteros negativos, excepto busque el mayor número no impar.


2

Portátil:

i % 2 ? odd : even;

Inportable:

i & 1 ? odd : even;

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

2

Como algunas personas han publicado, hay numerosas formas de hacerlo. Según este sitio web , la forma más rápida es el operador de módulo:

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

Sin embargo, aquí hay otro código que fue marcado por el autor que corrió más lento que la operación de módulo común anterior:

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

¿Cuántas personas sabían del método Math.System.DivRem o por qué lo usarían?



1

Para dar más detalles sobre el método del operador bit a bit para aquellos de nosotros que no hicimos mucho álgebra booleana durante nuestros estudios, aquí hay una explicación. Probablemente no sea de mucha utilidad para el OP, pero sentí que quería aclarar por qué NUMBER & 1 funciona.

Tenga en cuenta que, como alguien respondió anteriormente, la forma en que se representan los números negativos puede detener este método. De hecho, incluso puede romper el método del operador de módulo también, ya que cada idioma puede diferir en la forma en que trata los operandos negativos.

Sin embargo, si sabe que NUMBER siempre será positivo, esto funciona bien.

Como Tooony mencionó anteriormente, solo el último dígito en binario (y denario) es importante.

Una puerta AND lógica booleana dicta que ambas entradas deben ser 1 (o alto voltaje) para que 1 sea devuelto.

1 y 0 = 0.

0 y 1 = 0.

0 y 0 = 0.

1 y 1 = 1.

Si representa cualquier número como binario (he usado una representación de 8 bits aquí), los números impares tienen 1 al final, los números pares tienen 0.

Por ejemplo:

1 = 00000001

2 = 00000010

3 = 00000011

4 = 00000100

Si toma cualquier número y usa Y bit a bit (y en Java) por 1, devolverá 00000001, = 1, lo que significa que el número es impar. O 00000000 = 0, lo que significa que el número es par.

P.ej

¿Es impar?

1 y 1 =

00000001 y

00000001 =

00000001 <- impar

2 y 1 =

00000010 y

00000001 =

00000000 <- Par

54 y 1 =

00000001 y

00110110 =

00000000 <- Par

Por eso funciona esto:

if(number & 1){

   //Number is odd

} else {

   //Number is even
}

Lo siento si esto es redundante.


1

Número cero paridad | cero http://tinyurl.com/oexhr3k

Secuencia de código de 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))

Salida:

   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, gracias. Si Zero es par, ¿cuántos pares tiene?

@ el.pescado, Ok, estoy de acuerdo contigo. Entonces, si pensamos un poco, ¿por qué dividimos a 2 (dos)? ¿Qué queremos saber cuando dividimos a dos? ¿Por qué no dividir a 3, o, 5, etc.?

@ el.pescado Este artículo de Wikipedia Parity of Zero está mal. Mucha gente ha sido engañada por este artículo. Piensa antes de Wink.

1
Tienes razón. Ahora que he leído otras respuestas, encontré la suya más completa :)
el.pescado

@ el.pescado. Gracias. :) Ahora eres el mejor amigo de Zero. (abrazo)

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 el bien de la discusión ...

Solo necesita mirar el último dígito en cualquier número dado para ver si es par o impar. Firmado, sin firmar, positivo, negativo: todos son iguales con respecto a esto. Entonces esto debería funcionar en todos los aspectos:

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);
  }
}

La clave aquí está en la tercera línea de código, el operador de división realiza una división entera, por lo que al resultado le falta la parte fraccionaria del resultado. Entonces, por ejemplo, 222/10 dará 22 como resultado. Luego multiplícalo nuevamente por 10 y tendrás 220. Resta eso del 222 original y terminarás con 2, que por arte de magia es el mismo número que el último dígito en el número original. ;-) Los paréntesis están allí para recordarnos el orden en que se realiza el cálculo. Primero, haga la división y la multiplicación, luego reste el resultado del número original. Podríamos dejarlos fuera, ya que la prioridad es mayor para la división y la multiplicación que para la resta, pero esto nos da un código "más legible".

Podríamos hacerlo completamente ilegible si quisiéramos. No haría ninguna diferencia para un compilador moderno:

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

Pero haría el código mucho más difícil de mantener en el futuro. Solo imagine que le gustaría cambiar el texto de los números impares a "no es par". Luego, otra persona más adelante quiere saber qué cambios realizó y realizar un svn diff o similar ...

Si no le preocupa la portabilidad, sino más bien la velocidad, puede echar un vistazo al bit menos significativo. Si ese bit se establece en 1, es un número impar, si es 0 es un número par. En un pequeño sistema endian, como la arquitectura x86 de Intel, sería algo como esto:

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

¿Qué es exactamente lo que está mal con solo pasar iToTest% 2 == 0? Estás desperdiciando una división extrayendo el último dígito, por lo que el tuyo es dos veces más lento de lo necesario.
Freespace

@freespace: desperdicio más que eso, ¿no? :-) Una multiplicación y una resta también. Pero lo que es más eficiente entre las dos soluciones no me atrevo a decir. Nunca afirmé que esta fuera la solución más rápida, todo lo contrario si lees la primera línea de mi publicación nuevamente.
Tooony

@ Toooony, ah, mi sombrero de humor se cayó. Ahora está formalmente encendido: D Perdón por eso :)
espacio libre

0

Si quiere ser eficiente, use operadores bit a bit (x & 1 ), pero si quiere ser legible use el módulo 2 ( x % 2)


-1: Si quieres ser eficiente, usa cualquiera de los dos. Si quieres que sea portátil, úsalo %. Si quieres que sea legible, úsalo %. Hmmm, veo un patrón aquí.
Thomas Eding

@trinithis, no hay patrón y esta solución es mucho mejor que la tuya.
Subs

0

Verificar pares o impares es una tarea simple.

Sabemos que cualquier número exactamente divisible por 2 es un número par más impar.

Solo necesitamos verificar la divisibilidad de cualquier número y para verificar la divisibilidad usamos % operador

Comprobando incluso impar usando si más

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

Programa C para verificar pares o impares usando si no

Uso del operador condicional / ternario

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

Programa C para verificar pares o impares usando el operador condicional .

Usando el operador Bitwise

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

¿Y dónde está exactamente el operador ternario?
Beyondo

0

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

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

El código verifica el último bit del entero si es 1 en binario

Explicación

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 el bit más a la derecha siempre es 1 para los números impares .

los & operador AND comprueba el bit más a la derecha en nuestro retorno de línea si se trata de 1

Piensa en ello como verdadero y falso

Cuando comparamos n con 1, lo que significa 0001en binario (el número de ceros no importa).
entonces imaginemos que tenemos el número entero n con un tamaño de 1 byte.

Estaría representado por dígitos binarios de 8 bits / 8.

Si int n era 7 y lo comparamos con 1 , es 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

Cuál F significa falso y T significa verdadero.

Se compara solamente el bit más a la derecha si son ambas verdaderas. Entonces, automágicamente 7 & 1es T rue.

¿Qué sucede si quiero verificar el bit antes del extremo derecho?

Simplemente cambie n & 1a n & 2qué 2 representa 0010en binario y así sucesivamente.

Sugiero usar la notación hexadecimal si eres un principiante en operaciones bit a bit
return n & 1;>> return n & 0x01;.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.