The Museum of HP Calculators

HP Forum Archive 19

[ Return to Index | Top of Index ]

Days between dates - another solution
Message #1 Posted by Bart (UK) on 13 Aug 2009, 2:02 p.m.

In the recent challenge a formula was provided for days beteen dates, but didn't work in some instances. Actually it won't work when dates include any century years not divisible by 400.

Quote:
2.281900
3.011900
--> 2 using this algorithm (incorrect)
--> 1 on a real 12c (correct, because 1900 is not a leap year)


A single failure, ie a single time that it does not work means the program's broken.

OK, so flame me for being pedantic.

However, I offer a solution. I thought to base my solution on finding the difference between Julian Days using the Julian Day formulae:

a = INT[(14-month)/12]
y = year+4800-a
m = month + 12*a - 3


For a date in the Gregorian (current) calendar:
JD = day + INT[(153*m+2)/5] + y*365 + INT[y/4] – INT[y/100] + INT[y/400] - 32045 (this is for whole days)



Observations:
As the JD numbering starts in 4800BC, and assuming that we're only interested in 0 AD onwards, discard the 4800 in the "y" formula.
Also, as we're interested in the difference between 2 JD's and "-32045" is a common factor, discard it too.


My first implementation is on the 32S,
A01   LBL A        B21   +           B47   -
A02   STO A        B22   5           B48   RCL C
A03   x<>y         B23   ÷           B49   400
A04   STO B        B24   IP          B50   ÷
A05   100          B25   RCL B       B51   IP
A06   STO D        B26   RCL D       B52   +
B01   LBL B        B27   *           B53   RCL B
B02   RCL B        B28   FP          B54   FP
B03   IP           B29   RCL D       B55   RCL D
B04   +/-          B30   x^2         B56   *
B05   14           B31   *           B57   IP
B06   +            B32   RCL E       B58   +
B07   12           B33   -           B59   RCL A
B08   ÷            B34   STO C       B60   STO B
B09   IP           B35   365         B61   x<>y
B10   STO E        B36   *           B62   STO A
B11   12           B37   +           B63   x<>y
B12   *            B38   RCL C       B64   FP
B13   3            B39   4           B65   x>0?
B14   -            B40   ÷           B66   GTO B
B15   RCL B        B41   IP          B67   x<>y
B16   IP           B42   +           B68   LAST x
B17   +            B43   RCL C       B69   -
B18   153          B44   RCL D       B70   RTN
B19   *            B45   ÷            
B20   2            B46   IP            

This is 5 registers, 2 labels and 76 lines. I tried a mix of registers & code and this gives me the best combination (as numbers in lines seem to use lots of memory). 178 bytes calculated as (free mem before code and with cleared registers) subtract (free mem after coding, execution and leaving registers un-cleared).


I thought a challenge would be implementing it on a 20S. Being algebraic, it takes more steps to do things. It has a fixed 99 step memory and the register memory is separate. Unfortunately I could not find a solution without having to pre-enter data on some registers (6 to 9 see below listing).
01   LBL A         34   IP           67   RCL 3
02   STO 0         35   +            68   /
03   SWAP          36   1            69   4
04   STO 1         37   2            70   =
05   C (ON)        38   *            71   IP
06   LBL B         39   RCL 2        72   -
07   RCL 1         40   -            73   (
08   IP            41   3            74   RCL 3
09   +/-           42   =            75   /
10   +             43   STO 4        76   RCL 9
11   1             44   RCL 1        77   )
12   4             45   FP           78   IP
13   =             46   *            79   =
14   /             47   RCL 9        80   +
15   1             48   =            81   RCL 3
16   2             49   IP           82   /
17   =             50   +            83   RCL 6
18   IP            51   (            84   =
19   STO 2         52   RCL 8        85   IP
20   RCL 1         53   *            86   RCL 0
21   *             54   RCL 4        87   STO 1
22   RCL 9         55   +            88   FP
23   =             56   2            89   x=0?
24   FP            57   )            90   GTO D
25   *             58   /            91   LAST
26   RCL 9         59   5            92   STO 0
27   x^2           60   =            93   GTO B
28   =             61   IP           94   LBL D
29   -             62   +            95   LAST
30   RCL 2         63   RCL 7        96   -
31   =             64   *            97   RCL 0
32   STO 3         65   RCL 3        98   =
33   RCL 1         66   +            99   RTN

Pre-program the following numbers:
100 STO 9
153 STO 8
365 STO 7
400 STO 6


Executing:
D1 INPUT D2 XEQ A


