Як перевірити, чи є ціле число парним чи непарним? [зачинено]


193

Як я можу перевірити, чи задане число парне чи непарне в С?


5
Версія, що використовує побітове та (&), значно ефективніше, ніж версія модуля (%). Ви повинні змінити той, який ви вибрали як правильну відповідь.
Стефан Русек

6
Навряд чи має значення - аргумент є постійною. Легкий для оптимізатора
MSalters

2
Чинники читабельності також в цьому.
Брайан Г

2
У вбудованих додатках (у світі, де я провожу більшу частину свого програмування), деякі процесори мають дуже примітивні арифметичні одиниці і не можуть легко робити ділення / модульні операції легко. З цієї причини, як правило, я замість цього використовую побітовий метод. Однак на сучасному настільному процесорі це не так.
бта

3
Я ніколи не знаходив операцію з модулем легшою для розуміння. Коли мені вперше потрібно було визначити парне чи непарне, перше, що прийшло в голову, була розрядна маска. Це дещо природно, оскільки спосіб, яким ми це робимо вручну, - це подивитися на найменш значущу цифру, щоб побачити, чи є це в {0 2 4 6 8} або {1 3 5 7 9}. Це безпосередньо означає перегляд найменш значущого шматочка, щоб побачити, чи є це 0 або 1.
P Daddy

Відповіді:


449

Використовуйте оператор modulo (%), щоб перевірити, чи є залишок під час ділення на 2:

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

Кілька людей критикували мою відповідь вище, заявивши, що використання x & 1 "швидше" або "ефективніше". Я не вірю, що це так.

З цікавості я створив дві тривіальні програми тестів:

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

Потім я компілював їх з gcc 4.1.3 на одній із моїх машин 5 різних разів:

  • Без оптимізаційних прапорів.
  • З -О
  • З -О
  • З -O2
  • З -О3

Я вивчив вихідний збір кожного компілятора (використовуючи gcc -S) і виявив, що в кожному випадку вихід для and.c і modulo.c був однаковим (вони обидва використовували інструкцію andl $ 1,% eax). Я сумніваюся, що це "нова" особливість, і я підозрюю, що вона датується давніми версіями. Я також сумніваюсь, що будь-якому сучасному (зробленому за останні 20 років) компіляторові, що не є прихованим, комерційним чи відкритим кодом, такої оптимізації не вистачає. Я б протестував на інших компіляторах, але на даний момент у мене немає доступних.

Якщо хтось би потурбувався протестувати інші компілятори та / або цілі платформи, і отримав інший результат, мені було б дуже цікаво знати.

Нарешті, версія модуля гарантується стандартом для роботи, чи є ціле число позитивним, від'ємним або нульовим, незалежно від подання реалізацією підписаних цілих чисел. Побітова версія і ні. Так, я розумію, що доповнення двох є дещо повсюдним, тому це насправді не проблема.


11
Питання спеціально запитало, як це зробити на C, тому я відповів на C, незважаючи на те, що згадується, що вони не можуть розробити, як це зробити на Java. Я не стверджував або маю на увазі, що це відповідь Java, я не знаю Java. Я думаю, що щойно я отримав свій перший звук і я розгублений, чому. Що ж, добре.
Кріс Янг

33
Я б сказав, якщо (x% 2! = 0) {/ * x непарно * /}, але хто знає. Ява теж не знаю.
eugensk

9
Потрібно отримати чимало грошей, щоб відрізнити це від дебілів побитої операції, не витрачаючи нашу карму на їх голосування.
wnoise

13
Я погоджуюся з усім, крім одного: я люблю тримати цілі числа та значення істини окремо, концептуально, тому я вважаю за краще писати "якщо (x% 2 == 1)". Це те ж саме до компілятора, але, можливо, трохи зрозуміліше для людей. Крім того, ви можете використовувати той самий код на мовах, які не інтерпретують ненульовий як істинний.
Томас Падрон-Маккарті

