Come posso verificare se un numero intero è pari o dispari? [chiuso]


193

Come posso verificare se un dato numero è pari o dispari in C?


5
La versione che utilizza bit a bit e (&) è molto più efficiente della versione modulo (%). È necessario modificare quella selezionata come risposta corretta.
Stefan Rusek,

6
È improbabile che importi: l'argomento è una costante. Facile per l'ottimizzatore
MSalters

2
Fattori di leggibilità anche in questo.
Brian G,

2
Nelle applicazioni integrate (il mondo in cui passo gran parte del mio tempo di programmazione), alcuni processori hanno unità aritmetiche molto primitive e non possono eseguire facilmente operazioni di divisione / modulo. Per questo motivo, di solito utilizzo invece il metodo bitwise e. Tuttavia, sulla CPU di un desktop moderno non sarà così.
dal

3
Non ho mai trovato l'operazione del modulo più facile da capire. Quando ho dovuto determinare per la prima volta pari o dispari, la maschera bit a bit era la prima cosa che mi veniva in mente. È in qualche modo naturale, poiché il modo in cui tendiamo a farlo manualmente è guardare la cifra meno significativa per vedere se è in {0 2 4 6 8} o {1 3 5 7 9}. Ciò si traduce direttamente nel guardare il bit meno significativo per vedere se è 0 o 1.
P Papà,

Risposte:


449

Usa l'operatore modulo (%) per verificare se c'è un resto quando dividi per 2:

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

Alcune persone hanno criticato la mia risposta sopra affermando che l'uso di x & 1 è "più veloce" o "più efficiente". Non credo che sia così.

Per curiosità, ho creato due banali programmi per test case:

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

Ho quindi compilato questi con gcc 4.1.3 su una delle mie macchine 5 volte diverse:

  • Senza flag di ottimizzazione.
  • Con -O
  • Con -Os
  • Con -O2
  • Con -O3

Ho esaminato l'output dell'assembly di ogni compilazione (usando gcc -S) e ho scoperto che in ogni caso, l'output per and.c e modulo.c erano identici (entrambi hanno usato l'istruzione andl $ 1,% eax). Dubito che questa sia una "nuova" funzionalità e sospetto che risale alle versioni antiche. Dubito anche che qualsiasi compilatore non arcano moderno (realizzato negli ultimi 20 anni), commerciale o open source, manchi di tale ottimizzazione. Vorrei testare su altri compilatori, ma al momento non ne ho a disposizione.

Se a qualcun altro piacerebbe testare altri compilatori e / o target di piattaforme e ottenere un risultato diverso, sarei molto interessato a saperlo.

Infine, la versione del modulo è garantita dallo standard per funzionare indipendentemente dal fatto che l'intero sia positivo, negativo o zero, indipendentemente dalla rappresentazione dell'implementazione degli interi con segno. La versione bit per bit non lo è. Sì, mi rendo conto che il complemento a due è un po 'onnipresente, quindi questo non è davvero un problema.


11
La domanda in particolare mi ha chiesto come farlo in C, quindi ho risposto in C, nonostante Chustar menzionasse che non potevano capire come farlo in Java. Non ho affermato o sottinteso che questa era una risposta Java, non conosco Java. Penso di aver appena ottenuto il mio primo voto negativo e sono confuso sul perché. Oh bene.
Chris Young,

33
Direi se (x% 2! = 0) {/ * x è dispari * /}, ma chi lo sa. Non conosco neanche Java.
eugensk,

9
Sta ottenendo molti voti positivi per distinguerlo dai deficienti degli operatori bit per bit, senza dover spendere il nostro karma per votarli.
wnoise,

13
Sono d'accordo con tutto, tranne una cosa: mi piace mantenere numeri interi e valori di verità separati, concettualmente, quindi preferisco scrivere "if (x% 2 == 1)". È lo stesso per il compilatore, ma forse un po 'più chiaro per gli umani. Inoltre puoi usare lo stesso codice in lingue che non interpretano lo zero come vero.
Thomas Padron-McCarthy,