Now I do not have an 11C, nor emulator, but I Imagine the code would look like this:
01   LBL A        30   5            59   RCL 3
02   STO 0        31   3            60   ÷
03   x<>y         32   *            61   INT
04   STO 1        33   2            62   -
05   1            34   +            63   RCL 2
06   0            35   5            64   4
07   0            36   ÷            65   0
08   STO 3        37   INT          66   0
09   LBL B        38   RCL 1        67   ÷
10   RCL 1        39   RCL 3        68   INT
11   INT          40   *            69   +
12   +/-          41   FRAC         70   RCL 1
13   1            42   RCL 3        71   FRAC
14   4            43   x^2          72   RCL 3
15   +            44   *            73   *
16   1            45   RCL 4        74   INT
17   2            46   -            75   +
18   ÷            47   STO 2        76   RCL 0
19   INT          48   3            77   STO 1
20   STO 4        49   6            78   x<>y
21   1            50   5            79   STO 0
22   2            51   *            80   x<>y
23   *            52   +            81   FRAC
24   3            53   RCL 2        82   x>0?
25   -            54   4            83   GTO B
26   RCL 1        55   ÷            84   x<>y
27   INT          56   INT          85   LAST x
28   +            57   +            86   -
29   1            58   RCL 2        87   RTN

It would get a score of 119 (87lines + 7*4registers + 2*2labels). I could probably have saved on registers but was worried about program length as I think the 11C has max 99 lines (not sure about this as it dynamically re-allocates memory between registers?).

Closing observations: a = INT[(14-month)/12] gives 1 if month is <=2 and 0 if >2. Perhaps using flags could save some lines.

I am not a great programmer, so the above could probably be optimised. It would be particularly useful to see solutions for the 20S not requiring pre-entering data in some registers.

Bart

      
Re: Days between dates - another solution
Message #2 Posted by hugh steers on 13 Aug 2009, 2:22 p.m.,
in response to message #1 by Bart (UK)

Hi,

you can simplify your formula a bit more. set m = month + 12*a + 1, then lose the 2/5 in the JD formula.

This is what i did for the original problem, except i also chopped the y/100 and y/400 terms so as to get the wrong answer!

-- hugh.

      
Re: Days between dates - another solution
Message #3 Posted by Don Shepherd on 13 Aug 2009, 2:22 p.m.,
in response to message #1 by Bart (UK)

Bart, good work.

The challenge on the other thread noted that the algorithm in the 12c manual does not handle century years correctly, but since Katie and I had already developed 11c programs for that algorithm, we wanted to limit the entries to implementations of that specific algorithm.

Here is a link to another algorithm that does work for all years. Katie found this one and it is relativey easy to implement.

Thanks for your interest. I wonder how many dbd algorithms out there acutally don't handle the century years correctly.

      
Re: Days between dates - another solution
Message #4 Posted by Palmer O. Hanson, Jr. on 13 Aug 2009, 11:14 p.m.,
in response to message #1 by Bart (UK)

I noted that the program for days between dates in my HP-12C correctly handles the leap year for years divisible by 100 with one exception -- the year 4000 when the modified Gregorian calendar calls for that year not to be a leap year. The program in ML-20 of the Master Library module for the TI-59 operates in the same way. I compared the equations for days between dates on pages 208-209 of the HP-12C manual with the equations on page 76 of the manual for the TI-59 Master Library module and found that they were the same except that the TI-59 version has an additional term to take care of the years divisible by 100. For March through December the additional term is

INT(.75(INT(YYYY/100)+1))

For January and February the additional term is the same except that YYYY has been decreased by one. You can look at the ML-20 documentation at either Viktor Toth's or Joerg Woerner's site.

I did down-load the ML-20 program, altered it to bypass the last terms and verified that the omission did cause a problem at years divisible by 100 but not divisible by 400.

While looking at all of this I noted that both the HP-12C and TI-59 programs implement error trapping of the input such as not allowing the number of months to be greater than 12, etc. I don't know how much memory is used in the HP-12C for input error trapping. The entire TI-59 program for days between dates is 172 steps where 68 of those steps are used for input error trapping.

Bart wrote "Flame me for being pedantic." Here's an example of really being pedantic. Way back when, some purist discovered that input error trapping in the TI-59 program limits the number of days of any month to 31, but does not limit the days of the months that do not have 31 days to the lesser number. Thus, the TI-59 program will return the number of days between January 1, 1900 and March 3, 1900 as 61 days, but will also return the number of days between January 1, 1900 and February 31, 1900 as 61 days. I suppose someone, but not me, could argue that February 31, 1900 is really the same as March 3, 1900. The HP-12C program will indicate "Error 8" if a user tries to enter a February 31 date.

Palmer

            
Re: Days between dates - another solution
Message #5 Posted by Don Shepherd on 14 Aug 2009, 4:13 a.m.,
in response to message #4 by Palmer O. Hanson, Jr.