46
Мій орієнтир? Який орієнтир? Я не робив жодного бенчмаркингу. Я вивчив створену мову складання. Це абсолютно не пов'язане з printf.
Кріс Янг

207

Ви, хлопці, занадто ефективні. Те, що ти насправді хочеш:

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

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

  return odd;
}

Повторіть для isEven.

Звичайно, це не працює для негативних чисел. Але з блиском приходить жертва ...


17
Якщо ви кинули виняток аргументу щодо негативних значень і зазначили в документації, що ця функція є O (N), то я б просто з цим добре.
Джеффрі Л Уітлідж

7
Корпоративна версія повинна буде використовувати XML. Звичайно, зараз у вас є веб-сервіс, про який ви можете запитати
Мартін Беккет,

58
Ви повинні оптимізувати це за допомогою таблиці перегляду.
Weeble

1
Я такий чернець, мав поставити +1 своєму 999 реп в нове тисячоліття
Еран Медан

7
Це геніально! Мій бос сказав мені, що у нас був клієнт, який був розлючений, оскільки відчував, що його ліцензія на підприємство не дає нічого більше, ніж стандартна ліцензія. Тепер ми додали цю функцію в нашу програму, і тільки тому, що вона виконується повільніше, він думає, що його програмне забезпечення робить ЗАВДАННЯ більше роботи !!!
Філ

97

Використовуйте бітну арифметику:

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

Це швидше, ніж використання поділу чи модуля.


43
Я не думаю, що справедливо говорити, що це швидше, ніж використання поділу або модуля. Стандарт C нічого не говорить про продуктивність операторів, і будь-який гідний компілятор створить швидкий код для будь-якого. Я особисто вибрав би ідіому, яка повідомляє про мої наміри, і% здається тут більш доречним
Кріс Янг

21
Мені подобається (x & 1) краще, тому що він перевіряє, чи число є таким самим, як і люди: перевірити, чи остання цифра є парною чи непарною. На мою думку, він передає свої наміри більше, ніж метод модуля. (Не те, що це має велике значення.)
Джеремі Рутен

2
Ви маєте рацію, я думаю, це суб'єктивно. Хоча звичайним визначенням "навіть" є "ціле число, яке ділиться на 2", а не "ціле число, яке закінчується на 0, 2, 4, 6 або 8". :-)
Кріс Янг

4
@TraumaPony - для стандарту ANSI C та ранньої Java, залежить від комп'ютерної системи. Не визначено, яке представлення використовується для підписаних номерів - комплімент 2, комплімент 1, кодований сірим кольором тощо. Але модуль завжди є модулем
Аарон

9
Не працює універсально для від'ємних чисел. Детальніше див. У розділі Перевірити цю відповідь: stackoverflow.com/questions/160930/… .
Ендрю Еджкомб

36

[Жарт режим = "увімкнено]]

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

[Жарт режим = "вимкнено"]

EDIT: До перерахунків додано заплутані значення.


2
Нічого собі ... це скоріше недоліки, ніж рішення SCdF! Кудо! Немає жодної пропозиції ... Не можу цього рекомендувати. Але дякую за смішне!
Уес П

1
Перевага такого підходу полягає в тому, що він працює з більш ніж просто числами. Також, якщо ви заміните цей рядок: char bar = foo [foo.Length - 1]; з цим: подвійний бар = Char.GetNumericValue (foo [foo.Length - 1]); Тоді він буде працювати з будь-якою системою числення.
Джеффрі Л Уітлідж

5
звіт про помилку: 14,65 повідомляється як непарне, коли має бути невідомим.
TheSoftwareJedi

4
Програмне забезпечення джедаїв, це "особливість". ;)
Скліввз

31
TheSoftwareJedi: 14.65 - одне з дивних цілих чисел, які я коли-небудь бачив.
Брюс Олдермен

16

У відповідь на ffpf - у мене був такий самий аргумент з колегою років тому, і відповідь - ні , це не працює з негативними цифрами.