46
Il mio punto di riferimento? Quale benchmark? Non ho fatto alcun benchmarking. Ho esaminato il linguaggio assembly generato. Questo non ha assolutamente nulla a che fare con printf.
Chris Young,

207

Ragazzi, siete troppo efficienti. Quello che vuoi davvero è:

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

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

  return odd;
}

Ripeti per isEven.

Naturalmente, ciò non funziona per i numeri negativi. Ma con lo splendore arriva il sacrificio ...


17
Se hai lanciato un'eccezione argomento su valori negativi e hai notato nella documentazione che questa funzione è O (N), allora andrei bene con questo.
Jeffrey L Whitledge,

7
La versione aziendale dovrebbe usare XML. Ovviamente al giorno d'oggi avresti un servizio web che potresti interrogare
Martin Beckett,

58
Dovresti ottimizzarlo con una tabella di ricerca.
Weeble,

1
Sono un tale monaco, ho dovuto +1 i tuoi 6.999 rappresentanti in un nuovo millennio
Eran Medan,

7
È brillante! Il mio capo mi disse che avevamo un cliente arrabbiato perché sentiva che la sua Licenza Enterprise non dava altro che la Licenza standard. Ora abbiamo aggiunto questa funzione nel nostro programma, e solo perché si esegue più lentamente, pensa che il suo software stia WAY più lavoro !!!
Phil

97

Usa l'aritmetica dei bit:

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

Questo è più veloce rispetto all'uso della divisione o del modulo.


43
Non penso sia giusto dire che è più veloce dell'uso della divisione o del modulo. Lo standard C non dice nulla sulle prestazioni degli operatori e qualsiasi compilatore decente produrrà codice veloce per entrambi. Personalmente sceglierei il linguaggio che comunica il mio intento, e% sembra più appropriato qui
Chris Young,

21
Mi piace (x & 1) meglio, perché controlla se il numero è uguale allo stesso modo delle persone: controlla se l'ultima cifra è pari o dispari. Secondo me comunica il suo intento più del metodo modulo. (Non che importi molto.)
Jeremy Ruten,

2
Hai ragione, immagino sia soggettivo. Sebbene la definizione abituale di "pari" sia "numero intero divisibile per 2", non "numero intero che termina in 0, 2, 4, 6 o 8". :-)
Chris Young,

4
@TraumaPony - per ANSI standard C e Java iniziale, dipende dal sistema informatico. Non è specificato quale rappresentazione venga utilizzata per i numeri firmati - complimento 2, complimento 1, codice grigio, ecc. Ma il modulo è sempre modulo
Aaron

9
Non funziona universalmente per numeri negativi. Vedere Controlla questa risposta per maggiori dettagli: stackoverflow.com/questions/160930/… per i dettagli.
Andrew Edgecombe,

36

[Modalità Joke = "on"]

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

[Modalità Joke = "off"]

EDIT: aggiunti valori confusi all'enum.


2
Wow ... questo è più demente della soluzione di SCdF! Complimenti! Nessun voto però ... non posso raccomandarlo. Ma grazie per il divertimento!
Wes P

1
Il vantaggio di questo approccio è che funziona con più di semplici numeri. Inoltre, se si sostituisce questa riga: char bar = foo [foo.Length - 1]; con questo: double bar = Char.GetNumericValue (foo [foo.Length - 1]); Quindi funzionerà con qualsiasi sistema numerico.
Jeffrey L Whitledge,

5
segnalazione bug: 14.65 è segnalato come dispari quando dovrebbe essere sconosciuto.
TheSoftwareJedi

4
Software Jedi, è una "caratteristica". ;)
Sklivvz,

31
TheSoftwareJedi: 14.65 è uno dei numeri più strani che abbia mai visto.
Bruce Alderman,

16

In risposta a ffpf - ho avuto esattamente lo stesso argomento con un collega anni fa, e la risposta è no , non funziona con numeri negativi.

Lo standard C stabilisce che i numeri negativi possono essere rappresentati in 3 modi:

  • Complemento a 2
  • Complemento a 1
  • segno e grandezza