Hi Palmer. Regarding the year 4000 not being a leap year, apparently that idea was never officially adopted by whomever it is that makes such rules. Reference. It seems that the Earth is slowing down somewhat, which may make that adjustment unnecessary by the time 4000 rolls around. Of course, none of us will be here to have to worry about it.

On the other hand, today's date (August 14) is a VERY special day: it's my birthday!

                  
Re: Happy birthday Don
Message #6 Posted by Bart (UK) on 14 Aug 2009, 5:24 a.m.,
in response to message #5 by Don Shepherd

Happy 59th birthday!

I did note that from your example in the other thred :)

                        
Re: Happy birthday Don
Message #7 Posted by Don Shepherd on 14 Aug 2009, 10:23 a.m.,
in response to message #6 by Bart (UK)

Thanks Bart. I wondered, "how did he know 59" and then I remember the dbd example I did in the other thread. The interesting thing about the 12c dbd algorithm in the manual is that it differs from the actual implementation, because the real 12c does handle years like 1900 and 1700 correctly, so it doesn't exactly follow the algorithm in its manual. In fact, the manual (in later versions) has a disclaimer beneath the algorithm: "Additional tests are performed in order to ensure that the century (but not millenium) years are not considered leap years." Unfortunately, the disclaimer is not exactly correct either; some century years are leap years (2400), and some are not (2300), and some millenium years are leap years (2000), and some are not (3000). I guess if I had to choose for either the code to be correct or the documentation, I'd choose the code. I wish HP had documented the actual algorithm they used in the manual, however.

I teach math (and social studies sometimes), and it's always fun to ask the kids why we even have leap years. I bring in a lamp (the Sun), put it in the middle of the room, turn out the lights, and then carry a globe in a circle around the room, rotating it as I go, to illustrate what really happens in the world with the southern and northern hemispheres. And I note the tilt of the Earth on its axis, which is the reason we have seasons and is the practical reason that leap years are necessary (because I want to be surfing in June, not building snowmen!).

                              
Re: Happy birthday Don
Message #8 Posted by Palmer O. Hanson, Jr. on 14 Aug 2009, 9:55 p.m.,
in response to message #7 by Don Shepherd

Don:

First, I will add my wishes for a happy birthday.

I note that the TI-59 code does provide the desired result where years divisible by 100 are not leap years unless they are also divisible by 400 in which case the years are leap years. I have no idea whether or not the additional terms in the TI-59 implementation are optimized, and I can't recall an effort in the TI community to do so. Since the solution was in a module where memory was essentially not a limitation there would have been no real incentive to reduce keystrokes. Why not extend the challenge that Katie and you issued to include a complete solution for the HP-11C? Can such a solution be more efficient than the one that Bart proposed?

Palmer

                                    
Re: Happy birthday Don
Message #9 Posted by Don Shepherd on 14 Aug 2009, 10:23 p.m.,
in response to message #8 by Palmer O. Hanson, Jr.

Quote:
Why not extend the challenge that Katie and you issued to include a complete solution for the HP-11C?

That's a good idea. I'll start a new thread for that purpose. Bart, you can put a link to your 11c program in this thread in the new thread. Let's see what folks can do with a "correct" algorithm, that handles century years correctly. I may not post something myself, since I tend to use more registers and I don't like to keep a close check on what's in the stack. But we'll use the same rules for evaluation: lines of code + 7*registers + 2*labels.

Bart, I think in the 11c you can actually have up to 203 program lines, but you would have no registers to work with except the index register I!

                                          
Re: Happy birthday Don
Message #10 Posted by Paul Dale on 16 Aug 2009, 5:51 p.m.,
in response to message #9 by Don Shepherd

Quote:
Bart, I think in the 11c you can actually have up to 203 program lines, but you would have no registers to work with except the index register I!

Yes, the 11c is limited to 203 lines. The 34c can goto 210. In both cases, you'd only have I left as a register. The reason for the difference? Most likely because the 11c has a random number generator which uses a register for its seed.

- Pauli

                  
Re: Days between dates - another solution
Message #11 Posted by Bart (UK) on 14 Aug 2009, 5:53 a.m.,
in response to message #5 by Don Shepherd

Thank you all for your responses. Definitely some more food for thought - and options to try in programs.

I started this as a new thread because it did not implement the 12C manual's formula, which was the purpose of the previous thread.

In retrospect, that solution is not a problem within the scope of current affairs, i.e. 1 March 1900 to 28 Feb 2100.

However, as I find interest in working out such things on historical events (just for fun), I'd rather use a more complete formula.

Footnote: I found the American date format actually useful here as the month is easily extracted and used for both "a" & "m".

Bart


[ Return to Index | Top of Index ]

Go back to the main exhibit hall