Стандарт С передбачає, що від'ємні числа можна представити трьома способами:

  • 2 доповнення
  • Доповнення 1
  • знак і величина

Перевірка так:

isEven = (x & 1);

буде працювати для доповнення 2 та знаків та величин, але не для доповнення 1.

Однак я вважаю, що для всіх випадків буде працювати наступне:

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

Дякую ffpf за те, що він вказав, що текстове поле їсть усе, що було мені менше ніж персонаж!


Я думаю, що у вашому другому прикладі коду відсутній текст.
Джефф Йейтс

3
Давайте компліментуємо ці цифри!
thehh

14

Приємним є:

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

Зауважте, що цей метод використовує хвостову рекурсію, що включає дві функції. Це може бути ефективно реалізовано (перетворюється на деякий час / до виду циклу), якщо ваш компілятор підтримує хвостову рекурсію, як компілятор схеми. У цьому випадку стек не повинен переповнюватися!


1
Це не справляється ізOdd (0) добре.
Стів МакЛойд

1
Я думаю, у вас є нескінченний цикл (з хвостовою рекурсією) або переповнення стека (без хвостової рекурсії) для isOdd () з будь-якими парними значеннями або isEven () з будь-якими непарними значеннями. Він закінчується лише істинним. Це проблема зупинки знову.
Джеффрі Л Уітлідж

7
О, звичайно, виправте це без коментарів, і зробіть мене схожим на ідіота. Добре.
Джеффрі Л Уітлідж

1
Тепер у вас є помилка компіляції: в isEven не всі кодові шляхи повертають значення. Ні, я насправді не пробував цей код, нарікає компілятор в голові.
Джеффрі Л Уітлідж

5
помилка компіляції: не всі шляхи повертають ненависть значення, щоб обстрілювати вас коментарями про помилки щодо вашого зразкового коду, але що відбувається, коли ви телефонуєте isEven (5)
Кевін

11

Число є навіть, якщо, поділене на два, решта дорівнює 0. Число непарне, якщо при діленні на 2 решта дорівнює 1.

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

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

Методи чудові!


Ваш метод Java порушений, оскільки число% 2 == -1 для негативних непарних чисел.
WMR

Це ти чому мені прихильнився?
jjnguy

3
Я прихильнив це, оскільки ваша функція на C набирає більше символів, ніж те, що вона робить. IE num% I - 7 символів, включаючи пробіли IsOdd (I) - 8 символів. Чому б ви створили функцію, яка довша, ніж просто виконувати операцію?
Кевін

13
@Kevin, на мій погляд, код не вимірюється знаками, а часом, необхідним для його написання, включаючи час думки + налагодження. num% 2 потребує мілісекунд більше, ніж isOdd. тепер додайте цифри в усьому світі, і ви втратили колективний рік. Також isOdd може бути протестований і перевірений і врешті-решт не сертифікований помилками (наприклад, обробка негативних цифр), де як число% 2 - деякі розробники завжди матимуть сумніви і будуть експериментувати. хороший код - це код, який ви не пишете, просто повторно використовуйте ... лише мої 2 копійки.
Еран Медан

2
@EranMedan, та ж логіка застосовуватиметься до заміни i ++ на IncrementByOne (i), і це так само погана ідея. Якщо розробник сумнівається в тому, що робить число% 2, я не хочу, щоб він чи її не було біля мого коду.
Кевін


7

Я б сказав, що просто поділіть його на 2, і якщо є 0 залишок, це парне, інакше це дивно.

Використання модуля (%) робить це просто.

напр. 4% 2 = 0, тому 4 парно 5% 2 = 1, отже, 5 непарно


6

Ще одне рішення проблеми
(діти можуть голосувати)

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

  }
  return half1 == half2;
}

Ні, ти не така дитина, на яку я розраховував :)
eugensk

Я збирався підтримати це, але це трохи повільно на негативних цифрах. :)
Кріс Янг

