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

时间:2010-07-10 17:29:14

标签: c leap-year

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

17 个答案:

答案 0 :(得分:108)

最有效的闰年测试:

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),该算法一直受到不断的编辑,舆论和故意破坏。

不要实施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%的时间内,该算法不会分为四个,算法在四个案例中的三个案例中仅进行一次测试后结束。

  

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

讨论&#34;最有效率的&#34; LEAP年度测试

按位-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 4modulo 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)会为我完成此任务。 (是)

4-100-400测试

leap年的检查通常以三种可分性测试的形式编写:year % 4 == 0year % 100 != 0year % 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-100-400 tests

与许多人所建议的相反,首先用4检查可除性似乎不是最好的事情。相反,至少在这些测量中,前三个小节处于最差的五个中。最好的是

b100_400_4  : (year % 100 != 0 || year % 400 == 0) && year % 4 == 0

4-25-16测试

另一个提供的提示(我必须承认,我认为这是一个很好的提示)是将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-25-16 tests

同样,首先用4检查可除性似乎不是一个好主意。在这一轮中,快速是

b25_16_4  : (year % 25 != 0 || year % 16 == 0) && year % 4 == 0

4-100-400测试(无分支)

如前所述,分支可能会降低性能。特别地,短路可能适得其反。在这种情况下,一种经典的技巧是将逻辑运算符&&||替换为按位的&|。实现变为:

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):

4-100-400 tests (no branching)

一个显着的特征是性能差异不如分支情况那么明显,并且很难宣布获胜者。我们选择一个:

nb100_400_4  : ((year % 100 != 0) | (year % 400 == 0)) & (year % 4 == 0)

4-25-16测试(无分支)

为完成本练习,我们考虑具有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):

4-25-16 tests (no branching)

再一次,很难定义最好的,我们选择这个:

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):

Champions League

此图表明,短路确实是一种优化,但4的可除性应该是最后要检查的,而不是首先要检查的。为了获得更好的性能,应首先检查100的可除性。这真是令人惊讶!毕竟,后一项测试永远不足以决定年份是否是leap年,因此始终需要进行后续测试(通过4004)。

令人惊讶的是,对于使用更简单的除数2516的分支版本,它并不比使用更直观的100400好。我可以提供我的“理论”,该理论也部分解释了为什么首先测试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):

Final

等等,我听你说,为什么不使用上面图片中更具可读性的代码?好吧,我已经尝试并学习了另一堂课。当将此代码插入到基准循环中时,编译器将源代码作为一个整体进行查看,并决定发出与单独查看源代码时不同的代码。性能较差。走吧!

现在?是吗?

我不知道!我们可以探索很多事情。例如,最后一部分显示了另一个使用if语句而不是短路的版本。那可能是获得更好性能的一种方式。我们也可以尝试使用三元运算符?

请注意,所有测量和结论均基于GCC-9.2。使用其他编译器和/或版本,情况可能会发生变化。例如,版本9.1的GCC引入了一种新的改进的用于除数检查的算法。因此,较旧的版本具有不同的性能,并且不必要的计算和分支错误预测之间的权衡可能已经改变。

我们绝对可以得出的结论是:

  1. 不要想太多。比清楚难懂的优化更喜欢清晰的代码。毕竟,在升级编译器时,手工制作的代码片段可能不是最有效的选择。正如ex-nihilo在评论中说的那样:“除非有性能瓶颈,否则不要着急处理这样的一段代码。”
  2. 猜测很难,最好进行测量。
  3. 测量很困难,但是比猜测要好。
  4. 短路/分支可能会提高性能,但它取决于许多因素,包括代码在多大程度上有助于分支预测器。
  5. 在某个内联代码段中,为单独代码段发出的代码可能会有所不同。

答案 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];
}

clang 64-bit Assembly

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"

这里是benchmark results

enter image description here

答案 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;    
}  

在此处阅读how to check leap year in C

答案 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)

请确保您没有进入否定年份:)