Verifica in questo modo:

isEven = (x & 1);

funzionerà per il complemento di 2 e la rappresentazione di segni e grandezza, ma non per il complemento di 1.

Tuttavia, credo che quanto segue funzionerà per tutti i casi:

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

Grazie a ffpf per aver sottolineato che la casella di testo stava mangiando tutto dopo il mio personaggio meno che!


Penso che al tuo secondo esempio di codice manchi del testo.
Jeff Yates,

3
Facciamo i complimenti a quei numeri!
thejh

14

Una bella è:

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

Si noti che questo metodo utilizza la ricorsione della coda che coinvolge due funzioni. Può essere implementato in modo efficiente (trasformato in un po '/ fino al tipo di loop) se il tuo compilatore supporta la ricorsione della coda come un compilatore Scheme. In questo caso lo stack non dovrebbe traboccare!


1
Questo non gestisce bene isOdd (0).
Steve McLeod,

1
Penso che tu abbia un ciclo infinito (con ricorsione della coda) o uno stack overflow (senza ricorsione della coda) per isOdd () con qualsiasi valore pari o isEven () con qualsiasi valore dispari. Termina solo con true. È di nuovo il problema dell'arresto.
Jeffrey L Whitledge,

7
Oh, certo, aggiustalo senza commenti e fammi sembrare un idiota. Va bene.
Jeffrey L Whitledge,

1
Ora hai un errore di compilazione: in isEven non tutti i percorsi di codice restituiscono un valore. No, in realtà non ho provato questo codice, è il compilatore nella mia testa che si lamenta.
Jeffrey L Whitledge,

5
errore di compilazione: non tutti i percorsi restituiscono un odio di valore per bombardarti con commenti di bug sul tuo codice di esempio, ma cosa succede quando chiami isEven (5)
Kevin,

11

Un numero è anche se, quando diviso per due, il resto è 0. Un numero è dispari se, quando diviso per 2, il resto è 1.

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

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

I metodi sono fantastici!


Il tuo metodo Java non funziona perché num% 2 == -1 per numeri dispari negativi.
WMR,

È per questo che mi hai sottovalutato?
jjnguy,

3
L'ho ridimensionato perché la tua funzione in C richiede più caratteri da digitare rispetto a ciò che fa. IE num% I è composto da 7 caratteri compresi gli spazi IsOdd (I) è composto da 8 caratteri. Perché dovresti creare una funzione che è più lunga della semplice operazione?
Kevin,

13
@Kevin secondo me il codice non viene misurato dai caratteri, ma piuttosto dal tempo impiegato per scriverlo, incluso think + tempo di debug. num% 2 impiega un millisecondo in più a pensare di isOdd. ora aggiungi i numeri a livello globale e hai perso un anno collettivo. inoltre isOdd può essere testato, verificato e infine certificato privo di bug (ad es. gestione di numeri negativi) dove num% 2 - alcuni sviluppatori avranno sempre dei dubbi e continueranno a sperimentare. un buon codice è un codice che non scrivi, riutilizza solo ... solo i miei 2 centesimi.
Eran Medan,

2
@EranMedan, La stessa logica si applicherebbe alla sostituzione di i ++ con IncrementByOne (i) ed è un'idea altrettanto negativa. Se uno sviluppatore ha dubbi su ciò che fa num% 2, non lo voglio vicino al mio codice.
Kevin,


7

Direi di dividerlo per 2 e se c'è un resto 0, è pari, altrimenti è strano.

L'uso del modulo (%) lo rende facile.

per esempio. 4% 2 = 0 quindi 4 è pari al 5% 2 = 1 quindi 5 è dispari


6

Un'altra soluzione al problema
(i bambini sono invitati a votare)

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, non sei il tipo di bambino su cui ho contato :)
eugensk,

Stavo per votare questo, ma è un po 'lento su numeri negativi. :)
Chris Young,

3
Tutti i numeri sono luminosi e positivi. O sei prevenuto contro qualcuno? :))
eugensk,