3
Усі числа яскраві та позитивні. Або ти заборонений до деяких? :))
eugensk

3
У комп’ютерах всі числа, колись негативні, з часом стають позитивними. Ми називаємо це Перекиданням щастя (не застосовується до BIGNUMS, YMMY, не діє у всіх штатах).
Буде Хартюнг

@WillHartung "перекидання щастя" - це чудово! : D
thejh

6

Я створив би таблицю паритетів (0, якщо навіть 1, якщо непарне) цілих чисел (так що можна було б зробити пошук: D), але gcc не дозволить мені робити масиви таких розмірів:

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

Тож давайте замість цього скористаємось математичним визначенням парних та непарних.

Ціле число n є навіть якщо існує ціле число k таке, що n = 2k.

Ціле число n є непарним, якщо існує ціле число k таке, що n = 2k + 1.

Ось код для цього:

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

Нехай С-цілі числа позначають можливі значення intв даній компіляції С. (Зверніть увагу, що С-цілі числа - це підмножина цілих чисел.)

Тепер можна потурбуватися, що для заданого n у C-цілих числах відповідне ціле число k може не існувати в межах C-цілих чисел. Але з невеликим доказом можна показати, що для всіх цілих чисел n, | n | <= | 2n | (*), де | n | є "n, якщо n є позитивним, а -n в іншому випадку". Іншими словами, для всіх n у цілих числах принаймні одне із наведених нижче справді (саме або випадків (1 і 2), або випадків (3 і 4) насправді, але я не докажу це тут):

Випадок 1: n <= 2n.

Випадок 2: -n <= -2n.

Випадок 3: -n <= 2n.

Випадок 4: n <= -2n.

Тепер візьміть 2k = n. (Такий ak існує, якщо n є рівним, але я цього не докажу. Якщо n навіть немає, петля все-таки evenне повертається рано, тому це не має значення.) Але це означає k <n, якщо n не 0 по (*), а факт (знову тут не доведено), що для всіх m, z у цілих числах 2m = z випливає, що z не дорівнює m, m не дорівнює 0. У випадку n дорівнює 0, 2 * 0 = 0 значить, 0 навіть ми робимо (якщо n = 0, тоді 0 знаходиться в C-цілих числах, тому що n знаходиться в C-цілому цілі функції even, отже, k = 0 знаходиться в C-цілих числах). Таким чином, такі ak в C-цілих числах існують для n в C-цілих числах, якщо n є парним.

Подібний аргумент показує, що якщо n непарне, існує C в цілих цілих числах таких, що n = 2k + 1.

Отже, функція evenі oddпредставлена тут буде працювати для всіх C-цілих чисел.


1
Я не маю на увазі образи, але який сенс у цій відповіді? i % 2набагато менший і, мабуть, більш ефективний.
GManNickG

2
@GMan: Але це спосіб детермінованіший! Це допоможе правильно виявити всі крайові випадки.
P тато

1
... І (!!!) це правильно !!!
Томас Едінг

Не можу сказати, жартуєте ви чи ні. : X %2працює для всіх цілих чисел.
GManNickG

1
+1: Я хотів би сказати "Гарна відповідь", але вважаю, що "Цікава відповідь" є більш доречною.
Джеймс Вебстер

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

2
Що? Це не C #! Це чисто C! :-P
зірочка

8
Я кину навколо нього WinForm, щоб зробити його чистою C # ...
Майкл Петротта

@mateusza: Зазвичай , коли ви бачите «логічне значення» , в якій - то капіталізації або інший в C, це typedefабо #defineабо що - то.
Девід Торнлі

2
@mateusza @David Thornley In C99 bool - це стандартна функція ( en.wikipedia.org/wiki/Stdbool.h )
фортран

1
Поговоріть про надзвичайно надмірні дужки ...
Томас Едінг

4

Ось відповідь на 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

Спробуйте це: return (((a>>1)<<1) == a)

Приклад:

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

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

