Как программно выбрать високосный год в C

Я сделал программу, используя C, чтобы узнать, является ли введенный год високосным годом или нет. Но, к сожалению, он плохо работает. В нем говорится, что год является прыжком, а предыдущий год - не прыжком.

#include<stdio.h>
#include<conio.h>
int yearr(int year);
void main(void)
{
    int year;
    printf("Enter a year:");
    scanf("%d",&year);
    if(!yearr(year))
    {
        printf("It is a leap year.");
    }
    else
    {
    printf("It is not a leap year");
    }


getch();
}
int yearr(int year)
{
    if((year%4==0)&&(year/4!=0))
    return 1;
    else
    return 0;
}

После прочтения комментариев я отредактировал свою кодировку как:

#include<stdio.h>
#include<conio.h>
int yearr(int year);
void main(void)
{
    int year;
    printf("Enter a year:");
    scanf("%d",&year);
    if(!yearr(year))
    {
        printf("It is a leap year.");
    }
    else
    {
    printf("It is not a leap year");
    }


getch();
}
int yearr(int year)
{
    if((year%4==0)
    {
    if(year%400==0)
    return 1;
    if(year%100==0)
    return 0; 
    }
    else
    return 0;
}

Ответ 1

Ваша логика определения високосного года неверна. Это должно заставить вас начать (из Википедии):

if year modulo 400 is 0
       then is_leap_year
else if year modulo 100 is 0
       then not_leap_year
else if year modulo 4 is 0
       then is_leap_year
else
       not_leap_year

x modulo y означает остаток x, деленный на y. Например, 12 по модулю 5 равно 2.

Ответ 2

Самый эффективный тест на летальный год:

if ((year & 3) == 0 && ((year % 25) != 0 || (year & 15) == 0))
{
    /* leap year */
}

Этот код действителен в C, С++, С#, Java и многих других языках, подобных C. В коде используется одно выражение TRUE/FALSE, состоящее из трех отдельных тестов:

  • 4-й год: year & 3
  • 100-летний тест: year % 25
  • 400-летний тест: year & 15

Полное обсуждение того, как работает этот код, приводится ниже, но сначала обсуждение алгоритма Википедии вызывается для:

Алгоритм Википедии НЕВОЗМОЖНО/НЕПОСРЕДСТВЕННО

Википедия опубликовала псевдокодовый алгоритм (см.: Википедия: Високосный год - Алгоритм), который подвергался постоянному редактированию, и вандализм.

НЕ ВЫПОЛНЯЙТЕ АЛГОРИТМ WIKIPEDIA!

Один из самых длинных (и неэффективных) алгоритмов Википедии появился следующим образом:

if year modulo 400 is 0 then
   is_leap_year
else if year modulo 100 is 0 then
   not_leap_year
else if year modulo 4 is 0 then
   is_leap_year
else
   not_leap_year

Вышеуказанный алгоритм неэффективен, потому что он всегда выполняет тесты на 400-й год и 100-й год даже в течение многих лет, которые быстро не пройдут "тест 4-го года" (тест по модулю 4), что составляет 75% времени! Переупорядочив алгоритм для выполнения 4-годичного теста, мы значительно ускорим процесс.

"САМЫЙ ЭФФЕКТИВНЫЙ" АЛГОРИТМ ПСЕВДО-КОДА

Я предоставил Википедию следующий алгоритм (более одного раза):

if year is not divisible by 4 then not leap year
else if year is not divisible by 100 then leap year
else if year is divisible by 400 then leap year
else not leap year

Этот "самый эффективный" псевдокод просто изменяет порядок тестов, поэтому сначала происходит деление на 4, за которым следуют менее часто встречающиеся тесты. Поскольку "год" не делит на четыре 75 процентов времени, алгоритм заканчивается только после одного теста в трех из четырех случаев.

ПРИМЕЧАНИЕ: Я боролся с различными редакторами Википедии, чтобы улучшить опубликованный там алгоритм, утверждая, что многие новички и профессионалы и программисты быстро приходят на страницу Википедии (из-за лучших списков поисковых систем) и внедряют псевдокод Wikipedia без каких-либо дальнейших исследований. Редакторы Википедии отреклись от всех попыток, которые я сделал, чтобы улучшить, аннотировать или даже просто записать опубликованный алгоритм. По-видимому, они считают, что найти эффективность - проблема программиста. Это может быть правдой, но многие программисты слишком торопились, чтобы выполнить прочные исследования!

ОБСУЖДЕНИЕ ИСПЫТАНИЯ ГОДА "МОСТ-ЭФФЕКТИВНЫЙ"

Побитовое - и вместо modulo:

Я заменил две из операций по модулю в алгоритме Википедии с побитовыми-И-операциями. Почему и как?

Для вычисления по модулю требуется деление. Во время программирования ПК часто не так много думать об этом, но при программировании 8-разрядных микроконтроллеров, встроенных в небольшие устройства, вы можете обнаружить, что функция разделения не может быть изначально выполнена процессором. На таких процессорах разделение представляет собой сложный процесс, включающий повторение цикла, смещение битов и добавление/вычитание операций, которые очень медленные. Очень желательно избегать.

Оказывается, что по модулю степеней двух можно поочередно достичь с помощью операции поразрядного И (см. Википедия: Работа в модуле - Проблемы с производительностью):

x% 2 ^ n == x и (2 ^ n - 1)

Многие оптимизирующие компиляторы преобразуют такие операции modulo в bitwise-AND для вас, но менее продвинутые компиляторы для меньших и менее популярных процессоров не могут. Побитовое - И представляет собой одну инструкцию для каждого процессора.

Заменив теги modulo 4 и modulo 400 на & 3 и & 15 (см. ниже: "Факторинг для сокращения математики" ), мы можем гарантировать, что самый быстрый код будет работать без использования операции более медленного деления.

Существует не две силы, равные 100. Таким образом, мы вынуждены продолжать использовать модульную операцию для 100-летнего теста, однако 100 заменяется на 25 (см. ниже).

Факторинг для упрощения математики:

В дополнение к использованию побитового-И для замены операций modulo вы можете отметить два дополнительных спора между алгоритмом Википедии и оптимизированным выражением:

  • modulo 100 заменяется на modulo 25
  • modulo 400 заменяется на & 15

100-летний тест использует modulo 25 вместо modulo 100. Мы можем сделать это, потому что 100 факторов равны 2 x 2 x 5 x 5. Поскольку 4-летний тест уже проверяет факторы 4, мы можем исключить этот коэффициент из 100, оставив 25. Эта оптимизация, вероятно, незначительна почти для каждой реализации ЦП ( поскольку как 100, так и 25 подходят в 8 бит).

В 400-летнем тесте используется & 15, что эквивалентно modulo 16. Опять же, мы можем это сделать, потому что 400 факторов составляют 2 x 2 x 2 x 2 x 5 x 5. Мы можем устранить коэффициент 25, который проверяется на 100-летнем тесте, оставляя 16. Мы не можем далее уменьшить 16, потому что 8 в 200 раз, поэтому удаление каких-либо факторов приведет к нежелательному положительному результату на 200-й год.

Оптимизация на 400 лет очень важна для 8-разрядных ЦП, во-первых, потому что она позволяет избежать деления; но, что более важно, поскольку значение 400 представляет собой 9-разрядное число, с которым гораздо сложнее справиться в 8-разрядном процессоре.

Локальные логические операторы AND/OR:

Конечной и наиболее важной используемой оптимизацией являются операторы короткого замыкания AND ('& &') и OR ('||') (см. Википедия: оценка короткого замыкания), которые реализованы на большинстве C-подобных языков. Операторы короткого замыкания названы так потому, что они не хотят оценивать выражение с правой стороны, если выражение на левой стороне само по себе определяет результат операции.

Например: если год 2003, то year & 3 == 0 - false. Нет никакого способа, чтобы тесты с правой стороны логического И могли сделать результат истинным, поэтому ничего другого не оценили.

Выполняя сначала тест на 4-й год, только четвертый тест (простой побитовый-И) оценивается три четверти (75 процентов) времени. Это значительно ускоряет выполнение программы, тем более что она позволяет избежать деления, необходимого для теста 100-го года (операция по модулю 25).

ПРИМЕЧАНИЕ НА РАЗМЕЩЕНИЕ РОДИТЕЛЕЙ

Один комментатор считает, что круглые скобки были неуместны в моем коде и предложили, чтобы подвыражения были перегруппированы вокруг логического оператора И (а не вокруг логического ИЛИ), как показано ниже:

if (((year & 3) == 0 && (year % 25) != 0) || (year & 15) == 0) { /* LY */ }

Вышеуказанное неверно. Логический оператор И имеет более высокий приоритет, чем логический ИЛИ, и будет оцениваться сначала с новыми скобками или без них. Скобки вокруг логических аргументов AND не влияют. Это может привести к тому, что полностью исключить подгруппы:

if ((year & 3) == 0 && (year % 25) != 0 || (year & 15) == 0) { /* LY */ }

Но в случае обоих правильная сторона логического ИЛИ (тест 400-го года) оценивается почти каждый раз (т.е. годы не делятся на 4 и 100). Таким образом, полезная оптимизация была ошибочно устранена.

Скобки в моем исходном коде реализуют наиболее оптимизированное решение:

if ((year & 3) == 0 && ((year % 25) != 0 || (year & 15) == 0)) { /* LY */ }

Здесь логический ИЛИ оценивается только в течение лет, делящихся на 4 (из-за короткого замыкания И). Правая часть логического ИЛИ оценивается только в течение лет, делящихся на 4 и 100 (из-за короткого замыкания ИЛИ).

ПРИМЕЧАНИЕ ДЛЯ ПРОГРАММИРОВАНИЙ C/С++

Программисты C/С++ могут почувствовать, что это выражение более оптимизировано:

if (!(year & 3) && ((year % 25) || !(year & 15))) { /* LY */ }

Это не оптимизировано! Хотя явные тесты == 0 и != 0 удаляются, они становятся неявными и все еще выполняются. Хуже того, код больше не действует в строго типизированных языках, таких как С#, где year & 3 оценивается как int, но операторы логического AND (&&), OR (||) и NOT (!)) требуют аргументов bool.

Ответ 3

int isLeapYear(int year)
{
   return (year % 400 == 0) || ( ( year % 100 != 0) && (year % 4 == 0 ));
}

Ответ 4

Хотя логика, которая сначала делит на 400, безупречна, она не так эффективна в вычислительном отношении, как сначала деление на 4. Вы можете сделать это с помощью логики:

#define LEAPYEAR(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))

Это делит на 4 для каждого значения, но для 3/4 из них тестирование заканчивается там. Для 1/4, прошедшей первый тест, она делится на 100, исключая значения 24/25; для оставшихся 1 из 100 он тоже делится на 400 и дает окончательный ответ. Конечно, это не огромная экономия.

Ответ 5

Это может быть правильным решением. Алгоритм, приведенный в Википедии, неверен.

-(BOOL)isLeapYear: (int)year{    

    if(year%4==0){
      if(year%100!=0){
        return YES;
     }
     else if(year%400!=0){
        return YES;
     }
     else return NO;
   }

    else return NO;
  }

Ответ 7

Проблема с вашим кодом заключается в том, что вы возвращаете ненулевое значение из yearr, если считаете, что год является високосным годом. Поэтому вам не нужно ! в вашем операторе if.

Ответ 8

http://www.wwu.edu/depts/skywise/leapyear.html

Правила високосного года

Существует год високосного года, число отлично делится на четыре - за исключением лет, которые делятся на 100 и не делятся на 400. Вторая часть правила влияет на столетие. Например; века 1600 и 2000 гг. високосные годы, но столетие лет 1700, 1800 и 1900 нет. Эта означает, что три раза из каждого четыреста лет восемь лет между високосными годами.

Ответ 9

 if(year%400 ==0 || (year%100 != 0 && year%4 == 0))
    {
        printf("Year %d is a leap year",year);
    }
    else
    {
        printf("Year %d is not a leap year",year);
    }

Измените его, как указано выше. Также прочитайте this.

Ответ 10

<Предварительно >   #включают   void main (void)   {       int year;       printf ( "Введите год, чтобы проверить, является ли это" Високосный год "\n" );       зсапЕ ( "% d", & год);       if (year% 400 == 0)/* Почему mod 400 */           printf ( "% d - это год високосного года \n", год);       else if (year% 100 == 0)/* Почему mod 100 */           printf ( "% d не является високосным годом \n", год);       else if (year% 4 == 0)           printf ( "% d - это год високосного года \n", год);       еще           printf ( "% d не является високосным годом \n", год);   }

Ответ 11

I used this code:

#include <stdio.h>

int main()
{
    int yr;
    printf ("Enter a year \n");
    scanf ("%d", &yr);

    if (yr%400 == 0)
        printf("\n LEAP YEAR.");

    else if (yr%4==0 && yr%100!=0)
        printf("\n LEAP YEAR.");
    else
        printf ("\n NOT LEAP YEAR.");
}

Ответ 12

Как и другие, упомянутое условие для високосного года неверно. Он должен:

int yearr(int year)  
{  
    if(((year%4 == 0) && (year%100 !=0)) || (year%400==0))  
        return 1;    
    else    
        return 0;    
}  

Прочитайте здесь как проверить високосный год на C.

Ответ 13

Ответ Kevin обеспечивает оптимальный 8-й рабочий тест (с использованием XOR с использованием констант), но если вы ищете что-то более читаемое, попробуйте выполнить этот 9-й тестовый тест.

year % 4 == 0 && !((year % 100 == 0) ^ (year % 400 == 0))

Таблица истинности для (year % 100 == 0) ^ (year % 400 == 0)

                              (year % 100 == 0) ^ (year % 400 == 0)
100 doesnt divide year     .    F
only 100 divides year      .    T
100 and 400 divides year   .    F

Теперь !(year % 100 == 0) ^ (year % 400 == 0) дает то, что вы хотите.

Ответ 14

Рассчитать максимальный/последний день за месяц: 1..12, год: 1..3999

maxDays = month == 2 ?
  28 + ((year & 3) == 0 && ((year % 25) != 0 || (year & 15) == 0)) :
  30 + ((month & 1) ^ (month > 7));

Ответ 15

#define is_leap(A) !((A) & 3)

Просто убедитесь, что вы не указали отрицательный год:)