DAY 036: _STRCMP - Pete - 12-16-2022
Ever want to compare strings, without being called a racist? Well now you can with _STRCMP...
SYNTAX compare% = _STRCMP(string1$, string2$)
What does it do? _STRCMP reads each string from left to right, and compares each ASCII character value. At the first instance where one value is not equal to the other, the function exits and returns:
-1 If the string character being compared in the first string is smaller than the string character being compared in the second string.
0 If the strings are equal.
1 If the string character being compared in the first string is larger than the string character being compared in the second string.
Also, remember that: Upper case letters are valued less than lower case letters in the ASCII evaluation. (Wiki).
So here is a quick function using our Keyword of the Day to see how it "functions."
Code: (Select All) DO
LINE INPUT "String 1: "; a$
LINE INPUT "String 2: "; b$: PRINT
IF a$ = "" AND b$ = "" THEN END
PRINT scompare(a$, b$)
PRINT
LOOP
FUNCTION scompare$ (string1$, string2$)
a% = _STRCMP(string1$, string2$)
SELECT CASE a%
CASE -1
scompare$ = "The first string is smaller than the second string."
CASE 0
scompare$ = "The first string is equal to the second string."
CASE 1
scompare$ = "The first string is larger than the second string."
END SELECT
END FUNCTION
Well hold on a minute there, Sparky...
What if I input a$ = 99.9 and b$ = 1024?
Well, _STRCMP would correctly return "The first string is larger than the second string." because with left to right evaluation, "9" of a$ is larger than "1" of b$.
To get a method to tell you if the value of a string representing a numeric value like we use in String Math routines, requires a bit of work...
Code: (Select All) DO
LINE INPUT "Number 1: "; s1$
LINE INPUT "Number 2: "; s2$
IF s1$ = "" OR s2$ = "" THEN EXIT DO
gl% = 0: sm_greater_lesser s1$, s2$, gl%
SELECT CASE gl%
CASE -1: PRINT s1$; " < "; s2$
CASE 0: PRINT s1$; " = "; s2$
CASE 1: PRINT s1$; " > "; s2$
END SELECT
PRINT
LOOP
SUB sm_greater_lesser (stringmatha$, stringmathb$, gl%)
compa$ = stringmatha$: compb$ = stringmathb$ ' So original variables do not get changed.
DO
WHILE -1 ' Falx loop.
IF gl% = 2 THEN EXIT WHILE ' For bypassing sign and decimal adjustments when only positive non-decimal numbers are being evaluated.
' Remove trailing zeros after a decimal point.
IF INSTR(compa$, ".") THEN
DO UNTIL RIGHT$(compa$, 1) <> "0" AND RIGHT$(compa$, 1) <> "." AND RIGHT$(compa$, 1) <> "-"
compa$ = MID$(compa$, 1, LEN(compa$) - 1)
LOOP
END IF
IF INSTR(compb$, ".") THEN
DO UNTIL RIGHT$(compb$, 1) <> "0" AND RIGHT$(compb$, 1) <> "." AND RIGHT$(compb$, 1) <> "-"
compb$ = MID$(compb$, 1, LEN(compb$) - 1)
LOOP
END IF
IF MID$(compa$, 1, 2) = "-0" OR compa$ = "" OR compa$ = "-" THEN compa$ = "0"
IF MID$(compb$, 1, 2) = "-0" OR compb$ = "" OR compb$ = "-" THEN compb$ = "0"
' A - and +
j% = 0: k% = 0
IF LEFT$(compa$, 1) = "-" THEN j% = -1
IF LEFT$(compb$, 1) = "-" THEN k% = -1
IF k% = 0 AND j% THEN gl% = -1: EXIT DO
IF j% = 0 AND k% THEN gl% = 1: EXIT DO
j&& = INSTR(compa$, ".")
k&& = INSTR(compb$, ".")
' A starting decimal and non-decimal.
IF j&& = 0 AND k&& = 1 THEN
IF compa$ = "0" THEN gl% = -1 ELSE gl% = 1
EXIT DO
END IF
IF k&& = 0 AND j&& = 1 THEN
IF compb$ = "0" THEN gl% = 1 ELSE gl% = -1
EXIT DO
END IF
' remove decimals and align.
j2&& = 0: k2&& = 0
IF j&& <> 0 OR k&& <> 0 THEN
IF j&& THEN compa$ = MID$(compa$, 1, INSTR(compa$, ".") - 1) + MID$(compa$, INSTR(compa$, ".") + 1): j2&& = LEN(compa$) - j&& + 1
IF k&& THEN compb$ = MID$(compb$, 1, INSTR(compb$, ".") - 1) + MID$(compb$, INSTR(compb$, ".") + 1): k2&& = LEN(compb$) - k&& + 1
compa$ = compa$ + STRING$(k2&& - j2&&, "0")
compb$ = compb$ + STRING$(j2&& - k2&&, "0")
END IF
EXIT WHILE
WEND
' Remove leading zeros if any.
DO UNTIL LEFT$(compa$, 1) <> "0"
compa$ = MID$(compa$, 2)
LOOP
IF compa$ = "" THEN compa$ = "0"
DO UNTIL LEFT$(compb$, 1) <> "0"
compb$ = MID$(compb$, 2)
LOOP
IF compb$ = "" THEN compb$ = "0"
' Both positive or both negative whole numbers.
SELECT CASE LEN(compa$)
CASE IS < LEN(compb$)
gl% = -1
CASE IS = LEN(compb$)
IF compa$ = compb$ THEN
gl% = 0
ELSEIF compa$ > compb$ THEN gl% = 1
ELSEIF compa$ < compb$ THEN gl% = -1
END IF
CASE IS > LEN(compb$)
gl% = 1
END SELECT
EXIT DO
LOOP
END SUB
So sure, doing a simple numeric comparison like...
Code: (Select All) PRINT 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999997 = 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999997.1
Output 0 for False, which is correct.
...works, but below, using a strings to represent these values, the VAL() function converts our string numbers to S.N. with some loss of accuracy along the way.
Code: (Select All) PRINT VAL("99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999997") > VAL("99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999997.1")
Output -1 for True, which is incorrect; as VAL() of both is: 1D+224
So since String Math is designed for enormous numbers, a string comparison function for val() had to be created. If you have an alternative, please post, but otherwise please just keep in mind the difference between _STRCMP, which compares the ASCII character values from left to right, and the function I posted, which compares the numeric value of the strings.
Pete
RE: DAY 036: _STRCMP - SMcNeill - 12-16-2022
For string math, I always equalize my strings before doing anything else.
For example: What if I input a$ = 99.9 and b$ = 1024?
We check both and see that we have one decimal, with one decimal place, and the largest is a 4 digit number. Equalizing that, we get:
a$ = "0099.9"
b$ = "1024.0"
NOW, it's fairly obvious which is the largest value -- even when it comes to string comparison. (They're also easier to add and multiply and such like this, as well, so you're just doing work which needed to be done anyway with your values.)
RE: DAY 036: _STRCMP - Pete - 12-16-2022
Same approach, basically...
Code: (Select All) stringmatha$ = "99.9"
stringmathb$ = "1024"
compa$ = stringmatha$: compb$ = stringmathb$ ' So original variables do not get changed.
DO
WHILE -1 ' Falx loop.
IF gl% = 2 THEN EXIT WHILE ' For bypassing sign and decimal adjustments when only positive non-decimal numbers are being evaluated.
' Remove trailing zeros after a decimal point.
IF INSTR(compa$, ".") THEN
DO UNTIL RIGHT$(compa$, 1) <> "0" AND RIGHT$(compa$, 1) <> "." AND RIGHT$(compa$, 1) <> "-"
compa$ = MID$(compa$, 1, LEN(compa$) - 1)
LOOP
END IF
IF INSTR(compb$, ".") THEN
DO UNTIL RIGHT$(compb$, 1) <> "0" AND RIGHT$(compb$, 1) <> "." AND RIGHT$(compb$, 1) <> "-"
compb$ = MID$(compb$, 1, LEN(compb$) - 1)
LOOP
END IF
IF MID$(compa$, 1, 2) = "-0" OR compa$ = "" OR compa$ = "-" THEN compa$ = "0"
IF MID$(compb$, 1, 2) = "-0" OR compb$ = "" OR compb$ = "-" THEN compb$ = "0"
' A - and +
j% = 0: k% = 0
IF LEFT$(compa$, 1) = "-" THEN j% = -1
IF LEFT$(compb$, 1) = "-" THEN k% = -1
IF k% = 0 AND j% THEN gl% = -1: EXIT DO
IF j% = 0 AND k% THEN gl% = 1: EXIT DO
j&& = INSTR(compa$, ".")
k&& = INSTR(compb$, ".")
' A starting decimal and non-decimal.
IF j&& = 0 AND k&& = 1 THEN
IF compa$ = "0" THEN gl% = -1 ELSE gl% = 1
EXIT DO
END IF
IF k&& = 0 AND j&& = 1 THEN
IF compb$ = "0" THEN gl% = 1 ELSE gl% = -1
EXIT DO
END IF
' remove decimals and align.
j2&& = 0: k2&& = 0
IF j&& <> 0 OR k&& <> 0 THEN
IF j&& THEN compa$ = MID$(compa$, 1, INSTR(compa$, ".") - 1) + MID$(compa$, INSTR(compa$, ".") + 1): j2&& = LEN(compa$) - j&& + 1
IF k&& THEN compb$ = MID$(compb$, 1, INSTR(compb$, ".") - 1) + MID$(compb$, INSTR(compb$, ".") + 1): k2&& = LEN(compb$) - k&& + 1
compa$ = compa$ + STRING$(k2&& - j2&&, "0")
compb$ = compb$ + STRING$(j2&& - k2&&, "0")
END IF
EXIT WHILE
WEND
' Remove leading zeros if any.
DO UNTIL LEFT$(compa$, 1) <> "0"
compa$ = MID$(compa$, 2)
LOOP
IF compa$ = "" THEN compa$ = "0"
DO UNTIL LEFT$(compb$, 1) <> "0"
compb$ = MID$(compb$, 2)
LOOP
IF compb$ = "" THEN compb$ = "0"
' Both positive or both negative whole numbers.
SELECT CASE LEN(compa$)
CASE IS < LEN(compb$)
gl% = -1
CASE IS = LEN(compb$)
IF compa$ = compb$ THEN
gl% = 0
ELSEIF compa$ > compb$ THEN gl% = 1
ELSEIF compa$ < compb$ THEN gl% = -1
END IF
CASE IS > LEN(compb$)
gl% = 1
END SELECT
EXIT DO
LOOP
PRINT compa$, compb$
END
RE: DAY 036: _STRCMP - Pete - 12-17-2022
So since I wrote about _STRCMP, which came along after I made the abve function, I figured, hey, might as well make use of it to optimize the string math compare routine a bit...
Code: (Select All) DO
LINE INPUT "S1: "; string1$
LINE INPUT "S2: "; string2$
IF string1$ = "" AND string2$ = "" THEN SYSTEM
PRINT
SELECT CASE gl%(string1$, string2$)
CASE -1
PRINT string1$; " is smaller than " + string2$
CASE 0
PRINT string1$; " is equal to " + string2$
CASE 1
PRINT string1$; " is larger than " + string2$
END SELECT
PRINT
LOOP
FUNCTION gl% (string1$, string2$)
s1$ = string1$: s2$ = string2$: neg = 1: d1$ = "": d2$ = ""
DO ' Falx loop.
IF LEFT$(string1$, 1) = "-" THEN ' strip off - sign(s). If both are negative, continue.
IF LEFT$(string2$, 1) <> "-" THEN gl% = -1: EXIT DO
s1$ = MID$(s1$, 2)
neg = -1
END IF
IF LEFT$(string2$, 1) = "-" THEN
IF LEFT$(string1$, 1) <> "-" THEN gl% = 1: EXIT DO
s2$ = MID$(s2$, 2)
neg = -1
END IF
s1 = INSTR(s1$, "."): s2 = INSTR(s2$, ".") ' ID if decimal(s).
IF s1 THEN x1$ = MID$(s1$, 1, s1 - 1): d1$ = MID$(s1$, s1 + 1) ELSE x1$ = s1$ ' strip off decimal(s).
IF s2 THEN x2$ = MID$(s2$, 1, s2 - 1): d2$ = MID$(s2$, s2 + 1) ELSE x2$ = s2$
IF LEN(x1$) > LEN(x2$) THEN x2$ = STRING$(LEN(x1$) - LEN(x2$), "0") + x2$
IF LEN(x2$) > LEN(x1$) THEN x1$ = STRING$(LEN(x2$) - LEN(x1$), "0") + x1$
IF LEN(d1$) > LEN(d2$) THEN d2$ = d2$ + STRING$(LEN(d1$) - LEN(d2$), "0")
IF LEN(d2$) > LEN(d1$) THEN d1$ = d1$ + STRING$(LEN(d2$) - LEN(d1$), "0")
x1$ = x1$ + "." + d1$: x2$ = x2$ + "." + d2$
gl% = _STRCMP(x1$, x2$) * neg
EXIT DO
LOOP
END FUNCTION
I'm not 100% sure if it's bullet proof yet, so if anyone can find a flaw, please let me know.
Pete
|