Чи можете ви пояснити це будь ласка? Мені дуже незнайомі оператори побітових операцій
Абдул,

Якщо зсунути праворуч та ліворуч, нульовий останній біт (найправіший). Якщо нове число збігається з оригіналом, це означає, що останній біт початкового числа був 0. Так це парне. Погляньте на мою оновлену відповідь.
Кирило Олександров

дякую, я отримую це зараз
Абдул

Я не впевнений, який підхід швидший. Я не намагався їх порівняти.
Кирило Олександров

Чи це також не скасовує ваш найзначніший біт? Проблема з
неподписаними ввідними

4

Читаючи цю досить розважальну дискусію, я згадав, що у мене була функція реального світу, залежна від часу, яка перевіряла непарні і парні числа всередині основного циклу. Це ціла функція живлення, розміщена в іншому місці на StackOverflow наступним чином. Орієнтири були досить дивовижними. Принаймні, в цій реальній функції модуль повільніший , і це істотно. Переможець з великим відривом, який вимагає 67% часу за модулем, є підходом або (|) , і його ніде не можна знайти ніде на цій сторінці.

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

Для 300 мільйонів циклів терміни орієнтиру такі.

3.962 | і підхід маски

4.851 & підхід

5.850% підходу

Для людей, які думають, що теорія чи перелік мов асемблера встановлює подібні аргументи, це має бути застережливою історією. На небі та на землі, Гораціо, є більше речей, ніж про те, про що мріяли у твоїй філософії.


1
Краще використовувати, unsigned xяк x = x >> 1;це визначена реалізацією поведінка, коли x < 0. Незрозуміло, чому xі OrMaskвідрізняються між собою. Досить простий для переписування за допомогою while(x)тесту.
chux

2
Цікаво, який компілятор ви використовували для того, щоб це орієнтувати, оскільки більшість компіляторів повинні бути досить розумними, щоб скласти % 2справу за допомогою побітових розрядів &. Я щойно перевірив це, і результати абсолютно однакові (VS2015, випуск версій із усіма оптимізаціями, як x86, так і x64). У прийнятій відповіді також зазначено це для GCC (написано у 2008 році).
Лу

2
Проблема з цією публікацією полягає в тому, що припущення про те, що побітна orшвидкість буде швидше, ніж andвкрай малоймовірна, на будь-якій платформі / компіляторі. Навіть якби була така дивна комбінація платформи / компілятора (і ви не розмістили ні того, ні коду, який використовується для виконання еталону), залежно від того, щоб інші компілятори поводилися так само, було б поганою ставкою на оптимізацію. Отже, як я писав, мені цікаво, на якій платформі / компіляторі це було протестовано , оскільки я майже впевнений, що він був виміряний неправильно.
Лу

2
Не називаючи вас брехуном, просто стверджуючи, що ви неправильно виміряли. Поки не потрібно називати мене водієм вантажівки, прочитайте мій оригінальний коментар: я зробив орієнтир, і результати, як очікувалося, були абсолютно однаковими у всіх трьох випадках (впевненість ~ 3 сигми, після кожного тестування 10 разів по 500 000 .000 ітерацій). Якщо у вас справді тривала знаменита кар'єра, зробіть крок назад і подумайте, чи ваші претензії мають сенс, а потім опублікуйте фактичний код, який використовується для тестування. Інакше посада - це те, що я вважаю, що це просто помилка в вимірюванні.
Лу


4

Це продовження дискусії з @RocketRoy щодо його відповіді , але це може бути корисним для всіх, хто хоче порівняти ці результати.

tl; dr З того, що я бачив, підхід Роя ( (0xFFFFFFFF == (x | 0xFFFFFFFE)) не повністю оптимізований x & 1як modпідхід, але на практиці час роботи повинен виявитися рівним у всіх випадках.

Отже, спочатку я порівняв компільований вихід за допомогою Провідника компілятора :

Функції перевірені:

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

Капелюхи до CLang, він зрозумів, що всі три випадки функціонально рівні. Однак підхід Роя не оптимізований в GCC, тому YMMV.

Подібно до Visual Studio; оглядаючи демонтаж випуску x64 (VS2015) для цих трьох функцій, я міг побачити, що частина порівняння дорівнює для "mod" і "і" випадків, і трохи більша для випадку 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)