3
Nei computer, tutti i numeri una volta negativi, alla fine diventano positivi. Lo chiamiamo Rollover of Happiness (non applicabile a BIGNUMS, YMMY, non valido in tutti gli stati).
Will Hartung,

@WillHartung "rollover della felicità" è fantastico! : D
thejh

6

Costruirò una tabella delle parità (0 se anche 1 se dispari) degli interi (quindi si potrebbe fare una ricerca: D), ma gcc non mi permetterà di fare matrici di tali dimensioni:

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

Quindi ricorrere invece alla definizione matematica di pari e dispari.

Un numero intero n è anche se esiste un numero intero k tale che n = 2k.

Un numero intero n è dispari se esiste un numero intero k tale che n = 2k + 1.

Ecco il codice per questo:

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

Consenti ai numeri C di indicare i possibili valori di intin una data compilazione C. (Si noti che C-interi è un sottoinsieme degli interi.)

Ora ci si potrebbe preoccupare che per un dato n in numeri interi C il numero intero k corrispondente potrebbe non esistere all'interno di numeri C. Ma con una piccola prova si può dimostrare che per tutti gli interi n, | n | <= | 2n | (*), dove | n | è "n se n è positivo e -n altrimenti". In altre parole, per tutte le n in numeri interi almeno una delle seguenti è valida (esattamente sia i casi (1 e 2) che i casi (3 e 4), ma non lo dimostrerò qui):

Caso 1: n <= 2n.

Caso 2: -n <= -2n.

Caso 3: -n <= 2n.

Caso 4: n <= -2n.

Ora prendi 2k = n. (Tale ak esiste se n è pari, ma non lo dimostrerò qui. Se n non è nemmeno allora il loop in evennon riesce a tornare presto comunque, quindi non importa.) Ma questo implica k <n se n non 0 per (*) e il fatto (ancora non dimostrato qui) che per tutti m, z in numeri interi 2m = z implica z non uguale a m dato m non è 0. Nel caso n è 0, 2 * 0 = 0 quindi anche 0 è fatto (se n = 0 allora 0 è in numeri interi C perché n è in numero intero C nella funzione even, quindi k = 0 è in numeri interi C). Quindi tale ak negli interi C esiste per n negli interi C se n è pari.

Un argomento simile mostra che se n è dispari, esiste ak in C-interi tale che n = 2k + 1.

Quindi le funzioni evene oddqui presentate funzioneranno correttamente per tutti gli interi C.


1
Non intendo offesa, ma che senso ha questa risposta? i % 2è molto più piccolo e probabilmente più efficiente.
GManNickG,

2
@GMan: Ma questo è molto più deterministico! Questo funzionerà correttamente per rilevare tutti i casi limite.
P Papà,

1
... E (!!!) è corretto !!!
Thomas Eding,

Non so dire se stai scherzando o no. : X %2funziona per tutti i numeri interi.
GManNickG,

1
+1: Stavo per dire "Buona risposta", ma penso che "Risposta interessante" sia più appropriato.
James Webster,

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

2
Che cosa? Questo non è C #! Questo è puro C! :-P
asterite

8
Lancerò un WinForm attorno per renderlo puro C # ...
Michael Petrotta,

@mateusza: Di solito quando vedi "bool" in una maiuscola o in altra in C, è un typedefo #defineo qualcosa del genere.
David Thornley,

2
@mateusza @David Thornley In C99 bool è una funzionalità standard ( en.wikipedia.org/wiki/Stdbool.h )
fortran,

1
Parliamo di parentesi enormemente ridondanti ...
Thomas Eding,

4

Ecco una risposta in 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

Prova questo: return (((a>>1)<<1) == a)

Esempio:

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

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

Puoi spiegarlo per favore? Non ho molta familiarità con gli operatori bit a bit
Abdul

Spostando a destra e poi a sinistra si azzera l'ultimo bit (quello più a destra). Se il nuovo numero è uguale all'originale, significa che l'ultimo bit del numero originale era 0. Quindi è pari. Dai un'occhiata alla mia risposta aggiornata.
Kiril Aleksandrov,

