DAY 036: _STRCMP
#1
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
Reply
#2
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.)
Reply
#3
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
Reply
#4
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
Reply




Users browsing this thread: 3 Guest(s)