Однак після запуску фактичного еталону для порівняння цих трьох варіантів (звичайний мод, побітовий або побітовий і) результати були повністю рівними (знову ж таки, Visual Studio 2005 x86 / x64, випуск версії, відладчик не додається).

Збірка випуску використовує testінструкцію для andта modвипадків, тоді як у випадку Роя використовується cmp eax,0FFFFFFFFhпідхід, але він сильно розкручений та оптимізований, тому різниці у практиці немає.

Мої результати після 20 запусків (i7 3610QM, план живлення Windows 10 встановлено на високу продуктивність):

[Тест: Звичайний мод 2] СРІДНИЙ ЧАС: 689,29 мс (Відносна різниця: + 0,000%)
[Тест: Побітовий або] СРІДНИЙ ЧАС: 689,63 мс (Відносна різниця: + 0,048%)
[Тест: Побітові та] СРІДНИЙ ЧАС: 687,80 мс (Відносна різниця: -0,217%)

Різниця між цими варіантами становить менше 0,3%, тому досить очевидно, що збірка рівнозначна у всіх випадках.

Ось код, якщо хтось хоче спробувати, із застереженням, що я тестував його лише в Windows (перевірте #if LINUXумовне get_timeвизначення та застосуйте його за потреби, взятий з цієї відповіді ).

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

Я вважаю, що ви скоїли кардинального гріха бенчмаркінгу; створивши такий настільки специфічний, що він не представляє реального середовища. Подивіться на мову монтажу та помітьте, як мало регістрів ви використовуєте. Високі оцінки за зусилля, але ці результати не витримають у реальній обробці.

@RocketRoy: оскільки всі результати абсолютно однакові для всіх трьох випадків (ну, трохи гірше для вашої програми в одному випадку), мені не дуже важливо, скільки реєстрів було використано. Але знову ж таки, сміливо створюйте та розміщуйте таку прикладну програму / середовище, яка збиває з пантелику компілятор для створення більш оптимізованої збірки в одному з випадків, при цьому всі інші речі рівні.
Лу

Мені завжди подобаються зухвалі програмісти. Для програміста це хороша риса, але в більш складній реальній програмі мій метод буде краще, ніж ваш, тому що у компілятора є більше способів вирішити проблему, щоб інструкції збігалися (щодо архітектури Intel), що дало кращі результати . Дуже мало ветеранів-програмістів з хорошим досвідом бенчмаркінгу віддають перевагу вашому орієнтиру, але продовжуйте працювати, і не забудьте повторити свої показники, коли вийдуть нові випуски чіпів. Все з часом змінюється.

3

Я знаю, що це лише синтаксичний цукор і застосовується лише в .net, але що стосується методу розширення ...

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

Тепер ви можете зробити наступне

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

1
Хороший код. Шкода, що вона буде стверджувати, що 2 - це непарно, а 3 - ні.
Ентоні

ой, вибачте ... моя логіка неправильна навпаки ...
rudigrobler

3

У "креативній, але заплутаній категорії" я пропоную:

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

Варіант цієї теми, характерний для 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

Розрядний метод залежить від внутрішнього подання цілого числа. Модуло працюватиме в будь-якому місці, де є модуль оператора. Наприклад, деякі системи насправді використовують біти низького рівня для позначення тегів (наприклад, динамічні мови), тому сировина x & 1 насправді не працюватиме в такому випадку.


2

IsOdd (int x) {return true; }

Доказ правильності - розглянемо набір усіх натуральних чисел та припустимо, що існує не порожній набір цілих чисел, які не є непарними. Оскільки позитивні цілі числа добре впорядковані, вийде найменше не непарне число, яке саме по собі є досить непарним, тому однозначно це число не може бути в наборі. Тому цей набір не може бути порожнім. Повторіть для від'ємних цілих чисел, за винятком найбільшого нечетного числа.


2

Портативний:

i % 2 ? odd : even;

Неможливо:

i & 1 ? odd : even;

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

2

Як опублікували деякі люди, існує чимало способів зробити це. За даними цього веб-сайту , найшвидший спосіб - це оператор модуля:

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

Однак ось якийсь інший код, який автором було позначено автором, який працював повільніше, ніж загальна операція з модулем вище:

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

Скільки людей навіть знали про метод Math.System.DivRem або навіщо його використовувати ??



1

Щоб детальніше розглянути метод побітових операторів для тих із нас, хто не робив великої булевої алгебри під час наших досліджень, ось пояснення. Напевно, не дуже корисно для ОП, але я відчув, як зрозуміти, чому НОМЕР і 1 працює.

Зауважте, як, як хтось відповів вище, спосіб представлення негативних чисел може зупинити цей метод роботи. Насправді він навіть може порушити метод оператора модуля, оскільки кожна мова може відрізнятися тим, як він має справу з негативними операндами.

Однак якщо ви знаєте, що НОМЕР завжди буде позитивним, це добре працює.

Оскільки Тоуні вище зазначав, що важлива лише остання цифра у двійковій (і денарійній) формі.

Булева логіка І ворота диктує, що обидва входи повинні бути рівними 1 (або високою напругою), щоб повернути 1.

1 & 0 = 0.

0 & 1 = 0.

0 & 0 = 0.

1 & 1 = 1.

Якщо ви представляєте будь-яке число як двійкове (я тут використав 8-бітове представлення), непарні числа мають 1 в кінці, парні числа мають 0.

Наприклад:

1 = 00000001

2 = 00000010

3 = 00000011

4 = 00000100

Якщо ви візьмете будь-яке число і скористаєтеся побіжно AND (і в java), воно на 1 воно поверне 00000001, = 1 означає, що число непарне. Або 00000000 = 0, тобто число парне.

Напр

Незвичайно?

1 & 1 =

00000001 &

00000001 =

00000001 <- непарне

2 & 1 =

00000010 &

00000001 =

00000000 <- Навіть

54 & 1 =

00000001 &

00110110 =

00000000 <- Навіть

Ось чому це працює:

if(number & 1){

   //Number is odd

} else {

   //Number is even
}

Вибачте, якщо це зайве.


1

Число нульовий паритет | нуль http://tinyurl.com/oexhr3k

Послідовність коду 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))