grazie, lo capisco adesso
Abdul

Non sono sicuro di quale approccio sia più veloce. Non ho provato a confrontarli.
Kiril Aleksandrov,

Questo non azzera anche il tuo bit più significativo? Un problema con ints non firmati in alcune lingue e in negativo in molti ...
Troyseph,

4

Leggendo questa discussione piuttosto divertente, mi sono ricordato che avevo una funzione sensibile al tempo reale che verificava i numeri pari e dispari all'interno del ciclo principale. È una funzione di potenza intera, pubblicata altrove su StackOverflow, come segue. I benchmark sono stati abbastanza sorprendenti. Almeno in questa funzione del mondo reale, il modulo è più lento , e in modo significativo. Il vincitore, con un ampio margine, che richiede il 67% del tempo di modulo, è un approccio o (|) , e non si trova da nessuna parte in questa pagina.

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

Per 300 milioni di loop, i tempi di riferimento sono i seguenti.

3.962 il | e approccio alla maschera

4.851 l'approccio &

5.850 l'approccio%

Per le persone che pensano che la teoria, o un elenco di lingue assembleari, risolva argomenti come questi, questo dovrebbe essere un ammonimento. Ci sono più cose in cielo e in terra, Orazio, di quanto si sogna nella tua filosofia.


1
Meglio usare unsigned xcome x = x >> 1;è il comportamento definito dall'implementazione quando x < 0. Non è chiaro il perché xe OrMaskdifferiscono nel tipo. Abbastanza semplice da riscrivere usando un while(x)test.
chux - Ripristina Monica

2
Mi chiedo quale compilatore hai usato per confrontarlo, dal momento che la maggior parte dei compilatori dovrebbe essere abbastanza intelligente da compilare il % 2caso usando bit a bit &. Ho appena provato questo e i risultati sono completamente gli stessi (VS2015, Release build con tutte le ottimizzazioni, sia x86 che x64). La risposta accettata lo afferma anche per GCC (scritto nel 2008).
Lou

2
Il problema con questo post è che la premessa che un bit per bit orsarebbe più veloce di un andè altamente improbabile, su qualsiasi piattaforma / compilatore. Anche se ci fosse una combinazione così strana di piattaforma / compilatore (e non hai pubblicato né quello né il codice utilizzato per eseguire il benchmark), a seconda che altri compilatori si comportino allo stesso modo sarebbe una scommessa di ottimizzazione scadente. Quindi, come ho scritto, mi chiedo su quale piattaforma / compilatore sia stato testato , perché sono quasi certo che non sia stato misurato correttamente.
Lou,

2
Non chiamarti bugiardo, ma solo affermare con alta certezza che non hai misurato correttamente. Non ho ancora bisogno di chiamarmi camionista, leggi il mio commento originale: ho fatto un benchmark e i risultati, come previsto, erano completamente gli stessi in tutti e tre i casi (certezza di ~ 3 sigma, dopo aver eseguito ogni test 10 volte per 500.000 .000 iterazioni). Se hai davvero una lunga carriera illustre, fai un passo indietro e pensa se le tue affermazioni hanno un senso, quindi pubblica il codice effettivo utilizzato per fare il benchmark. Altrimenti, il post è quello che credo sia, solo un errore di misurazione.
Lou

1
Fatto .
Lou

4

Questo è il seguito della discussione con @RocketRoy sulla sua risposta , ma potrebbe essere utile a chiunque desideri confrontare questi risultati.

tl; dr Da quanto ho visto, l'approccio di Roy ( (0xFFFFFFFF == (x | 0xFFFFFFFE)) non è completamente ottimizzato x & 1come modapproccio, ma in pratica i tempi di esecuzione dovrebbero risultare uguali in tutti i casi.

Quindi, per prima cosa ho confrontato l'output compilato usando Compiler Explorer :

Funzioni testate:

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

Cappelli fino a CLang, ha capito che tutti e tre i casi sono funzionalmente uguali. Tuttavia, l'approccio di Roy non è ottimizzato in GCC, quindi YMMV.

