我使用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;
}
答案 0 :(得分:108)
if ((year & 3) == 0 && ((year % 25) != 0 || (year & 15) == 0))
{
/* leap year */
}
此代码在C,C ++,C#,Java和许多其他类C语言中有效。该代码使用单个TRUE / FALSE表达式,该表达式由三个单独的测试组成:
year & 3
year % 25
year & 15
有关此代码如何工作的完整讨论如下所示,但首先需要讨论维基百科的算法:
维基百科发布了一个伪代码算法(参见:Wikipedia: Leap year - Algorithm),该算法一直受到不断的编辑,舆论和故意破坏。
不要实施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年的测试中失败的测试#34; (模4测试) - 这是75%的时间!通过重新排序算法以执行第4年测试,我们可以显着提高速度。
<强>&#34; MOST效&#34;伪代码算法
我向维基百科提供了以下算法(不止一次):
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
这&#34;效率最高&#34;伪代码只是改变了测试的顺序,因此首先进行4除法,然后是不常发生的测试。因为&#34;年&#34;在75%的时间内,该算法不会分为四个,算法在四个案例中的三个案例中仅进行一次测试后结束。
注意:我与各种维基百科编辑进行了斗争,以改进在那里发布的算法,认为许多新手和专业程序员很快就会到达维基百科页面(由于顶级搜索引擎列表)并实施没有任何进一步研究的维基百科伪代码。维基百科编辑拒绝并删除了我为改进,注释甚至仅仅注释已发布的算法所做的每一次尝试。显然,他们觉得找到效率是程序员的问题。这可能是真的,但许多程序员都急于进行可靠的研究!
按位-AND代替模数:
我已经使用按位AND运算替换了维基百科算法中的两个模运算。为什么以及如何?
执行模数计算需要除法。在编程PC时,人们通常不会三思而后行,但是当编写嵌入在小型设备中的8位微控制器时,您可能会发现CPU无法执行除法功能。在这样的CPU上,除法是一个艰难的过程,涉及重复循环,位移和加/减操作,这些操作非常慢。非常希望避免。
事实证明,使用按位AND运算可以交替实现两个幂的模数(参见:Wikipedia: Modulo operation - Performance Issues):
x%2 ^ n == x&amp; (2 ^ n - 1)
许多优化编译器会将这些模运算转换为按位AND,但对于较小和较不流行的CPU,不太高级的编译器可能不会。 Bitwise-AND是每个CPU上的单个指令。
将modulo 4
和modulo 400
测试替换为& 3
和& 15
(见下文:&#39;保理以减少数学&#39;)我们可以确保最快的代码结果,而不使用慢得多的除法运算。
没有两个等于100的幂。因此,我们被迫继续使用模运算进行100年测试,但是100被替换为25(见下文)。
考虑简化数学:
除了使用bitwise-AND替换模运算之外,您还可以注意到维基百科算法和优化表达式之间的另外两个争议:
modulo 100
已被modulo 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年不需要的积极因素。
第400年的优化对于8位CPU非常重要,首先是因为它避免了划分;但更重要的是,因为值400是一个9位数,在8位CPU中处理起来要困难得多。
短路逻辑AND / OR运算符:
使用的最后也是最重要的优化是短路逻辑AND(&#39;&amp;&amp;&#39;)和OR(&#39; ||&#39;)运算符(参见:Wikipedia: Short-circuit evaluation),以大多数类C语言实现。短路操作符之所以如此命名,是因为如果左侧的表达式本身决定了操作的结果,他们就不会费心去评估右侧的表达式。
例如:如果年份是2003年,则year & 3 == 0
为假。逻辑AND右侧的测试无法使结果成立,因此没有其他任何内容得到评估。
首先进行第4年测试,仅对四分之三(75%)的时间进行第四年测试(简单的按位-AND)评估。这大大加快了程序的执行速度,特别是因为它避免了第100年测试所需的划分(模25操作)。
关于父母安置的说明
一位评论者认为括号在我的代码中放错位置,并建议将子表达式重新组合在逻辑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)。
对于C / C ++程序员的说明
C / C ++程序员可能会觉得这个表达式更加优化:
if (!(year & 3) && ((year % 25) || !(year & 15))) { /* LY */ }
这不是更优化!虽然删除了显式== 0
和!= 0
测试,但它们仍然是隐式的并且仍在执行。更糟糕的是,代码在强类型语言中不再有效,例如C#year & 3
评估为int
,但逻辑AND(&&
),OR(||
)和NOT(!
)运算符需要bool
个参数。
答案 1 :(得分:13)
确定闰年的逻辑是错误的。这应该让你开始(来自维基百科):
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 :(得分:6)
int isLeapYear(int year)
{
return (year % 400 == 0) || ( ( year % 100 != 0) && (year % 4 == 0 ));
}
答案 3 :(得分:4)
虽然首先除以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 :(得分:4)
这可能是正确的解决方案。维基百科上给出的算法是不对的。
-(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;
}
答案 5 :(得分:3)
许多答案都谈到了性能。没有显示任何测量值。
gcc在__builtin_expect
上的文档中有一个不错的报价是这样的:
众所周知,程序员在预测他们的程序实际执行情况时表现很差。
大多数实现都使用短路&&
和||
作为优化工具,并继续为“最佳”性能的可除性检查规定“正确”顺序。值得一提的是,短路并不是必要的一项优化功能。
同意,某些检查可能会给出确定的答案(例如,年份不是4的倍数),并使后续测试无效。在这一点上立即返回而不是继续进行不必要的计算似乎是合理的。另一方面,早期的回报会引入分支机构,这可能会降低绩效。 (请参见这个传奇的post。)很难猜测分支错误预测与不必要的计算之间的折衷。实际上,它取决于硬件,输入数据,编译器发出的确切汇编指令(可能会从一个版本更改为另一个版本)等。
续集应显示在quick-bench.com中获得的测量结果。在所有情况下,我们都会测量检查std::array<int, 65536>
中存储的每个值是否为a年所花费的时间。这些值是伪随机的,均匀分布在间隔[-400,399]中。更准确地说,它们是由以下代码生成的:
auto const years = [](){
std::uniform_int_distribution<int> uniform_dist(-400, 399);
std::mt19937 rng;
std::array<int, 65536> years;
for (auto& year : years)
year = uniform_dist(rng);
return years;
}();
即使有可能,我也不会将%
替换为&
(例如,year & 3 == 0
而不是year % 4 == 0
)。我相信编译器(位于-O3
的GCC-9.2)会为我完成此任务。 (是)
leap年的检查通常以三种可分性测试的形式编写:year % 4 == 0
,year % 100 != 0
和year % 400 == 0
。以下是涵盖所有可能出现这些检查的命令的实现的列表。每个实现都有一个相应的标签作为前缀。 (某些命令允许两种不同的实现,在这种情况下,第二种实现带有后缀b
。)它们是:
b4_100_400 : year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
b4_100_400b : (year % 4 == 0 && year % 100 != 0) || year % 400 == 0
b4_400_100 : year % 4 == 0 && (year % 400 == 0 || year % 100 != 0)
b100_4_400 : (year % 100 != 0 && year % 4 == 0) || year % 400 == 0
b100_400_4 : (year % 100 != 0 || year % 400 == 0) && year % 4 == 0
b400_4_100 : year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)
b400_100_4 : year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)
b400_100_4b : (year % 400 == 0 || year % 100 != 0) && year % 4 == 0
结果如下所示。 (见他们live。)
与许多人所建议的相反,首先用4
检查可除性似乎不是最好的事情。相反,至少在这些测量中,前三个小节处于最差的五个中。最好的是
b100_400_4 : (year % 100 != 0 || year % 400 == 0) && year % 4 == 0
另一个提供的提示(我必须承认,我认为这是一个很好的提示)是将year % 100 != 0
替换为year % 25 != 0
。这不会影响正确性,因为我们还会检查year % 4 == 0
。 (如果数字是4
的倍数,则100
的可除性等于25
的可除性。)类似地,由于以下原因,year % 400 == 0
可以替换为year % 16 == 0
25
进行了除数检查。
与上一节一样,我们有8个实现使用4-25-16的除数检查:
b4_25_16 : year % 4 == 0 && (year % 25 != 0 || year % 16 == 0)
b4_25_16b : (year % 4 == 0 && year % 25 != 0) || year % 16 == 0
b4_16_25 : year % 4 == 0 && (year % 16 == 0 || year % 25 != 0)
b25_4_16 : (year % 25 != 0 && year % 4 == 0) || year % 16 == 0
b25_16_4 : (year % 25 != 0 || year % 16 == 0) && year % 4 == 0
b16_4_25 : year % 16 == 0 || (year % 4 == 0 && year % 25 != 0)
b16_25_4 : year % 16 == 0 || (year % 25 != 0 && year % 4 == 0)
b16_25_4b : (year % 16 == 0 || year % 25 != 0) && year % 4 == 0
结果(实时here):
同样,首先用4
检查可除性似乎不是一个好主意。在这一轮中,快速是
b25_16_4 : (year % 25 != 0 || year % 16 == 0) && year % 4 == 0
如前所述,分支可能会降低性能。特别地,短路可能适得其反。在这种情况下,一种经典的技巧是将逻辑运算符&&
和||
替换为按位的&
和|
。实现变为:
nb4_100_400 : (year % 4 == 0) & ((year % 100 != 0) | (year % 400 == 0))
nb4_100_400b : ((year % 4 == 0) & (year % 100 != 0)) | (year % 400 == 0)
nb4_400_100 : (year % 4 == 0) & ((year % 400 == 0) | (year % 100 != 0))
nb100_4_400 : ((year % 100 != 0) & (year % 4 == 0)) | (year % 400 == 0)
nb100_400_4 : ((year % 100 != 0) | (year % 400 == 0)) & (year % 4 == 0)
nb400_4_100 : (year % 400 == 0) | ((year % 4 == 0) & (year % 100 != 0))
nb400_100_4 : (year % 400 == 0) | ((year % 100 != 0) & (year % 4 == 0))
nb400_100_4b : ((year % 400 == 0) | (year % 100 != 0)) & (year % 4 == 0)
结果(实时here):
一个显着的特征是性能差异不如分支情况那么明显,并且很难宣布获胜者。我们选择一个:
nb100_400_4 : ((year % 100 != 0) | (year % 400 == 0)) & (year % 4 == 0)
为完成本练习,我们考虑具有4-25-16除数测试的无分支情况:
nb4_25_16 : (year % 4 == 0) & ((year % 25 != 0) | (year % 16 == 0))
nb4_25_16b : ((year % 4 == 0) & (year % 25 != 0)) | (year % 16 == 0)
nb4_16_25 : (year % 4 == 0) & ((year % 16 == 0) | (year % 25 != 0))
nb25_4_16 : ((year % 25 != 0) & (year % 4 == 0)) | (year % 16 == 0)
nb25_16_4 : ((year % 25 != 0) | (year % 16 == 0)) & (year % 4 == 0)
nb16_4_25 : (year % 16 == 0) | ((year % 4 == 0) & (year % 25 != 0))
nb16_25_4 : (year % 16 == 0) | ((year % 25 != 0) & (year % 4 == 0))
nb16_25_4b : ((year % 16 == 0) | (year % 25 != 0)) & (year % 4 == 0)
结果(实时here):
再一次,很难定义最好的,我们选择这个:
nb25_16_4 : ((year % 25 != 0) | (year % 16 == 0)) & (year % 4 == 0)
现在是时候挑选上一节的最佳内容并进行比较了:
b100_400_4 : (year % 100 != 0 || year % 400 == 0) && year % 4 == 0
b25_16_4 : (year % 25 != 0 || year % 16 == 0) && year % 4 == 0
nb100_400_4 : ((year % 100 != 0) | (year % 400 == 0)) & (year % 4 == 0)
nb25_16_4 : ((year % 25 != 0) | (year % 16 == 0)) & (year % 4 == 0)
结果(实时here):
此图表明,短路确实是一种优化,但4
的可除性应该是最后要检查的,而不是首先要检查的。为了获得更好的性能,应首先检查100
的可除性。这真是令人惊讶!毕竟,后一项测试永远不足以决定年份是否是leap年,因此始终需要进行后续测试(通过400
或4
)。
令人惊讶的是,对于使用更简单的除数25
和16
的分支版本,它并不比使用更直观的100
和400
好。我可以提供我的“理论”,该理论也部分解释了为什么首先测试100
比测试4
更好。正如许多人指出的那样,4
的可除性测试将执行分为(25%,75%)部分,而100
的测试将其分为(1%,99%)部分。没关系,在进行后者检查之后,执行必须继续进行另一项测试,因为至少分支预测器更可能正确猜测要走的路。类似地,用25
检查可除性会将执行分为(4%,96%),这对分支预测器而言比(1%,99%)更具挑战性。看起来最好是使分布的熵最小,从而对分支预测器有所帮助,而不是使提前返回的概率最大化。
对于无分支版本,简化的除数确实提供了更好的性能。在这种情况下,分支预测器不起作用,因此,越简单越好。通常,编译器可以使用较小的数字来执行更好的优化。
我们撞墙了吗,发现了
b100_400_4 : (year % 100 != 0 || year % 400 == 0) && year % 4 == 0
leap年表现最好的支票吗?当然不。例如,我们没有混合分支运算符&&
或||
而没有分支运算符&
和|
。也许...让我们将以上内容与其他两种实现方式进行比较。第一个是
m100_400_4 : (year % 100 != 0 || year % 400 == 0) & (year % 4 == 0)
(注意分支||
和非分支&
运算符的混合。)第二个是晦涩的“ hack”:
bool b;
auto n = 0xc28f5c29 * year;
auto m = n + 0x051eb850;
m = (m << 30 | m >> 2);
if (m <= 0x28f5c28)
b = n % 16 == 0;
else
b = n % 4 == 0;
return b
后者可以工作吗?是的,它确实。与其给出数学证明,我不建议将上面发出的代码与此可读性更高的代码进行比较:
bool b;
if (year % 100 == 0)
b = year % 16 == 0;
else
b = year % 4 == 0;
return b;
在Compiler Explorer中。它们几乎相同,唯一的区别是一个使用add
指令,而另一个使用lea
指令。这应该使您确信hack代码确实有效(只要其他代码可以正常工作)。
基准测试结果(实时here):
等等,我听你说,为什么不使用上面图片中更具可读性的代码?好吧,我已经尝试并学习了另一堂课。当将此代码插入到基准循环中时,编译器将源代码作为一个整体进行查看,并决定发出与单独查看源代码时不同的代码。性能较差。走吧!
我不知道!我们可以探索很多事情。例如,最后一部分显示了另一个使用if
语句而不是短路的版本。那可能是获得更好性能的一种方式。我们也可以尝试使用三元运算符?
。
请注意,所有测量和结论均基于GCC-9.2。使用其他编译器和/或版本,情况可能会发生变化。例如,版本9.1的GCC引入了一种新的改进的用于除数检查的算法。因此,较旧的版本具有不同的性能,并且不必要的计算和分支错误预测之间的权衡可能已经改变。
我们绝对可以得出的结论是:
答案 6 :(得分:3)
来自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
答案 7 :(得分:2)
您的代码存在的问题是,如果您认为年份是闰年,则会从yearr
返回非零值。因此,您不需要if语句中的!
。
答案 8 :(得分:1)
#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); }
答案 9 :(得分:1)
还有2个解决方案似乎在quick-bench.com基准测试中胜过以前的解决方案。
这个有一个测试,但是可以用clang编译成无分支代码:
int isleap3(int year) {
unsigned y = year + 16000;
return (y % 100) ? !(y % 4) : !(y % 16);
}
这只使用一次模运算,没有测试,只编译为2个乘法:
static unsigned char const leaptest[400] = {
1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,
0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,
0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,
0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,
0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,
0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,
0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,
0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,
0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,
0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,
0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,
0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,
0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,
0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,
0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,
0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,
};
int isleap4(int year) {
unsigned y = year + 16000;
return leaptest[y % 400];
}
isleap3: # @isleap3
add edi, 16000
imul eax, edi, -1030792151
ror eax, 2
cmp eax, 42949673
mov eax, 15
mov ecx, 3
cmovb ecx, eax
xor eax, eax
test ecx, edi
sete al
ret
isleap4: # @isleap4
add edi, 16000
imul rax, rdi, 1374389535
shr rax, 39
imul eax, eax, 400
sub edi, eax
movzx eax, byte ptr [rdi + leaptest]
ret
leaptest:
.asciz "\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\000\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\000\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\000\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000\000\001\000\000"
答案 10 :(得分: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.");
}
答案 11 :(得分: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。
答案 12 :(得分:1)
http://www.wwu.edu/depts/skywise/leapyear.html
闰年规则
每年有一个闰年 数字可以完全被4整除 - 除了可以被100整除且不能被整除的年份 400.该规则的第二部分影响了世纪年。例如; 百年1600和2000年 闰年,但世纪年 1700,1800和1900不是。这个 意味着每次都是三次 四百年有八个 闰年之间的年份。
答案 13 :(得分:0)
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 :(得分:0)
int yearr(int year)
{
if(((year%4 == 0) && (year%100 !=0)) || (year%400==0))
return 1;
else
return 0;
}
答案 15 :(得分:-1)
计算月份的最大/最后一天:1..12,年份:1..3999
maxDays = month == 2 ?
28 + ((year & 3) == 0 && ((year % 25) != 0 || (year & 15) == 0)) :
30 + ((month & 1) ^ (month > 7));
答案 16 :(得分:-6)
#define is_leap(A) !((A) & 3)
请确保您没有进入否定年份:)