Compare two long integers using unsigned arithmetic

Why on Earth would anyone need to compare two unsigned long integers in
S*BASIC? Well, there could potentially be many reasons, but the most
obvious one is for comparing dates.

Under Qdos and SMSQ/E dates are represented as long integers signifying the
number of seconds since 1961 Jan 01 00:00:00. Other OSes will have different
base dates and may not always use 32 bits to represent them. And perhaps for
a good reason, as 32 bits cant represent forever. 4,294,967,294 may seem
like a lot of seconds, but in fact, "the last syllable of recorded time" for
the QL will arrive on Feb 06, 2097 at 06:28:15! If youre still around, and
no one has fixed it by then, expect a spectacular Y2.1K moment all of your
own, 'cause thats when the "QL" clock finally runs out of seconds.

But there is another critical date long before that, and not that far off
either, namely 2029 Jan 19 03:14:07. Thats when the unsigned number range
runs out, 2,147,483,647 seconds from 1961 base line. That is the number
2^31-1. Try at the console:

        PRINT DATE$(2^31-1)

Then try:

        PRINT DATE$(2^31)

and see what happens! The next second after 2^31-1 is not 2^31, as one might
expect, but -2^31! ie the "negative" number -2,147,483,648.

I wont go into the ins and outs of how numbers are represented internally in
binary computer systems. If you dont already know, you could lookup "two's
complement" in Wikipedia.

Back to LCMP. LCMP ignores the convention of signed and unsigned binary
numbers and does a simple, so called unsigned, comparison of two binary
numbers and returns the result of that comparison.

        result = LCMP(ulong1, ulong2)

              result: ulong1 > ulong2 =>  1
                      ulong1 = ulong2 =>  0
                      ulong1 < ulong2 => -1

S*BASIC uses the signed number convention internally, so when you supply a
long integer as a parameter it will complain if the namber is above 2^31-1 or
below -2^31. However, the DATE function works with unsigned long integers
so there we have to use unsigned arithmatic for, eg, date calculations and

Say you want to find all files between two dates and have to run through a
few hundred - or a few thousand - files to find them. Comparing date
strings could take rather a long time and would also require some nifty
footwork to deal with the month names in all the various languages.

LCMP is much quicker and will correctly compare any two dates dates from
day dot to (nearly) the end of the century.

An alternative is to float the datestamp, adjust the sign, and then do the
comparison. For example thus:

100 DEFine FuNction DtCmp(dt1, dt2)
102 LOCal d1, d2
104 d1 = MkFloat(dt1)
106 d2 = MkFloat(dt2)
108 IF d1 > d2: RETurn 1
110 IF d1 = d2: RETurn 0
112 RETurn -1
114 END DEFine DtCmp
116 :
118 :
120 DEFine FuNction MkFloat(n)
122 IF n < 0: RETurn 2^32 + n
124 RETurn n
126 END DEFine MkFloat
128 :

(Obviously, youd modify this routine if it were in a loop comparing one
date to many..) This method is fast enough for the odd file date comparison,
but not ideal if you need to do hundreds or thousands of comparisons. For
that use LCMP!

Bonus: The MkFloat routine is also perhaps useful for date calculations, eg
to calculate the number of days between two dates, eg:

10 CLS
12 d1 =   -1 : PRINT DATE$(d1)
14 d2 = DATE : PRINT DATE$(d2)
16 dd = (MkFloat(d1) - MkFloat(d2)) / 86400
18 PRINT INT(dd + 0.5)
20 :

prints the number of days between today and doomsday!

Program status

V0.02, pjw, 2021 Nov 28

Conditions and DISCLAIMER as per