首页 文章

如何在C中以编程方式查找闰年

提问于
浏览
7

我使用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;
}

15 回答

  • 3

    你确定闰年的逻辑是错误的 . 这应该让你开始(来自维基百科):

    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 .

  • 4

    最有效的闰年测试:

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

    此代码在C,C,C#,Java和许多其他类C语言中有效 . 该代码使用单个TRUE / FALSE表达式,该表达式由三个单独的测试组成:

    • 第四年测试: year & 3

    • 第100年测试: year % 25

    • 第400年测试: year & 15

    有关此代码如何工作的完整讨论如下所示,但首先需要讨论维基百科的算法:

    维基百科算法不充分/不可靠

    维基百科发布了一种伪代码算法(参见:Wikipedia: Leap year - Algorithm),该算法经常受到编辑,舆论和故意破坏 .

    DO NOT IMPLEMENT WIKIPEDIA ALGORITHM!

    最长期(且效率低下)的维基百科算法之一如下:

    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年测试,我们首先加快了速度 .

    "MOST-EFFICIENT" PSEUDO-CODE ALGORITHM

    我向维基百科提供了以下算法(不止一次):

    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%的时间,所以算法在四个案例中的三个案例中仅进行一次测试后结束 .

    注意:我已经与各种维基百科编辑进行了斗争,以改进在那里发布的算法,认为许多新手和专业程序员很快到达维基百科页面(由于顶级搜索引擎列表)并实施维基百科伪代码而无需进一步研究 . 维基百科编辑拒绝并删除了我为改进,注释甚至仅仅注释已发布的算法所做的每一次尝试 . 显然,他们觉得找到效率是程序员的问题 . 这可能是真的,但许多程序员都急于进行扎实的研究!

    讨论“最有效”的年度试验

    Bitwise-AND in place of modulo:

    我已经使用按位AND运算替换了维基百科算法中的两个模运算 . 为什么以及如何?

    执行模数计算需要除法 . 在编程PC时,人们通常不会三思而后行,但是当编程嵌入在小型设备中的8位微控制器时,您可能会发现CPU无法原生执行除法功能 . 在这样的CPU上,除法是一个艰难的过程,涉及重复循环,位移和加/减操作,这些操作非常慢 . 非常希望避免 .

    事实证明,使用按位AND运算可以交替地实现两个幂的模数(参见:Wikipedia: Modulo operation - Performance Issues):

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

    许多优化编译器会将这些模运算转换为按位AND,但对于较小和较不流行的CPU,不太先进的编译器可能不会 . Bitwise-AND是每个CPU上的单个指令 .

    通过用 & 3& 15 替换 modulo 4modulo 400 测试(见下文:'Factoring to reduce math'),我们可以确保在不使用更慢的除法运算的情况下获得最快的代码 .

    没有两个等于100的幂 . 因此,我们被迫继续使用模运算进行100年测试,但是100被替换为25(见下文) .

    Factoring to simplify the math:

    除了使用按位AND替换模运算之外,您还可以注意到维基百科算法和优化表达式之间的另外两个争议:

    • modulo 100modulo 25 取代

    • modulo 400& 15 取代

    第100年测试使用 modulo 25 而不是 modulo 100 . 我们可以做到这一点,因为100个因子超出2 x 2 x 5 x 5.因为第4年的测试已经检查了因子4,我们可以从100中消除该因子,留下25个 . 这种优化对于几乎每个CPU实现都可能是微不足道的(因为100和25都适合8位) .

    第400年测试使用 & 15 ,相当于 modulo 16 . 同样,我们可以做到这一点,因为有400个因子可以达到2 x 2 x 2 x 2 x 5 x 5.我们可以消除因为第100次测试而测试的因子25,留下16个 . 我们不能进一步减少16因为8是因此,消除任何更多的因素将导致200年不需要的积极因素 .

    对于8位CPU来说,第400年的优化非常重要,首先是因为它避免了划分;但更重要的是,因为值400是9位数,在8位CPU中处理起来要困难得多 .

    Short-circuit Logical AND/OR operators:

    使用的最后也是最重要的优化是短路逻辑AND('&&')和OR('||')运算符(参见:Wikipedia: Short-circuit evaluation),它们以大多数类C语言实现 . 短路操作符之所以如此命名,是因为如果左侧的表达式本身决定了操作的结果,那么它们就不会费心去评估右侧的表达式 .

    例如:如果年份是2003年,则 year & 3 == 0 为假 . 逻辑AND右侧的测试无法使结果成立,因此无法评估任何其他内容 .

    通过首先执行第四年测试,仅四分之三(75%)的时间评估第四年测试(简单的按位-AND) . 这大大加快了程序的执行速度,特别是因为它避免了第100年测试所需的划分(模25操作) .

    NOTE ON PARENTHESES PLACEMENT

    一位评论者认为括号在我的代码中放错位置,并建议子表达式在逻辑AND运算符(而不是逻辑OR)周围重新分组,如下所示:

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

    以上是不正确的 . 逻辑AND运算符的优先级高于逻辑OR,并且首先使用或不使用新括号进行求值 . 逻辑AND参数周围的括号无效 . 这可能导致人们完全消除子分组:

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

    但是,在上述情况下,逻辑OR(第400年测试)的右侧几乎每次都被评估(即,不能被4和100整除的年份) . 因此,错误地消除了有用的优化 .

    我原始代码中的括号实现了最优化的解决方案:

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

    这里,逻辑OR仅被评估为可被4整除的年份(因为短路AND) . 逻辑OR的右侧仅评估可被4和100整除的年份(因为短路OR) .

    NOTE FOR C/C++ PROGRAMMERS

    C / C程序员可能会觉得这个表达式更加优化:

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

    这不是更优化!当显式 == 0!= 0 测试被删除时,它们将变为隐式并仍然执行 . 更糟糕的是,代码在强类型语言(如C#,其中 year & 3 的计算结果为 int )中不再有效,但逻辑AND( && ),OR( || )和NOT( ! )运算符需要 bool 参数 .

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

    这可能是正确的解决方案 . 维基百科上给出的算法是不对的 .

    -(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;
      }
    
  • 0

    虽然首先除以400的逻辑是无可挑剔的,但它的计算效率不如先除4 . 你可以用逻辑做到这一点:

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

    对于每个值,这除以4,但对于3/4,测试在那里终止 . 对于通过第一次测试的1/4,它除以100,消除24/25值;对于100中的剩余1个,它也除以400,得出最终答案 . 当然,这不是一个巨大的节省 .

  • 4

    来自Wikipedia article on Leap year

    if (year modulo 4 is 0) and (year modulo 100 is not 0) or (year modulo 400 is 0)
       then is_leap_year
    else
       not_leap_year
    
  • 2

    代码的问题在于,如果您认为年份是闰年,则从 yearr 返回非零值 . 因此,在if语句中不需要 ! .

  • 1

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

    闰年规则每年有一个闰年,其数量可以完全被4整除 - 除了可被100整除且不能被400整除的年份 . 该规则的第二部分影响世纪年 . 例如;世纪1600年和2000年是闰年,但世纪年1700,1800和1900年都没有 . 这意味着每四百年中有三次在闰年之间有八年 .

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

  • 0
    #include 
        void main(void)
        {
            int year;
            printf("Enter a year to check if it is Leap Year\n");
            scanf("%d",&year);
            if(year%400==0) /* Why  mod 400 */
                printf("%d is a Leap Year\n",year);
            else if(year%100==0) /*  Why  mod 100  */
                printf("%d is not a Leap Year\n",year);
            else if(year%4==0)
                printf("%d is a Leap Year\n",year);
            else
                printf("%d is not a Leap Year\n",year);
    
        }
    
  • 1
    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.");
    }
    
  • 1

    正如其他人也提到的闰年条件不正确 . 这应该:

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

    在这里阅读how to check leap year in C .

  • -6

    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) 给出你想要的东西 .

  • -1

    计算月份的最大/最后一天:1..12,年份:1..3999

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

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

    只要确保你没有进入负面年份:)

相关问题