home *** CD-ROM | disk | FTP | other *** search
- # Macro CMP - Compares numbers even if they are bigger than machine word size.
- # \%1 = arithmetic comparison operator (>, <, =, etc, listed below);
- # \%2 = first number (signed or unsigned decimal, integer or floating-point);
- # \%3 = second number (ditto).
- #
- # Succeeds if comparison is true, fails if not, or if called incorrectly.
- # Example:
- #
- # cmp > 987 -0.10001824
- # if success { commands if true } else { commands if false }
- #
- # For comparison the numbers are normalized to 24 decimal places with
- # leading zeros before the decimal point, and 24 decimal places with trailing
- # zeros after the decimal point and then compared lexically, hence no
- # limitation is imposed by machine word size. You can change the precision
- # by defining \%9 to any other number you wish.
- #
- # Illustrates:
- # . Use of \%9, \%8, ... (unused macro args) as local temporary variables.
- # . Using \findex() to see if operator matches any of the legal ones.
- # . Compact substring notation e.g. \:(\%2[1:1])
- # . Using \fverify() to see if a number is all 0's
- # . Using \fword() to break floating-point number into whole & fraction parts
- # . Using \flpad() and \frpad() to format number so that lexical comparison
- # gives the result that arithmetic comparison would have given.
- #
- # F. da Cruz, Columbia U, 18-19 December 2007
- #
- def cmp {
- .\%9 = 24 # Digits before & after decimal point for normalization
- .\%8 = 0 # sign of \%2: 0(+) 1(-)
- .\%7 = 0 # sign of \%3: 0(+) 1(-)
- # Verify arguments
- if < \v(argc) 4 end 2 "Usage: CMP operator number number"
- if not float \%2 end 2 "CMP: operand not numeric '\%2'"
- if not float \%3 end 2 "CMP: operand not numeric '\%3'"
- if not \findex(:\%1:,:<:>:<=:>=:!=:=:==:) end 2 "CMP: Bad operator '\%1'"
- # Check sign of each number
- if eq "\:(\%2[1:1])" "+" .\%2 := \:(\%2[2]) # Strip any leading plus sign
- if eq "\:(\%3[1:1])" "+" .\%3 := \:(\%3[2])
- if eq "\:(\%2[1:1])" "-" { .\%7 = 1, .\%2 := \:(\%2[2]) } # Note minuses
- if eq "\:(\%3[1:1])" "-" { .\%8 = 1, .\%3 := \:(\%3[2]) } # and strip them
- if not \fverify(0,\%2) .\%7 = 0 # watch out for "-0"
- if not \fverify(0,\%3) .\%8 = 0 # watch out for "-0"
- # Handle easy cases first
- switch \%7\%8 {
- :01, if \findex(:\%1:,:>:>=:!=:) end 0, end 1 # Signs differ
- :10, if \findex(:\%1:,:<:<=:!=:) end 0, end 1 # Signs differ
- :11, .\%5 := \%2, .\%2 := \%3, .\%3 = \%5 # Both negative - swap args
- }
- # Get here if we must compare the magnitudes (signs 00 or 11)
- # Normalize for lexical comparison \%9 digits before & after decimal point
- .\%2 := \flpad(\fword(\%2,1),\%9,0).\frpad(\fword(\%2,2),\%9,0)
- .\%3 := \flpad(\fword(\%3,1),\%9,0).\frpad(\fword(\%3,2),\%9,0)
- # Make the desired comparison
- switch \%1 {
- :<, if LLT \%2 \%3 end 0, end 1
- :>, if LGT \%2 \%3 end 0, end 1
- :\=, if EQU \%2 \%3 end 0, end 1
- :\==, if EQU \%2 \%3 end 0, end 1
- :!=, if NOT EQU \%2 \%3 end 0, end 1
- :<=, if NOT LGT \%2 \%3 end 0, end 1
- :>=, if NOT LLT \%2 \%3 end 0, end 1
- }
- }
-