Вихід:

   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, Дякую Якщо нуль рівний, скільки пар має?

@ el.pescado, Добре, я з вами згоден. Тоді, Якщо подумайте трохи, чому ми ділимось на 2 (два)? Що ми хочемо знати, коли ділимось на два? Чому б не поділити на 3, або, 5 тощо?

@ el.pescado Ця стаття у Вікіпедії Паритет нуля помилкова. Дуже багато людей обдурили цю статтю. Подумайте перед тим, як підморгнути.

1
Ти маєш рацію. Тепер, коли я прочитав інші відповіді, я знайшов твою найбільш вичерпною :)
el.pescado

@ el.pescado. Спасибі. :) Тепер ти найкращий друг Зеро. (обійняти)

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

Заради обговорення ...

Вам потрібно лише подивитися останню цифру в будь-якому даному номері, щоб побачити, чи є парним чи непарним. Підписані, непідписані, позитивні, негативні - вони щодо цього однакові. Отже, це має працювати в усьому світі:

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

Ключ тут - у третьому рядку коду, оператор ділення виконує ціле ділення, так що в результаті не вистачає частки результату. Так, наприклад, 222/10 дасть 22 в результаті. Потім помножте його ще раз на 10 і у вас є 220. Відніміть це від початкового 222, і ви закінчите з 2, що за магією - це те саме число, що і остання цифра в початковому числі. ;-) Дужки існують там, щоб нагадати нам про порядок, у якому проводиться обчислення. Спочатку виконуємо ділення та множення, а потім віднімаємо результат від початкового числа. Ми могли б їх залишити, оскільки пріоритет для ділення та множення більший, ніж для віднімання, але це дає нам "більш читабельний" код.

