Needed a leap year function for a project. Then thought getting 28 or 29 days for a particular February would save selecting 28 or 29 based on true or false (cut the "middle-man"). The deciding arithmetic is the same for either. So in code snippets below you'll see both. Found 3 on PowerBASIC Forum, and one I wrote more then 15 years ago in my collection.
They are discussed slowest to fastest. My old one with AND 3 as pseudo MOD 4, and the bit of assembly I've learned since, inspired the winner.
Simplist part of a project can involve a greater effort. In the other project, that needed leap year, has a COMBOBOX for days in the month. By populating the COMBOBOX with 1 to 28 everytime, then then adding 29 to February in leap years, ans 29 and 30, or 29, 30 and 32 for other months, the leap code was reduced to do nothing or COMBOBOX ADD "29".
FASTPROC and FUNCTION source code on popup page IsLeap-FebDays_Procs (that opens a new window or tab).
The first found code snip I'll call "upside down" because it tests if years evenly divide by 400 first, then 100, finally 4. It is also the slowest. I'll go to next fastest all the way dowen. The coding for "upside down" is:
IF Year MOD 400 = 0 THEN IsLeap = %True ELSEIF Year MOD 100 = 0 THEN IsLeap = %False ELSEIF Year MOD 4 = 0 THEN IsLeap = %True ELSE IsLeap = %False END IFFor a consecutive sequence of 400 years MOD 400 is used 400 times, MOD 100 is used 399 times, and MOD 4 is used 396 times.
This method uses the "short-circuit evaluation" characteristic of PowerBasic's IF, in the order MOD 4, MOD 100 then MOD 400. It has the fewest lines of BASIC code and is faster than "upside down" algorithm, but slower than the "customary". Code:
if ((Year mod 4) = 0) and _ ((Year mod 100) > 0) or _ ((Year mod 400) = 0) then FebDays = 29 else FebDays = 28 end ifThere are implied IFs for Year MOD 100 > 0 and Year MOD 400, making the same number as "customary". OR and AND are not needed in "customary", which goes to nested IF on failure of "=" or ">". I believe this is why it is a bit slower than the next method.
A more customary method starts with MOD 4, then MOD 100, doing MOD 400 last. This may be coded as:
IF Year MOD 4 = 0 THEN IF Year MOD 100 = 0 THEN IF Year MOD 400 = 0 THEN IsLeap = %True ELSE IsLeap = %False END IF ELSE IsLeap = %True END IF ELSE IsLeap = %False END IFFor the same sequence, MOD 4 is used 400 times, MOD 100 100 times, and MOD 400 4 times.
A significant speed improvement over the "more customary" method is because 4 is a power of 2 (is bit 2). An AND operation with 3 (both bits 0 and 1) gives the same result as MOD 4. For about three quarters of years the MOD 4 resolves leap year state, with the AND 3 which is significanly quicker.
if (Year and 3) then 'conditionally equivilent to MOD op, FebDays = 28 '(<> 0) not a leap year else '(= 0) it maybe leap year if (Year mod 100) then '(<> 0) is a leap year FebDays = 29 else '(= 0) if (Year mod 400) then '(<> 0) it is not a leap year FebDays = 28 else '(= 0) it is a leap year FebDays = 29 end if end if end if(the "condition" is the divisor must be a power of 2)
This assembly version would not be fastest (maybe by a tiny amount) except that the assembly DIV gives quotient and remainder in single operation. The divide by 100 quotient remains in register EAX, allowing the MOD 400 to be replaced by another AND 3. It becomes even quicker if the code it is being used with is also assembly, or put in a procedure alone because the PUSHes and POPs may be removed (procedure overhead is slower than those PUSHes and POPs).
! push eax 'for pseudo MOD, dividend and quotient ! mov FebDays, 28& 'not a leap year, pre-set to 28 ! mov eax, 3??? ! and eax, Year 'conditionally equivalent MOD 4 ! jz MOD100 '0 is possible leap year ! jmp Done MOD100: ! push ebx 'for divisor ! push edx 'for high part of dividend, remainder (MOD) ! xor edx, edx ! mov eax, Year ! mov ebx, 100??? ! div ebx ! cmp edx, 0??? 'does MOD 100 = 0 ? ! pop edx ! pop ebx ! jz MOD400 '0 is possibly not a leap year ! jmp Is29Days 'non 0 is a leap year MOD400: ! and eax, 3??? 'EAX has Year\100, so conditionally equivalent MOD 400 ! jnz Done ! Is29Days: ! mov FebDays, 29& Done: ! pop eaxUsually I put the PUSHes together at the beginning and at the POPs at the end. A bit more speed is had by only PUSHing and POPing EBX and EDX for possible leap years (25% of years).
Created on 07 September 2024.
To
Domain Home. | To Dale's Notebook index. | To Programs index. |