Thread: is leap year function

  1. #1
    11DE784A SirPrattlepod's Avatar
    Join Date
    Aug 2013
    Posts
    485

    is leap year function

    Hi,

    I've written this to calculate leap years but it's slow. Any ideas on how to improve? I've already used stuff like a ^= a to make things 0 (so therefore quicker) but maybe it can be optimised more.

    Code:
    #include <stdio.h>
    
    #define COG1_TEETH    0x04
    #define COG2_TEETH    0x19
    #define COG3_TEETH    0x04
    #define BASE          0x630
    /* BASE == (4 * COG1_TEETH * COG2_TEETH * COG3_TEETH - 0x10) */
     
    int il(int v);
    
    int main(void) {
        int year;
        printf("Please enter a year\n");
        scanf("%d", &year);
        printf("%d is %s\n", year, il(year) 
                                        ? "a leap year" 
                                        : "not a leap year");
        return 0;
    }
     
    int il(int y) {
     
        unsigned a, b, c;
        unsigned i;
            
        if (y < BASE) return 0;
        
        a = 0;
        b = COG2_TEETH - COG1_TEETH;
        c = COG3_TEETH - (COG1_TEETH >> 2);
        i = y - BASE;
        
        while (i--) {
            if (++a == COG1_TEETH) {
                a ^= a;
                if (++b == COG2_TEETH) {
                    b ^= b;
                    if (++c == COG3_TEETH)
                        c ^= c;
                }
            }
        }
     
        return !b ? !a && !c : a == 0;
    }

  2. #2
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    Quote Originally Posted by SirPrattlepod View Post
    I've already used stuff like a ^= a to make things 0 (so therefore quicker)
    Do you have profiler results to prove your point?

    Code:
    (COG1_TEETH >> 2)
    looks like zero here

    Code:
    while (i--) {
    I would just use for loop to make it easier to compiler to optimize

    Do you have a link to algorithm you are implementing?
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  3. #3
    11DE784A SirPrattlepod's Avatar
    Join Date
    Aug 2013
    Posts
    485
    Thanks vart.

    Are for loops quicker? I'll look into that.

    My reference is: Leap year - Wikipedia, the free encyclopedia

    Thanks!

    Edit: Maybe I could use threads?
    Last edited by SirPrattlepod; 08-14-2013 at 12:22 AM.

  4. #4
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    So why not to use simple approach
    Code:
    if year is divisible by 400 then
       is_leap_year
    else if year is divisible by 100 then
       not_leap_year
    else if year is divisible by 4 then
       is_leap_year
    else
       not_leap_year
    Are for loops quicker?
    One of the problems with performance of code like yours - is failed branch prediction. (profiler should tell you if your code really has such problems)

    for loops make it easier to compiler to avoid such problems. But without profiling it could not be guaranteed
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  5. #5
    11DE784A SirPrattlepod's Avatar
    Join Date
    Aug 2013
    Posts
    485
    Quote Originally Posted by vart View Post
    Code:
    if year is divisible by 400 then
       is_leap_year
    // snipped
    I was thinking that I should test for is divisible by 4 first because that's the most common thing that will say if it's a leap year or not (before checking for century years).

    Edit: You think refactoring would be better? To avoid the loop? Not sure if that will work, but I'll look into that.

  6. #6
    Stoned Witch Barney McGrew's Avatar
    Join Date
    Oct 2012
    Location
    astaylea
    Posts
    420
    Firstly, be sure that your code actually needs to be optimised. You can do that by running your program with a profiler, which will give you an idea of where your program is using its time. If it's spending only 1% of its time in your leapyear function, and 99% elsewhere, there's hardly any reason to optimise it. Similarly, if you're writing a smaller program and it terminates successfully in a few nanoseconds, you'd just be wasting your time.

    If it's necessary to optimise this function, you can figure out the range you're most likely going to use (perhaps a few thousand years before the current year and a few thousand years after), then precompute the return values like so:

    Code:
    #include <stdio.h>
    
    enum {
    	BASE = 1584,
    	THRESHOLD = 10000
    };
    
    
    static unsigned char tab[THRESHOLD - BASE];
    
    
    int leapyear_slow(int y)
    {
    	return !(y % 400) || (y % 100 && !(y % 4));
    }
    
    
    int leapyear(int y)
    {
    	return y < BASE ? 0 :
    	       y < THRESHOLD ? tab[y - BASE] : leapyear_slow(y);
    }
    
    
    void gen(void)
    {
    	for (int i = 0; i < sizeof tab; i++)
    		tab[i] = leapyear_slow(i + BASE);
    }
    
    
    int main(void)
    {
    	const char *s[] = { "not leap year", "leap year" };
    	int n;
    
    
    	for (gen(); scanf("%d", &n) == 1; puts(s[leapyear(n)]))
    		;
    	return 0;
    }
    If you need a larger range, you can save space by packing CHAR_BIT return values into each character, and you can save time by computing the values before the programs interpretation, perhaps by writing the hex values to a text file then #including it into your initialiser list like so:

    Code:
    static unsigned char tab[THRESHOLD - BASE] = {
    #include "leapyears.txt"
    };

  7. #7
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    The OP is joking here (surely).
    The mods should keep an eye out.
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  8. #8
    11DE784A SirPrattlepod's Avatar
    Join Date
    Aug 2013
    Posts
    485
    Quote Originally Posted by oogabooga View Post
    The OP is joking here (surely).
    The mods should keep an eye out.
    ??????

    It works fine as far as I can tell. Just a bit slow.

  9. #9
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by SirPrattlepod View Post
    It works fine as far as I can tell. Just a bit slow.
    How have you determined it runs "a bit slow"?

    It seems to me that you practicing premature optimisation. In other words, you're trying to optimise earlier than necessary.

    Using techniques like a ^= a to make values zero is a classic sign. Simpler, more readable, techniques like "a = 0" work just as well and, in practice, most compilers implement that operation in the most efficient way possible (it is a pretty common operation). An operation like "a ^= a" is less likely, in practice, to be more efficient than a compiler would come up with given a statement "a = 0".
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  10. #10
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    I would essentially do what Barney has, except for two changes:
    Code:
    int leapyear(unsigned int y)
    {
        return (!(y % 4) && y % 100) || !(y % 400);
    }
    1. Reorder the sub-expressions to be evaluated in a way that takes advantage of short-circuit logic.
    2. Use unsigned int so that the compiler can turn the modulus operations into subtraction-from-multiplicative-inverse, which I know most compilers can do when the divisor is a constant and the values are unsigned.

    That's about as fast as you'll get it without going to a table-based solution.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. leap year function
    By cameuth in forum C Programming
    Replies: 5
    Last Post: 11-26-2011, 01:05 AM
  2. leap year program
    By camel-man in forum C Programming
    Replies: 5
    Last Post: 05-23-2011, 07:50 PM
  3. Leap Year Problems
    By jc99 in forum C Programming
    Replies: 13
    Last Post: 06-21-2009, 04:02 AM
  4. Program showing if a year its leap year or not.
    By Cyberman86 in forum C++ Programming
    Replies: 5
    Last Post: 09-12-2008, 08:00 AM
  5. Help with leap year program.
    By IxTanGxI in forum C Programming
    Replies: 8
    Last Post: 02-20-2006, 08:49 PM