如何在C编程中find闰年

我用C语言编写了一个程序来查找input的年份是不是闰年。 但不幸的是,它运作不好。 它说一年是飞跃,前一年不是飞跃。

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

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

 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 modulo y的余数除以y 。 例如,12模5是2。

最有效的闰年testing:

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

此代码在C,C ++,C#,Java和许多其他C语言中有效。 该代码使用由三个单独的testing组成的单个TRUE / FALSEexpression式:

  • 第四年testing: year & 3
  • 100年testing: year % 25
  • 第400年testing:第year & 15

关于这个代码如何工作的完整讨论如下,但首先要讨论维基百科的algorithm:

维基百科algorithm是不适当的/不可靠的

维基百科已经出版了一种经常受到不断编辑,舆论和破坏行为的伪代码algorithm(见: 维基百科:闰年 – algorithm )。

不要执行WIKIPEDIAalgorithm!

其中一个最长的(和低效的)维基百科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 

上述algorithm效率低下,因为它始终执行第400年和第100年的testing,即使是在很短的时间内也会很快通过“第4年testing”(模4testing),即75%的时间! 通过重新sortingalgorithm来进行第四年testing,我们显着加快了速度。

“最有效”的伪代码algorithm

我向Wikipedia提供了以下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 

这个“最高效”的伪代码只是简单地改变了testing的顺序,所以除以4会先发生,然后是不经常发生的testing。 由于“年份”不会被四分之三的75%的时间所分割,所以在四种情况中的三种情况下,algorithm仅经过一次testing就结束了。

注意:我已经与各种维基百科编辑进行了斗争,以改进那里发布的algorithm,认为许多新手和专业程序员很快到达维基百科页面(由于顶级search引擎列表)并且实施维基百科伪代码而没有进一步的研究。 维基百科的编辑们否定和删除了我为改进,注释甚至只是脚注已发布的algorithm所做的每一个尝试。 显然,他们觉得效率是程序员的问题。 这可能是事实,但许多程序员太急于进行扎实的研究!

关于“最有效”的飞跃年鉴的探讨

按位与取代模:

我用位运算来replaceWikipediaalgorithm中的两个模运算。 为什么和如何?

执行模运算需要划分。 在编程一个PC时,人们通常不会三思而后行,但是当编写embedded在小型设备中的8位微控制器时,可能会发现CPU本身无法执行除法function。 在这样的CPU上,划分是一个艰难的过程,涉及重复循环,位移和非常缓慢的加/减操作。 避免是非常可取的。

事实certificate,两个幂的模可以通过按位与运算来交替实现(参见: Wikipedia:Modulo operation – Performance Issues ):

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

许多优化的编译器会将这些模数操作转换为按位“与”,而针对较小和不太stream行的CPU的较低级编译器可能不会。 按位AND是每个CPU上的单个指令。

通过用& 3& 15replacemodulo 4modulo 400testing(参见下文:“减lessmath的因式分解”),我们可以确保最快的代码结果不会使用较慢的除法操作。

不存在两个等于100的幂。因此,我们被迫继续使用模数运算进行第100个testing,然而100取代了25(见下文)。

保理简化math:

除了使用按位“与”replace模运算,您还可以注意到维基百科algorithm与优化expression式之间的另外两个争议:

  • modulo 100modulo 25取代
  • modulo 400& 15代替

第100年testing使用modulo 25而不是modulo 100 。 我们可以这样做,因为100个因素可以达到2 x 2 x 5 x 5.因为第4年的testing已经检查了4的因素,我们可以从100中消除这个因素,而是离开25.这个优化对于几乎每个CPU实现来说可能都是微不足道的因为100和25都适合8位)。

第四百年的testing利用& 15 ,相当于modulo 16 。 再次,我们可以这样做,因为有400个因素可以达到2 x 2 x 2 x 2 x 5 x 5.我们可以消除经过100年testingtesting的25的因子,剩下16个。我们不能进一步减less16因为8一个200的因素,所以消除更多的因素会产生一个不想要的积极的200年。

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

短路逻辑与/或运算符:

最后,也是最重要的优化是短路逻辑AND('&&')和OR('||')操作符(参见: Wikipedia:短路评估 ),它们在大多数C语言。 短路操作者的名字是这样命名的,因为如果左边的expression本身就决定了操作的结果,那么它们就不用去评估右边的expression式。

例如:如果年份是2003,那么year & 3 == 0是false。 在逻辑“与”的右侧进行testing是没有办法使结果成立的,所以没有任何东西会被评估。

首先进行第四年testing,只有第四年testing(一个简单的按位AND)被评估四分之三(75%)的时间。 这大大加快了程序的执行速度,特别是因为它避免了第100年testing(模数25操作)所需的划分。

关于父母放置的注意事项

一位评论者觉得括号在我的代码中放错了位置,并build议将子expression式重新组合在逻辑AND运算符周围(而不是围绕逻辑或),如下所示:

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

以上是不正确的。 逻辑AND运算符具有比逻辑OR更高的优先级,并且首先使用或不使用新的括号进行评估。 逻辑“与”参数周围的圆括号不起作用。 这可能导致人们完全消除这些小组:

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

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

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

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

在这里,逻辑“或”只能被4整除(因为短路AND)。 逻辑“或”的右侧仅被评估为可被4和100整除的年(由于短路OR)。

C / C ++程序员注意事项

C / C ++程序员可能会觉得这个expression式更优化:

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

这不是更优化! 当明确的== 0!= 0testing被移除时,它们变得隐含并且仍然被执行。 更糟糕的是,这些代码在像C#这样的强types语言中不再有效,其中year & 3计算为int ,但逻辑AND( && ),OR( || )和NOT( ! )运算符需要bool参数。

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

虽然先由400除以的逻辑是无可挑剔的,但它并不像第一个4除以计算有效率。 你可以用逻辑来做到这一点:

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

每个值除以4,但其中3/4的testing终止在那里。 对于通过第一次testing的1/4,则将其除以100,从而消除24/25的值; 对于100个中的其余1个,它也除以400,得出最终答案。 当然,这不是一个巨大的节约。

这可能是正确的解决scheme。 在Wikipedia上给出的algorithm是不正确的。

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

你的代码的问题是,如果你认为今年是一个闰年,你将返回一个来自yearr的非零值。 所以你不需要! 在你的if语句中。

从维基百科文章闰年

 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 

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

闰年规则

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

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

像上面那样改变它。 也读这个 。


     #包括 
     void main(void)
     {
        年份
         printf(“input一年来检查是否为闰年\ n”);
         scanf函数( “%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”,年);

     }

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

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

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

在这里阅读它如何检查闰年在C。

凯文的答案提供了一个最佳的8操作testing(XOR使用常量),但如果你正在寻找一些更可读的东西,尝试这9操作testing。

 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..12,年:1..3999

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

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

只要确保你不input负面的一年:)