Ми могли б зробити це абсолютно нечитабельним, якби цього хотіли. Для сучасного компілятора це не мало би значення: -

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

Але це полегшить підтримку коду в майбутньому. Уявіть собі, що ви хотіли б змінити текст для непарних чисел на "не парне". Потім хтось ще пізніше захоче з’ясувати, які зміни ви внесли та виконати svn diff чи подібні ...

Якщо вас не турбує портативність, але більше про швидкість, ви можете ознайомитись з принаймні значущим бітом. Якщо для цього біта встановлено 1, це непарне число, якщо це 0 - це парне число. У такій маленькій системі, як архітектура Intel x86, це було б щось подібне:

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

Що саме не так, просто перейшовши на iToTest% 2 == 0? Ви витрачаєте поділ, дістаючи останню цифру, тож ваш удвічі повільніше, ніж це потрібно.
простір

@freespace: Я витрачаю більше на це, чи не так? :-) Множення і віднімання теж. Але що найефективніше між двома рішеннями, я не наважуюся сказати. Ніколи не стверджував, що це найшвидше рішення, зовсім навпаки, якщо ви знову прочитаєте перший рядок моєї публікації.
Tooony

@Tooony, ах, моя гуморна шапка відвалилася. Це формально повернулося зараз: D Вибачте за це :)
простір

0

Якщо ви хочете бути ефективними, використовуйте побітові оператори ( x & 1), але якщо ви хочете читати, використовуйте modulo 2 ( x % 2)


-1: Якщо ви хочете бути ефективними, використовуйте будь-який. Якщо ви хочете, щоб він був портативним, використовуйте %. Якщо ви хочете, щоб воно було читабельним, використовуйте %. Хммм, я бачу тут візерунок.
Томас Едінг

@trinithis, немає шаблону, і це рішення набагато краще, ніж ваше.
Абонемент

0

Перевірка парного чи непарного - це просте завдання.

Ми знаємо, що будь-яке число, яке точно ділиться на 2, є парним числом.

Нам просто потрібно перевірити подільність будь-якої кількості і для перевірки подільності ми використовуємо %оператор

Перевірка парного використання, якщо інше

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

Програма C, щоб перевірити парне або непарне використання, якщо інше

Використання умовного / термінального оператора

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

Програма C для перевірки парного чи непарного використання за допомогою умовного оператора .

Використання оператора "Бітовий"

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

і де саме знаходиться потрійний оператор?
Бейондо,

0

+ 66% швидше>!(i%2) / i%2 == 0

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

Код перевіряє останній біт цілого числа, якщо він 1 у Бінарному

Пояснення

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

Зауважте, що кращий правий біт завжди 1 для непарних чисел.

в & побітовое І оператор перевіряє правий біт в нашій повернутої рядку , якщо це 1

Подумайте про це як про справжнє та хибне

Коли ми порівнюємо n з 1, що означає 0001двійкові (кількість нулів не має значення).
то давайте просто уявімо, що у нас є ціле число n розміром 1 байт.

Він буде представлений 8-бітовими / 8-бінарними цифрами.

Якщо int n було 7, і ми порівнюємо його з 1 , це як

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

Який F означає false, а T - true.

Він порівнює лише самий крайній біт, якщо вони обоє істинні. Отже, автоматично 7 & 1це T rue.

Що робити, якщо я хочу перевірити шматочок перед самим правим?

Просто змініть n & 1на n & 22, яке представляє0010 в довічним і так далі.

Я пропоную використовувати шістнадцяткову нотацію, якщо ви новачок у бітових операціях
return n & 1;>> return n & 0x01;.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.