È simile con Visual Studio; ispezionando lo smontaggio Release x64 (VS2015) per queste tre funzioni, ho potuto vedere che la parte di confronto è uguale per i casi "mod" e "e" e leggermente più grande per il caso "o" di 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)

Tuttavia, dopo aver eseguito un benchmark effettivo per il confronto di queste tre opzioni (mod semplice, bit a bit o, bit a bit e), i risultati sono stati completamente uguali (di nuovo, Visual Studio 2005 x86 / x64, versione di rilascio, nessun debugger collegato).

L'assemblea di rilascio usa l' testistruzione per ande modcasi, mentre il caso di Roy usa l' cmp eax,0FFFFFFFFhapproccio, ma è fortemente srotolato e ottimizzato, quindi non c'è differenza nella pratica.

I miei risultati dopo 20 corse (i7 3610QM, piano di alimentazione di Windows 10 impostato su Prestazioni elevate):

[Test: Plain mod 2] TEMPO MEDIO: 689.29 ms (differenza relativa: + 0.000%)
[Test: bit a bit o] TEMPO MEDIO: 689.63 ms (differenza relativa: + 0,048%)
[Test: bit per bit e] TEMPO MEDIO: 687.80 ms (differenza relativa: -0.217%)

La differenza tra queste opzioni è inferiore allo 0,3%, quindi è abbastanza ovvio che l'assemblaggio è uguale in tutti i casi.

