QB64 Phoenix Edition
String eval substitute for val in progress... - Printable Version

+- QB64 Phoenix Edition (https://qb64phoenix.com/forum)
+-- Forum: Chatting and Socializing (https://qb64phoenix.com/forum/forumdisplay.php?fid=11)
+--- Forum: General Discussion (https://qb64phoenix.com/forum/forumdisplay.php?fid=2)
+--- Thread: String eval substitute for val in progress... (/showthread.php?tid=739)



String eval substitute for val in progress... - Pete - 08-05-2022

I'm trying to figure out any pitfalls that would break using the shortcut method in the code, below.

Code: (Select All)
CLS
LINE INPUT "First number  a: "; a$
LINE INPUT "Second number b: "; b$
PRINT

IF MID$(a$, 1, 2) = "-0" THEN a$ = "0"
IF MID$(b$, 1, 2) = "-0" THEN b$ = "0"

' Shortcut routine to evaluate string value comparisons.=====================
PRINT "Shortcut method...": PRINT
IF MID$(a$, 1, 1) = "-" AND MID$(b$, 1, 1) = "-" THEN
    ' Invert results
    REM PRINT "Two negatives adjustment routine..."
    IF MID$(a$, 2) < MID$(b$, 2) THEN
        PRINT "a$ > b$"
    ELSEIF MID$(a$, 2) = MID$(b$, 2) THEN PRINT "a$ = b$"
    ELSEIF MID$(a$, 2) > MID$(b$, 2) THEN PRINT "a$ < b$"
    END IF
ELSE
    IF a$ < b$ THEN
        PRINT "a$ < b$"
    ELSEIF a$ = b$ THEN PRINT "a$ = b$"
    ELSEIF a$ > b$ THEN PRINT "a$ > b$"
    END IF
END IF
PRINT

' Longer routine to evaluate string value comparisons.========================
PRINT "Long method...": PRINT
neg_a = 0: neg_b = 0: dec_a1$ = "": dec_a2$ = "": dec_b1$ = "": dec_b2$ = ""

IF MID$(a$, 1, 1) = "-" THEN neg_a = -1
IF MID$(b$, 1, 1) = "-" THEN neg_b = -1
IF INSTR(a$, ".") THEN
    dec_a1$ = MID$(a$, 1, INSTR(a$, ".") - 1): dec_a2$ = MID$(a$, INSTR(a$, ".") + 1)
ELSE
    dec_a1$ = a$
END IF

IF INSTR(b$, ".") THEN
    dec_b1$ = MID$(b$, 1, INSTR(b$, ".") - 1): dec_b2$ = MID$(b$, INSTR(b$, ".") + 1)
ELSE
    dec_b1$ = b$
END IF

DO
    ' Test for sign.
    SELECT CASE neg_a + neg_b
        CASE 0, -2 ' Both positive or negative
            IF dec_a1$ = dec_b1$ AND dec_a2$ = dec_b2$ THEN a_less_b = 0: EXIT DO ' Same number.
            IF LEN(dec_a1$) AND dec_b1$ = "" THEN a_less_b = 1: EXIT DO ' a >=1 and b is a decimal.
            IF LEN(dec_b1$) AND dec_a1$ = "" THEN a_less_b = -1: EXIT DO ' b >=1 and a is a decimal.
            IF LEN(dec_a1$) AND dec_a1$ <> dec_b1$ OR LEN(dec_b1$) AND dec_a1$ <> dec_b1$ THEN ' One or both >=1 and non-decimal parts are not equal.
                IF LEN(dec_a1$) > LEN(dec_b1$) THEN a_less_b = 1: EXIT DO
                IF LEN(dec_a1$) < LEN(dec_b1$) THEN a_less_b = -1: EXIT DO
                IF LEN(dec_a1$) = LEN(dec_b1$) THEN
                    FOR i = 1 TO LEN(dec_a1$)
                        IF MID$(dec_a1$, i, 1) <> MID$(dec_b1$, i, 1) THEN EXIT FOR
                    NEXT
                    IF MID$(dec_a1$, i, 1) < MID$(dec_b1$, i, 1) THEN a_less_b = -1: EXIT DO ELSE a_less_b = 1: EXIT DO
                END IF
            ELSE ' Both decimals or non-decimal digits are the same and cancel out.
                j = LEN(dec_a2$)
                IF LEN(dec_b2$) > j THEN j = LEN(dec_b2$)
                FOR i = i TO j
                    IF MID$(dec_a2$, i, 1) <> MID$(dec_b2$, i, 1) THEN EXIT FOR
                NEXT
                IF MID$(dec_a2$, i, 1) < MID$(dec_b2$, i, 1) THEN a_less_b = -1: EXIT DO ELSE a_less_b = 1: EXIT DO
            END IF
        CASE -1 ' One is negative.
            j = -999
            IF neg_a THEN a_less_b = -1: EXIT DO ELSE a_less_b = 1: EXIT DO
    END SELECT
    EXIT DO
LOOP
IF neg_a OR neg_b THEN IF j <> -999 THEN a_less_b = a_less_b * -1
IF a_less_b < 0 THEN PRINT "a$ < b$" ELSE IF a_less_b = 0 THEN PRINT "a$ = b$" ELSE PRINT "a$ > b$"
REM PRINT dec_a1$, dec_a2$, dec_b1$, dec_b2$, neg_a, neg_b
PRINT
SLEEP
RUN


The point I'm at now, I can get away with using string comparison as long as I disallow -0 and flip the results for two negatives. A single negative is always the smaller number but as I came to realize in string comparisons from a different tread, when faced with two negatives, a string evaluation will not change the fact that the larger numeric value of string is all that is considered. You need a sub-routine to invert the results.

So, can anyone see anything I missed here in the shortcut routine, or is this cake all backed?

Pete


RE: String eval substitute for val in progress... - Stuart - 08-08-2022

I just did a quick test, and the only problem that I could find was that any trailing 0's need to be removed from decimal numbers, and if there is only a zero (0) after the decimal then also remove the decimal.

(Try comparing 1 to 1.  -OR-  1 to 1.0)


RE: String eval substitute for val in progress... - Pete - 08-10-2022

@Stuart

Yep, and I found another flaw on top of the ones you mentioned. String comparison needs to evaluate a = 0 b = .1 so a < b. Since the asc value of zero is greater than the asc value of a decimal point, another routine is needed to adjust for this problem. So here is a remake that addresses all of these issues...

Code: (Select All)
CLS
LINE INPUT "First number  a: "; num1$
LINE INPUT "Second number b: "; num2$
PRINT

a$ = num1$: b$ = num2$

REM Remove trailing zeros after a decimal point.
IF INSTR(a$, ".") THEN
    DO UNTIL RIGHT$(a$, 1) <> "0" AND RIGHT$(a$, 1) <> "." AND RIGHT$(a$, 1) <> "-"
        a$ = MID$(a$, 1, LEN(a$) - 1)
    LOOP
END IF
IF INSTR(b$, ".") THEN
    DO UNTIL RIGHT$(b$, 1) <> "0" AND RIGHT$(b$, 1) <> "." AND RIGHT$(b$, 1) <> "-"
        b$ = MID$(b$, 1, LEN(b$) - 1)
    LOOP
END IF

IF MID$(a$, 1, 2) = "-0" OR a$ = "" OR a$ = "-" THEN a$ = "0"
IF MID$(b$, 1, 2) = "-0" OR b$ = "" OR b$ = "-" THEN b$ = "0"

PRINT "a$ = "; a$, "b$ = "; b$

' Shortcut routine to evaluate string value comparisons.=====================
PRINT "Shortcut method...": PRINT

IF a$ = "0" AND LEFT$(b$, 1) = "." OR b$ = "0" AND LEFT$(a$, 1) = "." THEN
    IF a$ = "0" THEN gl% = -1 ELSE gl% = 1
ELSE
    IF a$ < b$ THEN
        gl% = -1
    ELSEIF a$ = b$ THEN gl% = 0
    ELSEIF a$ > b$ THEN gl% = 1
    END IF
    IF LEFT$(a$, 1) = "-" AND LEFT$(b$, 1) = "-" THEN gl% = gl% * -1
END IF

SELECT CASE gl%
    CASE -1
        PRINT "a$ < b$"
    CASE 0
        PRINT "a$ = b$"
    CASE 1
        PRINT "a$ > b$"
END SELECT
PRINT

' Longer routine to evaluate string value comparisons.========================
PRINT "Long method...": PRINT

IF a$ = "0" AND LEFT$(b$, 1) = "." OR b$ = "0" AND LEFT$(a$, 1) = "." THEN
    IF a$ = "0" THEN gl% = -1 ELSE gl% = 1
ELSE
    neg_a = 0: neg_b = 0: dec_a1$ = "": dec_a2$ = "": dec_b1$ = "": dec_b2$ = ""

    IF MID$(a$, 1, 1) = "-" THEN neg_a = -1
    IF MID$(b$, 1, 1) = "-" THEN neg_b = -1
    IF INSTR(a$, ".") THEN
        dec_a1$ = MID$(a$, 1, INSTR(a$, ".") - 1): dec_a2$ = MID$(a$, INSTR(a$, ".") + 1)
    ELSE
        dec_a1$ = a$
    END IF

    IF INSTR(b$, ".") THEN
        dec_b1$ = MID$(b$, 1, INSTR(b$, ".") - 1): dec_b2$ = MID$(b$, INSTR(b$, ".") + 1)
    ELSE
        dec_b1$ = b$
    END IF

    DO
        ' Test for sign.
        SELECT CASE neg_a + neg_b
            CASE 0, -2 ' Both positive or negative
                IF dec_a1$ = dec_b1$ AND dec_a2$ = dec_b2$ THEN gl% = 0: EXIT DO ' Same number.
                IF LEN(dec_a1$) AND dec_b1$ = "" THEN gl% = 1: EXIT DO ' a >=1 and b is a decimal.
                IF LEN(dec_b1$) AND dec_a1$ = "" THEN gl% = -1: EXIT DO ' b >=1 and a is a decimal.
                IF LEN(dec_a1$) AND dec_a1$ <> dec_b1$ OR LEN(dec_b1$) AND dec_a1$ <> dec_b1$ THEN ' One or both >=1 and non-decimal parts are not equal.
                    IF LEN(dec_a1$) > LEN(dec_b1$) THEN gl% = 1: EXIT DO
                    IF LEN(dec_a1$) < LEN(dec_b1$) THEN gl% = -1: EXIT DO
                    IF LEN(dec_a1$) = LEN(dec_b1$) THEN
                        FOR i = 1 TO LEN(dec_a1$)
                            IF MID$(dec_a1$, i, 1) <> MID$(dec_b1$, i, 1) THEN EXIT FOR
                        NEXT
                        IF MID$(dec_a1$, i, 1) < MID$(dec_b1$, i, 1) THEN gl% = -1: EXIT DO ELSE gl% = 1: EXIT DO
                    END IF
                ELSE ' Both decimals or non-decimal digits are the same and cancel out.
                    j = LEN(dec_a2$)
                    IF LEN(dec_b2$) > j THEN j = LEN(dec_b2$)
                    FOR i = i TO j
                        IF MID$(dec_a2$, i, 1) <> MID$(dec_b2$, i, 1) THEN EXIT FOR
                    NEXT
                    IF MID$(dec_a2$, i, 1) < MID$(dec_b2$, i, 1) THEN gl% = -1: EXIT DO ELSE gl% = 1: EXIT DO
                END IF
            CASE -1 ' One is negative.
                j = -999
                IF neg_a THEN gl% = -1: EXIT DO ELSE gl% = 1: EXIT DO
        END SELECT
        EXIT DO
    LOOP
    IF neg_a OR neg_b THEN IF j <> -999 THEN gl% = gl% * -1
    REM PRINT dec_a1$, dec_a2$, dec_b1$, dec_b2$, neg_a, neg_b
END IF
IF gl% < 0 THEN PRINT "a$ < b$" ELSE IF gl% = 0 THEN PRINT "a$ = b$" ELSE PRINT "a$ > b$"
PRINT
SLEEP
RUN

Thanks,

Pete