如何在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
和& 15
replacemodulo 4
和modulo 400
testing(参见下文:“减lessmath的因式分解”),我们可以确保最快的代码结果不会使用较慢的除法操作。
不存在两个等于100的幂。因此,我们被迫继续使用模数运算进行第100个testing,然而100取代了25(见下文)。
保理简化math:
除了使用按位“与”replace模运算,您还可以注意到维基百科algorithm与优化expression式之间的另外两个争议:
-
modulo 100
被modulo 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
和!= 0
testing被移除时,它们变得隐含并且仍然被执行。 更糟糕的是,这些代码在像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负面的一年:)