Ecco il codice se qualcuno vuole provare, con un avvertimento che ho testato solo su Windows (controlla il #if LINUXcondizionale per la get_timedefinizione e implementalo se necessario, preso da questa risposta ).

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

Credo che tu abbia commesso il cardinale Sin di benchmarking; crearne uno così specifico da non rappresentare un ambiente reale. Guarda il tuo linguaggio assembly e nota quanti registri stai utilizzando. Un punteggio elevato per lo sforzo, ma questi risultati non reggeranno nell'elaborazione del mondo reale.

@RocketRoy: poiché tutte le uscite sono esattamente le stesse per tutti e tre i casi (beh, leggermente peggio per il tuo programma in un caso), non mi interessa davvero quanti registri sono stati usati. Tuttavia, sentiti libero di creare e pubblicare un tale programma / ambiente di esempio che confonderà il compilatore per creare un assembly più ottimizzato in uno dei casi, a parità di condizioni.
Lou

Mi piacciono sempre i programmatori arroganti. È una buona caratteristica per un programmatore, ma in un programma più complesso e reale, il mio metodo funzionerà meglio del tuo perché il compilatore ha più modi per risolvere il problema in modo che le istruzioni si sovrappongano (sulle architetture Intel) producendo risultati migliori . Pochissimi programmatori veterani con una buona esperienza di benchmarking preferirebbero il tuo benchmark, ma continuerebbero a lavorare bene e si ricorderebbero di rieseguire i benchmark quando usciranno nuove versioni di chip. Le cose cambiano nel tempo.

3

So che questo è solo zucchero sintattico e applicabile solo in .net ma per quanto riguarda il metodo di estensione ...

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

Ora puoi fare quanto segue

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

1
Bel codice. Peccato che affermerà che 2 è dispari e 3 no.
Anthony,

oops, scusa ... la mia logica è sbagliata ...
rudigrobler,

3

Nella "categoria creativa ma confusa" offro:

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

Una variante di questo tema specifica per 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

Il metodo bit a bit dipende dalla rappresentazione interna dell'intero. Modulo funzionerà ovunque ci sia un operatore modulo. Ad esempio, alcuni sistemi utilizzano effettivamente i bit di basso livello per la codifica (come i linguaggi dinamici), quindi la x e 1 non funzioneranno in quel caso.


2

IsOdd (int x) {return true; }

Prova di correttezza: considera l'insieme di tutti i numeri interi positivi e supponi che esista un insieme non vuoto di numeri interi che non sono dispari. Poiché gli interi positivi sono ben ordinati, ci sarà un numero non dispari più piccolo, che di per sé è piuttosto dispari, quindi chiaramente quel numero non può essere nell'insieme. Pertanto, questo set non può essere non vuoto. Ripetere l'operazione per numeri interi negativi, tranne cercare il numero maggiore e non dispari.


2

Portatile:

i % 2 ? odd : even;

portabile:

i & 1 ? odd : even;

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

2

Come alcune persone hanno pubblicato, ci sono molti modi per farlo. Secondo questo sito Web , il modo più veloce è l'operatore del modulo:

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

Tuttavia, ecco qualche altro codice che è stato contrassegnato dal banco dall'autore e che ha funzionato più lentamente dell'operazione modulo comune sopra:

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

Quante persone conoscevano persino il metodo Math.System.DivRem o perché lo avrebbero usato ??



1

Per dare maggiore elaborazione sul metodo dell'operatore bit a bit per quelli di noi che non hanno fatto molta algebra booleana durante i nostri studi, ecco una spiegazione. Probabilmente non serve molto all'OP, ma mi è sembrato di chiarire perché NUMBER & 1 funzionano.

Nota come quando qualcuno ha risposto sopra, il modo in cui sono rappresentati i numeri negativi può impedire a questo metodo di funzionare. In effetti, può anche rompere anche il metodo dell'operatore modulo poiché ogni lingua può differire nel modo in cui tratta gli operandi negativi.

Tuttavia, se sai che NUMBER sarà sempre positivo, questo funziona bene.

Come Tooony sopra ha sottolineato che solo l'ultima cifra in binario (e diniego) è importante.

Una porta AND logica booleana impone che entrambi gli ingressi debbano essere 1 (o alta tensione) affinché 1 venga restituito.

1 & 0 = 0.

0 & 1 = 0.

0 e 0 = 0.

1 & 1 = 1.

Se rappresenti un numero come binario (qui ho usato una rappresentazione a 8 bit), i numeri dispari hanno 1 alla fine, i numeri pari hanno 0.

Per esempio:

1 = 00000001

2 = 00000010

3 = 00000011

4 = 00000100

Se prendi un numero qualsiasi e usi AND bit a bit (e in java) per 1, restituirà 00000001, = 1, il che significa che il numero è dispari. O 00000000 = 0, il che significa che il numero è pari.

Per esempio

È dispari?

1 & 1 =

00000001 &

00000001 =

00000001 <- Dispari

2 & 1 =

00000010 &

00000001 =

00000000 <- Pari

54 & 1 =

00000001 &

00110110 =

00000000 <- Pari

Ecco perché funziona:

if(number & 1){

   //Number is odd

} else {

   //Number is even
}

Scusate se questo è ridondante.


1

Parità Numero Zero | zero http://tinyurl.com/oexhr3k

Sequenza di codici 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))

Produzione:

   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, grazie. Se Zero è pari, quante coppie ha?

@ el.pescado, Ok, sono d'accordo con te. Quindi, se pensi un po ', perché dividiamo in 2 (due)? Cosa vogliamo sapere quando ci dividiamo in due? Perché non dividere in 3, o, 5, ecc.?

@ el.pescado Questo articolo di Wikipedia Parity of Zero è sbagliato. Molte persone sono state ingannate da questo articolo. Pensa prima di ammiccare.

1
Hai ragione. Ora che ho letto altre risposte, ho trovato le tue più complete :)
el.pescado,

@ el.pescado. Grazie. :) Ora sei il migliore amico di Zero. (abbraccio)

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

Per motivi di discussione ...

Hai solo bisogno di guardare l'ultima cifra in un dato numero per vedere se è pari o dispari. Firmati, non firmati, positivi, negativi - sono tutti uguali per quanto riguarda questo. Quindi questo dovrebbe funzionare a tutto tondo: -

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 chiave qui è nella terza riga di codice, l'operatore di divisione esegue una divisione intera, in modo che al risultato manchi la parte della frazione del risultato. Quindi, ad esempio, 222/10 darà 22 di conseguenza. Quindi moltiplicalo di nuovo per 10 e hai 220. Sottrai quello dal 222 originale e finisci con 2, che per magia è lo stesso numero dell'ultima cifra del numero originale. ;-) Le parentesi sono lì per ricordarci l'ordine in cui viene eseguito il calcolo. Prima fai la divisione e la moltiplicazione, quindi sottrai il risultato dal numero originale. Potremmo lasciarli fuori, poiché la priorità è più alta per la divisione e la moltiplicazione che per la sottrazione, ma questo ci dà un codice "più leggibile".

