tonyg 发表于 2012-11-28 23:30:12

AVR里边计算天数差的法子

打算计算天数差,翻了翻网上的讨论,都用了循环。可是像M8这样的芯片,RAM倒是有几个,可速度就不是很快。用循环在PC上也许更合适。

想了想,反正每400年公历就经历一个循环,找出400年内的润平年数,就能用乘加的方法一次性算出相对某个400年周期的总天数,然后天数差就好算了。

从表中看出,公元0年、400年...2000年是闰年(当然历法中没规定0年),所以起始加一,然后再屏蔽掉一个闰年就能得到400年内的全部闰年数,再用年数乘365之后就得到本年之前经过的总天数了。之后确定本年内的天数,就得到包括当前日期在内的全部天数。这样就回避了循环。

用OCTAVE验证之后写了下面的程序。还附了一个计算annual date的程序,两个加起来比较好用。

程序里叫做qdt_time_t的类型不过是包含年月日整型变量的一个结构而已。/*
        The days of the past years can be calculated by using the following formula:
        The year:        2000        2001        2002        2003        2004        2005        2006        2007        2008        2009        2010        2011        2012
        MOD 400:        0                1                2                3                4                5                6                7                8                9                10                11                12
        Leap year:        0                1                0                0                0                1                0                0                0                1                0                0                0
        Days past:        0                366                365                365                365                366                365                365                365                366                365                365                365
        The sums:        0                366                731                1096        1461        1827        2192        2557        2922        3288        3653        4018        4383

        Formula:        n = 1 + y*365 + fix((y-1)/4) - fix((y-1)/100) + fix((y-1)/400)
       
        Before 2012, we have 4383 + 146097 * 5 = 734868 days past. Where 146097 = 365 * 400 + 97 is a 400 years cycle.
*/
uint32_t qdt_to_long_date(const qdt_time_t *time)
{
        uint16_t year = time->year;

    /* The DOY means what day is today of the year. It is an annual date. */
    uint32_t days;
       
        if(year == 0){
                days = 0;
        }else{
                days = 1 + year * 365UL;
                year --;
                days += year/4 - year/100 + year/400;
        }

        /* Add the annual date at the final. */
        days += qdt_day_of_annual(time);

        return days;
}
uint16_t qdt_day_of_annual(const qdt_time_t *time)
{
        /* Days accumulation table of an ordinary year. */
        static const uint16_t PROGMEM annual_table[] = {
        /*x1   2   3   4    5    6    7    8    9   10   11   12 */
          0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
        };

        /* Use buffered values. */
        uint8_t month = time->month;
        uint8_t day = time->day;
       
    /* Using 16-bit unsigned integer accumulator. */
    uint16_t days = day + pgm_read_word(&annual_table);

    /* Append one day in leap year. */
    if((month > 2) && qdt_is_leap_year(time)){
      days += 1;
    }

    return days;
}
uint8_t qdt_is_leap_year(const qdt_time_t *time)
{
        /* For every 400 years, it's transmigrated. */
        uint16_t year = time->year % 400;
       
        return ((year == 0) || ((year % 4 == 0) && (year % 100 != 0)));
}       

tonyg 发表于 2012-11-28 23:32:24

啊呀格式好乱,凑合看吧。

tonyg 发表于 2012-11-28 23:44:14

又试了试,还能计算星期数:uint8_t qdt_day_of_week(const qdt_time_t *time)
{
        uint32_t days;

        /* Calculate the days with the Saturday compensation. */       
        days = (qdt_to_long_date(time) - 1) + 6;
       
        return days % 7;
}        初验好像对的,有错误就以后更正。

qiqirachel 发表于 2014-3-6 23:13:36

擦,高手的帖子,顶一下,这算是科研了,一般人还真不知道为何要琢磨这个
页: [1]
查看完整版本: AVR里边计算天数差的法子