Potremmo renderlo completamente illeggibile se volessimo. Non farebbe alcuna differenza per un compilatore moderno: -

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

Ma renderebbe il codice molto più difficile da mantenere in futuro. Immagina di voler cambiare il testo per i numeri dispari in "non è pari". Quindi qualcun altro in seguito vorrà scoprire quali modifiche sono state apportate ed eseguire un svn diff o simile ...

Se non sei preoccupato per la portabilità ma più per la velocità, potresti dare un'occhiata al bit meno significativo. Se quel bit è impostato su 1 è un numero dispari, se è 0 è un numero pari. Su un piccolo sistema endian, come l'architettura x86 di Intel, sarebbe qualcosa del genere: -

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

Cosa c'è che non va esattamente con iToTest% 2 == 0? Stai sprecando una divisione per estrarre l'ultima cifra, quindi la tua è due volte più lenta di quanto deve essere.
freespace,

@freespace: spreco di più, no? :-) Una moltiplicazione e anche una sottrazione. Ma ciò che è più efficiente tra le due soluzioni non oso dire. Non ho mai affermato che questa sia la soluzione più veloce, al contrario se rileggi di nuovo la prima riga del mio post.
Tooony,

@Tooony, ah, il mio cappello umoristico è caduto. È tornato formalmente ora: D Mi dispiace :)
freespace

0

Se vuoi essere efficiente, usa operatori bit per bit ( x & 1), ma se vuoi essere leggibile usa modulo 2 ( x % 2)


-1: se si desidera essere efficienti, utilizzare uno dei due. Se vuoi che sia portatile, usa %. Se vuoi che sia leggibile, usa %. Hmmm, vedo uno schema qui.
Thomas Eding,

@trinithis, non esiste uno schema e questa soluzione è molto meglio della tua.
Sottoscrivi

0

Il controllo di pari o dispari è un compito semplice.

Sappiamo che qualsiasi numero esattamente divisibile per 2 è pari al numero dispari.

Dobbiamo solo verificare la divisibilità di qualsiasi numero e per verificare la divisibilità che usiamo % operatore

Controllo dispari usando se altrimenti

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

Programma C per controllare se pari o dispari usando altrimenti

Utilizzo dell'operatore Condizionale / Ternario

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

Programma C per controllare pari o dispari usando l'operatore condizionale .

Utilizzo dell'operatore Bitwise

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

e dov'è esattamente l'operatore ternario?
Beyondo,

0

+ 66% più veloce>!(i%2) / i%2 == 0

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

Il codice controlla l'ultimo bit dell'intero se è 1 in binario

Spiegazione

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

Si noti che il bit più a destra è sempre 1 per i numeri dispari .

i & bit per bit AND operatore controlla il bit più a destra nel nostro ritorno linea se si tratta di 1

Pensalo come vero e falso

Quando confrontiamo n con 1 che significa 0001in binario (il numero di zeri non ha importanza).
allora immaginiamo di avere l'intero n con una dimensione di 1 byte.

Sarebbe rappresentato da cifre a 8 bit / 8 binarie.

Se int n era 7 e lo confrontiamo con 1 , è come

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

Quale F sta per falso e T per vero.

Esso mette a confronto solo il bit più a destra se sono entrambe vere. Quindi, automagicamente 7 & 1è T rue.

E se volessi controllare il bit prima dell'estrema destra?

Passa semplicemente n & 1a n & 22 che rappresenta0010 in binario e così via.

Ti suggerisco di usare la notazione esadecimale se sei un principiante alle operazioni bit per bit
return n & 1;>> return n & 0x01;.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.