Treebeard's String-Math
#7
(07-28-2022, 03:09 PM)James D Jarvis Wrote: I'm working on big number support for up to 2,147,483,645 digits with strings (need space for the negative sign and the decimal point), always happy to see how other people tackle the general idea for math with strings.

I put this together a few years back for +-?* strimg math routines.

What I didn't include was a way to carry repeating decimals (repetends). So 1 / 3 * 3 will result in .9... instead of 1, just as .3... * 3 would result in .9...

So I would consider it unfinished for use in a calculator.

Code: (Select All)
PRINT
INPUT "Limit Display: "; limit&&
LINE INPUT "Use Rounding? Y/N: "; ans$
IF UCASE$(ans$) = "Y" THEN
    round_total% = -1
    LINE INPUT "Show if Rounded? Y/N: "; ans$
    IF UCASE$(ans$) = "Y" THEN show_rounding% = -1 ELSE show_rounding% = 0
ELSE
    round_total% = 0
END IF
LINE INPUT "Display in Scientific Notation? Y/N: "; ans$
IF UCASE$(ans$) = "Y" THEN
    snconvert% = -1
ELSE
    snconvert% = 0
    LINE INPUT "Display in Dollars and Cents? Y/N: "; ans$
    IF UCASE$(ans$) = "Y" THEN currency_display% = -1 ELSE currency_display% = 0
    LINE INPUT "Display Results with Commas? Y/N: "; ans$
END IF
IF UCASE$(ans$) = "Y" THEN comma_display% = -1 ELSE comma_display% = 0
DO
    DO
        LINE INPUT "Number: "; stringmathb$
        IF UCASE$(stringmathb$) = "C" THEN RUN
        origb$ = stringmathb$
        CALL stringmath(stringmatha$, operator$, stringmathb$, runningtotal$, snconvert%, round_total%, show_rounding%, comma_display%, currency_display%, limit&&)
        IF stringmathb$ <> "invalid number" AND stringmathb$ <> "overflow" THEN
            EXIT DO
        ELSE
            PRINT stringmathb$
        END IF
    LOOP
    IF operator$ <> "" THEN
        DO UNTIL INSTR(origa$, ",") = 0
            origa$ = MID$(origa$, 1, INSTR(origa$, ",") - 1) + MID$(origa$, INSTR(origa$, ",") + 1)
        LOOP
        DO UNTIL INSTR(origb$, ",") = 0
            origb$ = MID$(origb$, 1, INSTR(origb$, ",") - 1) + MID$(origb$, INSTR(origb$, ",") + 1)
        LOOP
        IF INSTR(origb$, "$") THEN origb$ = MID$(origb$, 1, INSTR(origb$, "$") - 1) + MID$(origb$, INSTR(origb$, "$") + 1)
        SELECT CASE orig_operator$
            CASE "+"
                runningtotal# = VAL(origa$) + VAL(origb$)
            CASE "-"
                runningtotal# = VAL(origa$) - VAL(origb$)
            CASE "*"
                runningtotal# = VAL(origa$) * VAL(origb$)
            CASE "/"
                runningtotal# = VAL(origa$) / VAL(origb$)
            CASE "C", "c"
                RUN
        END SELECT
        origa$ = LTRIM$(STR$(runningtotal#))
        COLOR 8, 0: PRINT "Numeric Total: "; origa$: COLOR 7, 0
        PRINT "String Total:  "; runningtotal$
    ELSE
        origa$ = runningtotal$: IF INSTR(origa$, "$") THEN origa$ = MID$(origa$, 1, INSTR(origa$, "$") - 1) + MID$(origa$, INSTR(origa$, "$") + 1)
    END IF
    COLOR 2, 0: PRINT "Operator: +-/*: ";: COLOR 7, 0
    DO
        operator$ = INKEY$
        IF LEN(operator$) THEN
            IF operator$ = CHR$(27) THEN SYSTEM
            IF INSTR("-+/*=8cC", operator$) THEN EXIT DO
        END IF
    LOOP
    IF UCASE$(operator$) = "C" THEN RUN
    IF operator$ = "=" THEN operator$ = "+"
    IF operator$ = "8" THEN operator$ = "*"
    orig_operator$ = operator$
    PRINT operator$
LOOP

SUB stringmath (stringmatha$, operator$, stringmathb$, runningtotal$, snconvert%, round_total%, show_rounding%, comma_display%, currency_display%, limit&&)
stringmathround$ = ""
IF limit&& > 2147483640 THEN limit&& = 2147483640
IF limit&& = 0 THEN limit&& = 70 ' Default.

IF RIGHT$(UCASE$(runningtotal$), 1) = "R" THEN runningtotal$ = MID$(runningtotal$, 1, LEN(runningtotal$) - 1) 'Strip off rounding designation.
' Check running total. If S.N. convert to numeric for operations.
IF INSTR(runningtotal$, ",") <> 0 OR INSTR(runningtotal$, "e") <> 0 THEN
    holdstringmathb$ = stringmathb$
    stringmathb$ = runningtotal$
    IF INSTR(runningtotal$, ",") <> 0 THEN GOSUB comma_removal ELSE GOSUB scientific_to_numeric
    runningtotal$ = stringmathb$: stringmathb$ = holdstringmathb$: holdstringmathb$ = ""
END IF
' Check input number. If S.N. convert to numeric for operations.
IF INSTR(UCASE$(stringmathb$), "D") <> 0 OR INSTR(UCASE$(stringmathb$), "E") <> 0 THEN
    GOSUB validate_string_number
    IF stringmathb$ = "invalid number" THEN EXIT SUB
    GOSUB scientific_to_numeric
END IF

IF runningtotal$ = "" THEN
    GOSUB validate_string_number
    IF stringmathb$ = "invalid number" THEN EXIT SUB

    IF LEFT$(stringmathb$, 1) = "-" THEN
        stringmathb$ = MID$(stringmathb$, 2)
        n2sign$ = "-"
    ELSE
        n2sign$ = ""
    END IF
    GOSUB limit_round_convert
    IF stringmathb$ = "overflow" THEN
        n2sign$ = "": PRINT "Validated: "; stringmathb$: EXIT SUB
    END IF
    runningtotal$ = n2sign$ + stringmathb$: n2sign$ = ""
    IF stringmathround$ <> "" THEN runningtotal$ = runningtotal$ + stringmathround$
    PRINT "Validated: "; runningtotal$
    IF INSTR(LCASE$(stringmathb$), "e") <> 0 THEN BEEP: GOSUB scientific_to_numeric
ELSE
    GOSUB validate_string_number
    PRINT "Validated: "; stringmathb$
    IF stringmathb$ = "invalid number" THEN EXIT SUB
    IF INSTR(UCASE$(stringmathb$), "e") <> 0 THEN GOSUB scientific_to_numeric
END IF
IF runningtotal$ <> "" THEN stringmatha$ = runningtotal$

SELECT CASE operator$
    CASE "+", "-"
        string_add_subtract:
        IF INSTR(stringmatha$, ".") <> 0 THEN ' Evaluate sum for decimal fraction.
            sumplace& = LEN(stringmatha$) - INSTR(stringmatha$, ".")
            stringmatha$ = MID$(stringmatha$, 1, INSTR(stringmatha$, ".") - 1) + MID$(stringmatha$, INSTR(stringmatha$, ".") + 1) ' Strip out decimal
        END IF
        IF INSTR(stringmathb$, ".") <> 0 THEN ' Evaluate number for decimal fraction.
            numplace& = LEN(stringmathb$) - INSTR(stringmathb$, ".")
            stringmathb$ = MID$(stringmathb$, 1, INSTR(stringmathb$, ".") - 1) + MID$(stringmathb$, INSTR(stringmathb$, ".") + 1) ' Strip out decimal
        END IF
        IF sumplace& > numplace& THEN addsubplace& = sumplace& ELSE addsubplace& = numplace&
        IF sumplace& > addsubplace& THEN
            stringmatha$ = stringmatha$ + STRING$(sumplace& - addsubplace&, "0")
        ELSEIF addsubplace& > sumplace& THEN
            stringmatha$ = stringmatha$ + STRING$(addsubplace& - sumplace&, "0")
        END IF
        IF numplace& > addsubplace& THEN
            stringmathb$ = stringmathb$ + STRING$(numplace& - addsubplace&, "0")
        ELSEIF addsubplace& > numplace& THEN
            stringmathb$ = stringmathb$ + STRING$(addsubplace& - numplace&, "0")
        END IF ' END Decimal evaluations.

        IF LEFT$(stringmatha$, 1) = "-" THEN sign_input$ = "-" ELSE sign_input$ = "+"
        IF LEFT$(stringmathb$, 1) = "-" THEN sign_total$ = "-" ELSE sign_total$ = "+"

        addsubsign% = 0
        SELECT CASE sign_input$ + operator$ + sign_total$
            CASE "+++", "+--"
                operator$ = "+"
                IF LEFT$(stringmathb$, 1) = "-" THEN stringmathb$ = MID$(stringmathb$, 2)
            CASE "++-", "+-+"
                operator$ = "-"
                IF LEFT$(stringmathb$, 1) = "-" THEN stringmathb$ = MID$(stringmathb$, 2)
                IF VAL(stringmathb$) > VAL(stringmatha$) THEN SWAP stringmatha$, stringmathb$: addsubsign% = -1
            CASE "---", "-++"
                operator$ = "-"
                IF LEFT$(stringmatha$, 1) = "-" THEN stringmatha$ = MID$(stringmatha$, 2)
                IF LEFT$(stringmathb$, 1) = "-" THEN stringmathb$ = MID$(stringmathb$, 2)
                IF VAL(stringmathb$) > VAL(stringmatha$) THEN SWAP stringmatha$, stringmathb$ ELSE addsubsign% = -1
            CASE "--+", "-+-"
                operator$ = "+"
                IF LEFT$(stringmatha$, 1) = "-" THEN stringmatha$ = MID$(stringmatha$, 2)
                IF LEFT$(stringmathb$, 1) = "-" THEN stringmathb$ = MID$(stringmathb$, 2)
                addsubsign% = -1
        END SELECT

        IF LEN(stringmatha$) > LEN(stringmathb$) THEN
            stringmathb$ = STRING$(LEN(stringmatha$) - LEN(stringmathb$), "0") + stringmathb$
        ELSEIF LEN(stringmatha$) < LEN(stringmathb$) THEN
            stringmatha$ = STRING$(LEN(stringmathb$) - LEN(stringmatha$), "0") + stringmatha$
        END IF
        addsubx1$ = ""

        SELECT CASE operator$
            CASE "+", "="
                FOR addsubii& = LEN(stringmatha$) TO 1 STEP -1
                    addsubx1% = VAL(MID$(stringmatha$, addsubii&, 1)) + VAL(MID$(stringmathb$, addsubii&, 1)) + addsubcarry%
                    IF addsubx1% > 9 THEN addsubx1% = addsubx1% - 10: addsubcarry% = 1 ELSE addsubcarry% = 0
                    addsubx1$ = LTRIM$(STR$(addsubx1%)) + addsubx1$
                NEXT
                IF addsubcarry% THEN addsubx1$ = "1" + addsubx1$: addsubcarry% = 0
                GOSUB replace_decimal
            CASE "-"
                FOR addsubii& = LEN(stringmatha$) TO 1 STEP -1
                    addsubx1% = VAL(MID$(stringmatha$, addsubii&, 1)) - VAL(MID$(stringmathb$, addsubii&, 1)) + addsubcarry%
                    IF addsubx1% < 0 THEN addsubx1% = addsubx1% + 10: addsubcarry% = -1 ELSE addsubcarry% = 0
                    addsubx1$ = LTRIM$(STR$(addsubx1%)) + addsubx1$
                NEXT
                IF addsubx1$ <> "" AND addsubx1$ <> STRING$(LEN(addsubx1$), "0") THEN GOSUB replace_decimal
                DO UNTIL LEFT$(addsubx1$, 1) <> "0" ' Remove leading zeros.
                    addsubx1$ = MID$(addsubx1$, 2)
                LOOP
                IF addsubx1$ = "" THEN
                    addsubx1$ = "0": addsubsign% = 0
                ELSE
                    IF addsubcarry% THEN addsubx1$ = "-" + addsubx1$: addsubcarry% = 0
                END IF
        END SELECT

        IF addsubsign% THEN
            IF LEFT$(addsubx1$, 1) = "-" THEN addsubx1$ = MID$(addsubx1$, 2) ELSE addsubx1$ = "-" + addsubx1$
        END IF
        stringmatha$ = addsubx1$: addsubx1$ = ""
        IF operationdivision% THEN RETURN
        stringmathb$ = stringmatha$: stringmatha$ = ""
        IF LEFT$(stringmathb$, 1) = "-" THEN
            stringmathb$ = MID$(stringmathb$, 2)
            n2sign$ = "-"
        ELSE
            n2sign$ = ""
        END IF
        GOSUB limit_round_convert
        IF stringmathb$ = "overflow" THEN n2sign$ = "": EXIT SUB
        GOSUB sm_converter
        runningtotal$ = n2sign$ + stringmathb$: n2sign$ = ""

    CASE "*"
        string_multiply:
        fac1$ = stringmatha$: fac2$ = stringmathb$ ' Make numbers whole numbers and remove any - sign.
        IF LEFT$(fac1$, 1) = "-" THEN fac1$ = MID$(fac1$, 2): m_sign% = -1
        IF LEFT$(fac2$, 1) = "-" THEN fac2$ = MID$(fac2$, 2): IF m_sign% THEN m_sign% = 0 ELSE m_sign% = -1
        IF INSTR(fac1$, ".") <> 0 THEN m_decimal_places& = LEN(fac1$) - INSTR(fac1$, "."): fac1$ = MID$(fac1$, 1, INSTR(fac1$, ".") - 1) + MID$(fac1$, INSTR(fac1$, ".") + 1)
        IF INSTR(fac2$, ".") <> 0 THEN m_decimal_places& = m_decimal_places& + LEN(fac2$) - INSTR(fac2$, "."): fac2$ = MID$(fac2$, 1, INSTR(fac2$, ".") - 1) + MID$(fac2$, INSTR(fac2$, ".") + 1)
        FOR m_i& = LEN(fac2$) TO 1 STEP -1 ' Multiply each charter top and bottom.
            m_k& = m_l&
            m_x2$ = MID$(fac2$, m_i&, 1)
            FOR m_j& = LEN(fac1$) TO 1 STEP -1
                m_x1$ = MID$(fac1$, m_j&, 1)
                IF m_product$ <> "" THEN
                    m_add$ = LTRIM$(STR$(VAL(m_x1$) * VAL(m_x2$))) + STRING$(m_k&, "0")
                    m_t& = 0: m_xproduct$ = "": m_carry% = 0
                    DO ' Add multiplied characters together.
                        m_x3$ = MID$(m_add$, LEN(m_add$) - m_t&, 1)
                        m_x4$ = MID$(m_product$, LEN(m_product$) - m_t&, 1)
                        IF m_x3$ = "" AND m_x4$ = "" THEN
                            IF m_carry% THEN m_xproduct$ = "1" + m_xproduct$
                            EXIT DO
                        END IF
                        m_g% = VAL(m_x3$) + VAL(m_x4$) + m_carry%
                        IF m_g% >= 10 THEN m_g% = m_g% - 10: m_carry% = 1 ELSE m_carry% = 0
                        m_xproduct$ = LTRIM$(STR$(m_g%)) + m_xproduct$
                        m_t& = m_t& + 1
                    LOOP
                    m_product$ = m_xproduct$: m_xproduct$ = ""
                ELSE
                    m_product$ = LTRIM$(STR$(VAL(m_x1$) * VAL(m_x2$))) + STRING$(m_k&, "0") ' First loop makes variable here.
                END IF
                m_k& = m_k& + 1 ' Adds trailing zeros multiplication
            NEXT
            m_l& = m_l& + 1 ' Used to reset value for m_k& adding one trailing zer for each loop.
        NEXT
        fac1$ = "": fac2$ = "": m_l& = 0: m_k& = 0: m_t& = 0
        IF m_decimal_places& > LEN(m_product$) THEN m_product$ = STRING$(m_decimal_places& - LEN(m_product$), "0") + m_product$ ' Add any leading zeros to a decimal. Ex: .02 * .01 is factored as 002. It needs one leading zero before adding the decimal point, .0002.
        IF m_decimal_places& AND m_product$ <> "0" THEN ' Replace any decimal point.
            m_product$ = MID$(m_product$, 1, LEN(m_product$) - m_decimal_places&) + "." + MID$(m_product$, LEN(m_product$) - m_decimal_places& + 1)
        END IF
        DO UNTIL LEFT$(m_product$, 1) <> "0" ' Remove leading zeros.
            m_product$ = MID$(m_product$, 2)
        LOOP
        IF m_decimal_places& THEN
            DO UNTIL RIGHT$(m_product$, 1) <> "0" ' Remove trailing zeros in a decimal sum.
                m_product$ = MID$(m_product$, 1, LEN(m_product$) - 1)
            LOOP
        END IF
        IF m_product$ = "" THEN m_product$ = "0": m_sign% = 0
        IF RIGHT$(m_product$, 1) = "." THEN m_product$ = MID$(m_product$, 1, LEN(m_product$) - 1) ' Remove decimal from the end of an integer total.
        IF operationdivision% THEN m_sign% = 0: RETURN
        stringmathb$ = m_product$: m_product$ = "": GOSUB limit_round_convert
        IF stringmathb$ = "overflow" THEN EXIT SUB
        GOSUB sm_converter
        runningtotal$ = stringmathb$: stringmathb$ = ""
        IF m_sign% THEN runningtotal$ = "-" + runningtotal$: m_sign% = 0

    CASE "/"
        operationdivision% = -1
        divbuffer& = LEN(stringmathb$) - LEN(stringmatha$)
        IF divbuffer& < 0 THEN divbuffer& = 0
        d2dividend$ = stringmatha$
        d1divisor$ = stringmathb$
        IF LEFT$(d1divisor$, 1) = "0" AND LEN(d1divisor$) = 1 THEN PRINT "Division by zero not allowed.": END
        IF LEFT$(d1divisor$, 1) = "-" THEN divsign% = -1: d1divisor$ = MID$(d1divisor$, 2)
        IF LEFT$(d2dividend$, 1) = "-" THEN
            IF divsign% THEN
                divsign% = 0
            ELSE
                divsign% = -1
            END IF
            d2dividend$ = MID$(d2dividend$, 2)
        END IF
        IF INSTR(d1divisor$, ".") <> 0 THEN
            DO UNTIL RIGHT$(d1divisor$, 1) <> "0"
                d1divisor$ = MID$(d1divisor$, 1, LEN(d1divisor$) - 1) ' Strip off trailing zeros
            LOOP
            divplace& = LEN(d1divisor$) - INSTR(d1divisor$, ".")
            d1divisor$ = MID$(d1divisor$, 1, INSTR(d1divisor$, ".") - 1) + MID$(d1divisor$, INSTR(d1divisor$, ".") + 1) ' Strip off decimal point.
            DO UNTIL LEFT$(d1divisor$, 1) <> "0"
                d1divisor$ = MID$(d1divisor$, 2) ' Strip off leading zeros for divisors smaller than .1
            LOOP
        END IF

        IF INSTR(d2dividend$, ".") <> 0 THEN
            d2dividend$ = d2dividend$ + STRING$(divplace& - LEN(d2dividend$) - INSTR(d2dividend$, "."), "0") ' Add any zeros based on the length of dividend at decimal - length of divisor at decimal. If less than zero, nothing added.
            divplace2& = INSTR(d2dividend$, ".")
            DO UNTIL RIGHT$(d2dividend$, 1) <> "0"
                d2dividend$ = MID$(d2dividend$, 1, LEN(d2dividend$) - 1) ' Strip off trailing zeros
            LOOP
            d2dividend$ = MID$(d2dividend$, 1, INSTR(d2dividend$, ".") - 1) + MID$(d2dividend$, INSTR(d2dividend$, ".") + 1) ' Strip off decimal point.
        ELSE
            d2dividend$ = d2dividend$ + STRING$(divplace&, "0") ' Add any zeros based on the length of dividend at decimal - length of divisor at decimal. If less than zero, nothing added.
            divplace& = 0
        END IF
        DO
            DO
                divremainder& = divremainder& + 1: divremainder$ = divremainder$ + MID$(d2dividend$, divremainder&, 1)
                IF MID$(d2dividend$, divremainder&, 1) = "" THEN
                    IF divremainder$ = STRING$(LEN(divremainder$), "0") AND LEN(quotient$) > LEN(d2dividend$) THEN divflag% = -1: EXIT DO
                    divcarry& = divcarry& + 1
                    IF divcarry& = 1 THEN divplace3& = divremainder& - 1
                    IF divcarry& > limit&& + 1 + divbuffer& THEN
                        divflag% = -2: EXIT DO
                    END IF
                    divremainder$ = divremainder$ + "0" ' No more digits to bring down.
                END IF
                IF LEN(divremainder$) > LEN(d1divisor$) OR LEN(divremainder$) = LEN(d1divisor$) AND divremainder$ >= d1divisor$ THEN EXIT DO
                quotient$ = quotient$ + "0"
            LOOP
            IF divflag% THEN divflag% = 0: EXIT DO
            FOR div_i% = 9 TO 1 STEP -1
                stringmatha$ = LTRIM$(STR$(div_i%)): stringmathb$ = d1divisor$
                m_product$ = "": GOSUB string_multiply
                tempcutd$ = divremainder$ ' divremainder$ can be 00 or other leading zero values.
                DO
                    IF LEN(tempcutd$) = 1 THEN EXIT DO
                    IF LEFT$(tempcutd$, 1) = "0" THEN
                        tempcutd$ = MID$(tempcutd$, 2)
                    ELSE
                        EXIT DO
                    END IF
                LOOP
                IF LEN(tempcutd$) > LEN(m_product$) OR LEN(tempcutd$) = LEN(m_product$) AND m_product$ <= tempcutd$ THEN EXIT FOR
            NEXT
            quotient$ = quotient$ + LTRIM$(STR$(div_i%))
            stringmatha$ = LTRIM$(STR$(div_i%)): stringmathb$ = d1divisor$
            m_product$ = "": GOSUB string_multiply
            operator$ = "-"
            stringmatha$ = divremainder$
            stringmathb$ = m_product$
            GOSUB string_add_subtract
            divremainder$ = stringmatha$
            operator$ = "/"
        LOOP
        IF divplace& = 0 AND divplace2& = 0 THEN divplace& = divplace3&
        IF divplace2& THEN divplace& = divplace& + divplace2& - 1
        IF quotient$ = "" THEN divplace& = 0 ' dividend is zero.
        IF divplace& OR divplace2& THEN
            quotient$ = MID$(quotient$, 1, divplace&) + "." + MID$(quotient$, divplace& + 1)
            DO UNTIL RIGHT$(quotient$, 1) <> "0"
                quotient$ = MID$(quotient$, 1, LEN(quotient$) - 1) ' Strip off trailing zeros
            LOOP
            IF RIGHT$(quotient$, 1) = "." THEN quotient$ = MID$(quotient$, 1, LEN(quotient$) - 1) ' Strip off abandoned decimal.
        END IF
        DO UNTIL LEFT$(quotient$, 1) <> "0"
            quotient$ = MID$(quotient$, 2) ' Strip off leading zeros
        LOOP
        IF quotient$ = "" THEN quotient$ = "0": divsign% = 0
        operationdivision% = 0
        stringmathb$ = quotient$: quotient$ = "": GOSUB limit_round_convert
        IF stringmathb$ = "overflow" THEN divsign% = 0: EXIT SUB
        GOSUB sm_converter
        runningtotal$ = stringmathb$: stringmathb$ = ""
        IF divsign% THEN runningtotal$ = "-" + runningtotal$
END SELECT
IF stringmathround$ <> "" THEN runningtotal$ = runningtotal$ + stringmathround$
EXIT SUB

validate_string_number:
vsn_negcnt& = 0: vsn_poscnt& = 0: vsn_depresent& = 0: decimalcnt& = 0: vsn_numberpresent& = 0: vsn_zerospresent& = 0
IF LEFT$(stringmathb$, 1) = "-" THEN stringmathb$ = MID$(stringmathb$, 2): sm_sign$ = "-" ELSE sm_sign$ = ""
IF LEFT$(stringmathb$, 1) = "+" THEN IF sm_sign$ <> "-" THEN stringmathb$ = MID$(stringmathb$, 2) ELSE stringmathb$ = "invalid number": RETURN
IF INSTR(UCASE$(stringmathb$), "D") OR INSTR(UCASE$(stringmathb$), "E") THEN ' Evaluate for Scientific Notation.
    FOR sm_i& = 1 TO LEN(stringmathb$)
        validatenum$ = MID$(UCASE$(stringmathb$), sm_i&, 1)
        SELECT CASE validatenum$
            CASE "+"
                IF vsn_depresent& THEN vsn_poscnt& = vsn_poscnt& + 1 ELSE stringmathb$ = "invalid number": RETURN
            CASE "-"
                IF vsn_depresent& THEN vsn_negcnt& = vsn_negcnt& + 1 ELSE stringmathb$ = "invalid number": RETURN
            CASE "0" TO "9"
                vsn_numberpresent& = -1
            CASE "D", "E"
                vsn_depresent& = vsn_depresent& + 1
                IF decimalcnt& = 0 AND sm_i& <> 2 OR vsn_depresent& > 1 OR vsn_numberpresent& = 0 OR vsn_negcnt& > 1 OR vsn_poscnt& > 1 OR vsn_negcnt& = 1 AND vsn_poscnt& >= 1 THEN vsn_numberpresent& = 0: EXIT FOR
                vsn_numberpresent& = 0
                MID$(stringmathb$, sm_i&, 1) = "e" ' Standardize
            CASE "."
                decimalcnt& = decimalcnt& + 1
                IF sm_i& <> 2 THEN vsn_numberpresent& = 0: EXIT FOR
            CASE ELSE
                vsn_numberpresent& = 0: EXIT FOR
        END SELECT
    NEXT
    IF decimalcnt& = 0 THEN stringmathb$ = MID$(stringmathb$, 1, 1) + "." + MID$(stringmathb$, 2) ' Standardize "."
    IF vsn_numberpresent& = 0 OR vsn_negcnt& = 1 AND vsn_poscnt& = 1 OR decimalcnt& > 1 OR INSTR(stringmathb$, ".") <> 2 THEN stringmathb$ = "invalid number": RETURN
    vsn_depresent& = INSTR(stringmathb$, "e")
    sm_x$ = MID$(stringmathb$, vsn_depresent& + 1, 1) ' Standardize exponent "+" these two lines.
    IF sm_x$ <> "+" AND sm_x$ <> "-" THEN stringmathb$ = MID$(stringmathb$, 1, vsn_depresent&) + "+" + MID$(stringmathb$, vsn_depresent& + 1)
    IF MID$(stringmathb$, vsn_depresent& + 2, 1) = "0" THEN
        IF MID$(stringmathb$, vsn_depresent& + 3, 1) <> "" THEN stringmathb$ = "invalid number": RETURN ' No leading zeros allowed in exponent notation.
    END IF
    jjed& = INSTR(stringmathb$, "e") ' Get position of notation.
    valexpside$ = MID$(stringmathb$, jjed&) ' These two lines break up into number and notation
    stringmathb$ = MID$(stringmathb$, 1, jjed& - 1) ' stringmathb$ is +- single digit whole number, decimal point and decimal number. valexpside$ is notation, sign and exponent.
    DO UNTIL RIGHT$(stringmathb$, 1) <> "0" ' Remove any trailing zeros for number. Example 1.0d3 or 1.0000d3, etc.
        stringmathb$ = MID$(stringmathb$, 1, LEN(stringmathb$) - 1)
    LOOP
    IF VAL(MID$(stringmathb$, 1, INSTR(stringmathb$, ".") - 1)) = 0 THEN
        IF RIGHT$(stringmathb$, 1) = "." THEN
            stringmathb$ = "0.e+0" ' Handles all types of zero entries.
        ELSE
            stringmathb$ = "invalid number": RETURN
        END IF
        RETURN
    END IF
    stringmathb$ = sm_sign$ + stringmathb$ + valexpside$
    RETURN
ELSE
    FOR sm_i& = 1 TO LEN(stringmathb$)
        validatenum$ = MID$(stringmathb$, sm_i&, 1)
        SELECT CASE validatenum$
            CASE "."
                decimalcnt& = decimalcnt& + 1
            CASE "0"
                vsn_zerospresent& = -1
            CASE "1" TO "9"
                vsn_numberpresent& = -1
            CASE "$"
            CASE ELSE
                stringmathb$ = "invalid number": RETURN
        END SELECT
    NEXT
    IF decimalcnt& > 1 OR vsn_negcnt& > 1 OR vsn_poscnt& > 1 OR vsn_negcnt& >= 1 AND vsn_poscnt& >= 1 THEN
        stringmathb$ = "invalid number": RETURN
    END IF
    IF INSTR(stringmathb$, "$") THEN GOSUB currency_validate
    IF INSTR(stringmathb$, ",") THEN
        GOSUB comma_validation
        IF stringmathb$ = "invalid number" THEN RETURN
        GOSUB comma_removal
    END IF
    IF RIGHT$(stringmathb$, 1) = "." THEN stringmathb$ = MID$(stringmathb$, 1, LEN(stringmathb$) - 1)
    DO UNTIL LEFT$(stringmathb$, 1) <> "0" ' Strip off any leading zeros.
        stringmathb$ = MID$(stringmathb$, 2)
    LOOP
    stringmathb$ = sm_sign$ + stringmathb$
    IF INSTR(stringmathb$, ".") THEN
        DO UNTIL RIGHT$(stringmathb$, 1) <> "0" ' Strip off any trailing zeros in a decimal.
            stringmathb$ = MID$(stringmathb$, 1, LEN(stringmathb$) - 1)
        LOOP
    END IF
    IF RIGHT$(stringmathb$, 1) = "." THEN stringmathb$ = MID$(stringmathb$, 1, LEN(stringmathb$) - 1)
    IF vsn_numberpresent& = 0 THEN
        IF vsn_zerospresent& THEN
            stringmathb$ = "0"
        ELSE
            stringmathb$ = "invalid number"
        END IF
    END IF
END IF
RETURN

' Convert to commas, currency, S.N., etc.
sm_converter:
IF comma_display% THEN GOSUB comma_placement
IF currency_display% THEN GOSUB currency_convert
IF snconvert% THEN GOSUB numeric_to_scientific
RETURN

' Add in commas.
comma_placement:
GOSUB comma_prep
sm_i& = 0: sm_j& = 0: sm_seed& = 0
sm_seed& = LEN(temp_stringmathb1$) MOD 3: IF sm_seed& = 0 THEN sm_seed& = 3
sm_m1& = LEN(temp_stringmathb1$)
sm_m2& = (LEN(temp_stringmathb1$) - 1) \ 3
sm_replace$ = SPACE$(sm_m1& + sm_m2&)
DO WHILE sm_i& < sm_m1&
    MID$(sm_replace$, sm_j& + 1, sm_seed& + 1) = MID$(temp_stringmathb1$, sm_i& + 1, sm_seed&) + ","
    sm_i& = sm_i& + sm_seed&: sm_j& = sm_j& + sm_seed& + 1: sm_seed& = 3
LOOP
sm_replace$ = RTRIM$(sm_replace$)
IF RIGHT$(sm_replace$, 1) = "," THEN
    stringmathb$ = MID$(sm_replace$, 1, LEN(sm_replace$) - 1)
ELSE
    stringmathb$ = sm_replace$
END IF
sm_replace$ = "": temp_stringmathb1$ = ""
RETURN

' Validate comma entry.
comma_validation:
GOSUB comma_prep
IF INSTR(temp_stringmathb2$, ",") <> 0 OR temp_stringmathb1$ = STRING$(LEN(temp_stringmathb1$), ",") THEN
    stringmathb$ = "invalid number" ' Decimal part has comma or entry is all commas.
ELSE
    FOR sm_i& = LEN(temp_stringmathb1$) TO 1 STEP -1
        sm_j% = sm_j% + 1
        IF sm_j% = 4 THEN
            IF MID$(temp_stringmathb1$, sm_i&, 1) <> "," THEN stringmathb$ = "invalid number": EXIT FOR
            sm_j% = 0
        END IF
    NEXT
    IF stringmathb$ <> "invalid number" THEN
        stringmathb$ = sm_sign$ + temp_stringmathb1$ + temp_stringmathb2$
    END IF
END IF
temp_stringmathb1$ = "": temp_stringmathb2$ = "": sm_i& = 0: sm_j% = 0: sm_sign$ = "": sm_dollar$ = ""
RETURN

comma_removal:
sm_i& = 0: sm_j& = 0: sm_seed& = 0
sm_replace$ = SPACE$(LEN(stringmathb$))
DO
    sm_i& = INSTR(sm_seed& + 1, stringmathb$, ",")
    IF sm_i& = 0 THEN EXIT DO
    MID$(sm_replace$, sm_j& + 1, sm_i& - sm_seed& + 1) = MID$(stringmathb$, sm_seed& + 1, sm_i& - sm_seed& - 1)
    sm_j& = sm_j& + sm_i& - sm_seed& - 1
    sm_seed& = sm_i&
LOOP
stringmathb$ = RTRIM$(sm_replace$) + MID$(stringmathb$, sm_seed& + 1): sm_replace$ = ""
RETURN

comma_prep:
IF LEFT$(stringmathb$, 1) = "-" THEN stringmathb$ = MID$(stringmathb$, 2): sm_sign$ = "-"
temp_stringmathb1$ = stringmathb$: stringmathb$ = ""
IF INSTR(temp_stringmathb1$, ".") THEN
    temp_stringmathb2$ = MID$(temp_stringmathb1$, INSTR(temp_stringmathb1$, ".")) ' Decimal part
    temp_stringmathb1$ = MID$(temp_stringmathb1$, 1, INSTR(temp_stringmathb1$, ".") - 1) ' Non-decimal part
END IF
IF LEFT$(temp_stringmathb1$, 1) = "$" THEN temp_stringmathb1$ = MID$(temp_stringmathb1$, 2): sm_dollar$ = "$"
RETURN

currency_validate:
IF LEFT$(stringmathb$, 2) = "$-" OR LEFT$(stringmathb$, 2) = "$+" THEN stringmathb$ = "invalid number": RETURN
IF LEFT$(stringmathb$, 1) = "$" THEN stringmathb$ = MID$(stringmathb$, 2)
IF INSTR(stringmathb$, "$") THEN stringmathb$ = "invalid number": RETURN
sm_dollar$ = "$"
RETURN

currency_convert:
IF INSTR(UCASE$(stringmathb$), "D") <> 0 OR INSTR(UCASE$(stringmathb$), "E") <> 0 THEN GOSUB scientific_to_numeric
IF INSTR(stringmathb$, ",") = 0 THEN GOSUB comma_placement
IF INSTR(stringmathb$, ".") = 0 THEN stringmathb$ = stringmathb$ + ".00"
IF RIGHT$(stringmathb$, 1) = "." THEN stringmathb$ = stringmathb$ + "00"
IF MID$(stringmathb$, LEN(stringmathb$) - 2, 1) <> "." THEN stringmathb$ = stringmathb$ + "0"
IF MID$(stringmathb$, LEN(stringmathb$) - 2, 1) <> "." THEN stringmathb$ = "invalid number": RETURN
IF LEFT$(stringmathb$, 1) = "-" THEN stringmathb$ = MID$(stringmathb$, 2)
stringmathb$ = sm_sign$ + "$" + stringmathb$
RETURN

numeric_to_scientific:
IF LEFT$(stringmathb$, 1) = "-" THEN stringmathb$ = MID$(stringmathb$, 2): n2sign$ = "-"
IF INSTR(stringmathb$, ".") = 0 THEN exponentvalue&& = LEN(stringmathb$) - 1 ELSE exponentvalue&& = INSTR(stringmathb$, ".") - 2 ' Exponent is one less than number of digits for whole number an two less than the placement of the decimal point for a fraction.
stringmathb$ = MID$(stringmathb$, 1, INSTR(stringmathb$, ".") - 1) + MID$(stringmathb$, INSTR(stringmathb$, ".") + 1)
IF LEFT$(stringmathb$, 1) = "0" AND LEN(stringmathb$) > 1 OR exponentvalue&& = -1 THEN
    DO UNTIL LEFT$(stringmathb$, 1) <> "0" ' Remove leading zeros to consider rounding.
        stringmathb$ = MID$(stringmathb$, 2)
        exponentvalue&& = exponentvalue&& - 1
    LOOP
    esign$ = "-"
ELSE
    esign$ = "+"
END IF
DO UNTIL RIGHT$(stringmathb$, 1) <> "0" ' Remove trailing zeros.
    stringmathb$ = MID$(stringmathb$, 1, LEN(stringmathb$) - 1)
LOOP
IF stringmathb$ = "" THEN stringmathb$ = "0": esign$ = "+": exponentvalue&& = 0
stringmathb$ = LEFT$(stringmathb$, 1) + "." + MID$(stringmathb$, 2)
IF stringmathb$ = "0." THEN n2sign$ = "": esign$ = "+"
stringmathb$ = stringmathb$ + "e" + esign$ + LTRIM$(STR$(ABS(exponentvalue&&))) ' S.N formed here.
IF stringmathb$ <> "overflow" THEN
    stringmathb$ = n2sign$ + stringmathb$
END IF
n2sign$ = "": esign$ = "": exponentvalue&& = 0
RETURN

scientific_to_numeric:
IF INSTR(UCASE$(stringmathb$), "D") THEN MID$(stringmathb$, INSTR(UCASE$(stringmathb$), "D"), 1) = "e"
IF MID$(stringmathb$, INSTR(stringmathb$, "e") + 2) = "0" THEN ' The numeric value is the number without the zero exponent.
    stringmathb$ = MID$(stringmathb$, 1, INSTR(stringmathb$, "e") - 1)
    IF RIGHT$(stringmathb$, 1) = "." THEN stringmathb$ = MID$(stringmathb$, 1, LEN(stringmathb$) - 1)
    RETURN
ELSE
    IF LEFT$(stringmathb$, 1) = "-" THEN stn_sign$ = "-": stringmathb$ = MID$(stringmathb$, 2)
    stringmathb$ = MID$(stringmathb$, 1, INSTR(stringmathb$, ".") - 1) + MID$(stringmathb$, INSTR(stringmathb$, ".") + 1) ' Remove decimal point.
    stn_i& = INSTR(stringmathb$, "e") - 1 ' Length of the numric part.
    IF MID$(stringmathb$, INSTR(stringmathb$, "e") + 1, 1) = "-" THEN
        stringmathb$ = "." + STRING$(VAL(MID$(stringmathb$, stn_i& + 3)) - 1, "0") + MID$(stringmathb$, 1, stn_i&) ' Decimal point followed by exponent value in zeros added in front of numeric part.
    ELSE
        IF stn_i& - 1 > VAL(MID$(stringmathb$, stn_i& + 3)) THEN stn_point$ = "." ' - 1 for decimal place. Ex 2.034d+2 is 2034 here where 3 places to the right . could be moved before . disappears. > so no trailing decimal results.
        stringmathb$ = MID$(MID$(stringmathb$, 1, stn_i&), 1, VAL(MID$(stringmathb$, stn_i& + 3)) + 1) + stn_point$ + MID$(MID$(stringmathb$, 1, stn_i&), VAL(MID$(stringmathb$, stn_i& + 3)) + 2, stn_i& - VAL(MID$(stringmathb$, stn_i& + 3)) - 1) + STRING$(VAL(MID$(stringmathb$, stn_i& + 2)) - (stn_i& - 1), "0")
    END IF
END IF
IF stringmathb$ = "0" THEN stn_sign$ = ""
stringmathb$ = stn_sign$ + stringmathb$
stn_sign$ = "": stn_point$ = ""
RETURN

limit_round_convert:
' Try SN if whole number is too large (as it may be trailing zeros) or decimal is beyond limit.
IF LEFT$(stringmathb$, 2) = ".0" AND LEN(stringmathb$) > limit&& + 1 OR INSTR(stringmathb$, ".") > limit&& + 1 OR INSTR(stringmathb$, ".") = 0 AND LEN(stringmathb$) > limit&& THEN
    IF limit&& > 1 THEN
        GOSUB numeric_to_scientific ' Retry as S.N.
        IF LEN(stringmathb$) > limit&& + 3 THEN ' Needs rounding.
            snotation$ = MID$(stringmathb$, INSTR(UCASE$(stringmathb$), "E"))
            exponentvalue&& = VAL(MID$(snotation$, 2)) ' Get positive or negative sign.
            snexponent$ = MID$(stringmathb$, INSTR(UCASE$(stringmathb$), "E") + 2)
            stringmathb$ = MID$(stringmathb$, 1, INSTR(UCASE$(stringmathb$), "E") - 1)
            '''IF LEN(stringmathb$) + LEN(snexponent$) > limit&& + 1 AND exponentvalue&& >= limit&& THEN BEEP
            IF exponentvalue&& >= limit&& THEN
                stringmathb$ = MID$(stringmathb$, 1, exponentvalue&& + 3)
            ELSE
                stringmathb$ = MID$(stringmathb$, 1, limit&& - LEN(snexponent$) + 2)
            END IF
            GOSUB string_rounding_method
            IF LEFT$(stringmathb$, 3) = "10." THEN
                stringmathb$ = "1." + MID$(stringmathb$, 4)
                ' Add one to the exponent.
                FOR round_i& = LEN(snexponent$) TO 1 STEP -1
                    round_x$ = CHR$(ASC(MID$(snexponent$, round_i&, 1)) + 1)
                    IF round_x$ <> CHR$(47) THEN ' Decimal point + 1. Ignore.
                        IF round_x$ = CHR$(58) THEN
                            MID$(snexponent$, round_i&, 1) = "0": carry$ = "1"
                        ELSE
                            MID$(snexponent$, round_i&, 1) = round_x$: carry$ = "": EXIT FOR
                        END IF
                    END IF
                NEXT
                snexponent$ = carry$ + snexponent$: carry$ = ""
            END IF
            stringmathb$ = stringmathb$ + MID$(snotation$, 1, 2) + snexponent$
            IF LEN(snexponent$) + LEN(MID$(stringmathb$, 1, INSTR(UCASE$(stringmathb$), "E") - 1)) > limit&& + 1 THEN
                stringmathb$ = "overflow"
            END IF
            exponentvalue&& = 0
        END IF
    ELSE
        IF INSTR(stringmathb$, ".") > 0 AND INSTR(stringmathb$, ".") <= limit&& THEN
            stringmathb$ = MID$(stringmathb$, 1, limit&& + 2)
            IF round_total% = -1 AND RIGHT$(stringmathb$, 1) > "4" THEN
                GOSUB string_rounding_method
            ELSE
                stringmathb$ = MID$(stringmathb$, 1, limit&& + 1)
                IF show_rounding% THEN stringmathround$ = "r"
            END IF
        ELSE
            stringmathb$ = "overflow"
        END IF
    END IF
    RETURN
END IF
IF LEN(stringmathb$) > limit&& AND INSTR(stringmathb$, ".") = 0 OR LEN(stringmathb$) > limit&& + 1 AND INSTR(stringmathb$, ".") <> 0 THEN
    IF INSTR(stringmathb$, ".") = 0 THEN
        stringmathb$ = MID$(stringmathb$, 1, limit&& + 1)
    ELSE
        stringmathb$ = MID$(stringmathb$, 1, limit&& + 2)
    END IF
    GOSUB string_rounding_method
    IF LEN(stringmathb$) > limit&& + lrc_decimalpoint& THEN ' Ex: limit&& = 4 9999.9 1.e+4
        GOSUB numeric_to_scientific
    ELSE
        IF LEN(stringmathb$) > limit&& + lrc_decimalpoint& THEN stringmathb$ = "overflow"
    END IF
END IF
RETURN

replace_decimal:
IF addsubplace& THEN
    addsubx1$ = STRING$(addsubplace& - LEN(addsubx1$), "0") + addsubx1$
    addsubx1$ = MID$(addsubx1$, 1, LEN(addsubx1$) - addsubplace&) + "." + MID$(addsubx1$, LEN(addsubx1$) - addsubplace& + 1)
    DO UNTIL RIGHT$(addsubx1$, 1) <> "0" ' Remove trailing zeros in a decimal sum.
        addsubx1$ = MID$(addsubx1$, 1, LEN(addsubx1$) - 1)
        addsubplace& = addsubplace& - 1
    LOOP
    IF RIGHT$(addsubx1$, 1) = "." THEN addsubx1$ = MID$(addsubx1$, 1, LEN(addsubx1$) - 1) ' Number is now an integer.
END IF
RETURN

string_rounding_method:
IF INSTR(stringmathb$, ".") THEN lrc_decimalpoint& = 1 ELSE lrc_decimalpoint& = 0
IF MID$(stringmathb$, LEN(stringmathb$), 1) > "4" THEN
    FOR round_i& = LEN(stringmathb$) - 1 TO 1 STEP -1
        round_x$ = CHR$(ASC(MID$(stringmathb$, round_i&, 1)) + 1)
        IF round_x$ <> CHR$(47) THEN ' Decimal point + 1. Ignore.
            IF round_x$ = CHR$(58) THEN
                MID$(stringmathb$, round_i&, 1) = "0": carry$ = "1"
            ELSE
                MID$(stringmathb$, round_i&, 1) = round_x$: carry$ = "": EXIT FOR
            END IF
        END IF
    NEXT
    stringmathb$ = carry$ + MID$(stringmathb$, 1, LEN(stringmathb$) - 1): carry$ = ""
    IF show_rounding% THEN stringmathround$ = "R"
ELSE
    stringmathb$ = MID$(stringmathb$, 1, LEN(stringmathb$) - 1)
    IF show_rounding% THEN stringmathround$ = "r"
END IF

IF lrc_decimalpoint& THEN
    DO UNTIL RIGHT$(stringmathb$, 1) <> "0"
        stringmathb$ = MID$(stringmathb$, 1, LEN(stringmathb$) - 1)
    LOOP
    IF stringmathb$ = "" OR stringmathb$ = "." THEN stringmathb$ = "0": lrc_decimalpoint& = 0
    IF RIGHT$(stringmathb$, 1) = "." AND exponentvalue&& = 0 THEN
        stringmathb$ = MID$(stringmathb$, 1, LEN(stringmathb$) - 1): lrc_decimalpoint& = 0
    END IF
END IF
RETURN
END SUB
So the prompts...

Limit Display - Input how many digits you want displayed to the screen.
Use Rounding Y/N - Sef explanitory.
Display in Scientitic Notation Y/N - Sef explanitory.
Display in Dollars and Cents Y/N - Sef explanitory.
Display Results with Commas Y/N- Sef explanitory.

Number - Input the first number. It will give a validation message after input.
Operator +-/* - Input the symbol to add, subtract, divide, or multiply.
Number - Input the nextnumber to complete the operation. It ouputs both the numeric and string answer.


Pete
Reply


Messages In This Thread
Treebeard's String-Math - by Jack - 07-27-2022, 11:52 PM
RE: Treebeard's String-Math - by Pete - 07-28-2022, 02:00 AM
RE: Treebeard's String-Math - by Jack - 07-28-2022, 02:12 AM
RE: Treebeard's String-Math - by Pete - 07-28-2022, 06:17 AM
RE: Treebeard's String-Math - by Jack - 07-28-2022, 10:43 AM
RE: Treebeard's String-Math - by James D Jarvis - 07-28-2022, 03:09 PM
RE: Treebeard's String-Math - by Pete - 07-28-2022, 04:02 PM
RE: Treebeard's String-Math - by James D Jarvis - 07-28-2022, 05:23 PM
RE: Treebeard's String-Math - by Pete - 07-28-2022, 04:55 PM
RE: Treebeard's String-Math - by Kernelpanic - 07-29-2022, 07:06 PM
RE: Treebeard's String-Math - by Jack - 07-29-2022, 08:45 PM
RE: Treebeard's String-Math - by Kernelpanic - 07-29-2022, 09:13 PM
RE: Treebeard's String-Math - by Jack - 07-29-2022, 09:36 PM
RE: Treebeard's String-Math - by Jack - 07-29-2022, 10:33 PM
RE: Treebeard's String-Math - by Pete - 07-29-2022, 11:18 PM
RE: Treebeard's String-Math - by Jack - 07-30-2022, 12:13 AM
RE: Treebeard's String-Math - by Pete - 07-30-2022, 12:30 AM
RE: Treebeard's String-Math - by Jack - 07-30-2022, 12:37 AM
RE: Treebeard's String-Math - by Pete - 07-30-2022, 12:56 AM
RE: Treebeard's String-Math - by Jack - 07-30-2022, 01:05 AM
RE: Treebeard's String-Math - by Pete - 07-30-2022, 02:44 AM
RE: Treebeard's String-Math - by Jack - 07-31-2022, 12:34 PM
RE: Treebeard's String-Math - by bplus - 07-31-2022, 03:03 PM
RE: Treebeard's String-Math - by Jack - 07-31-2022, 04:41 PM
RE: Treebeard's String-Math - by Jack - 08-08-2022, 06:12 PM
RE: Treebeard's String-Math - by bplus - 08-08-2022, 07:23 PM
RE: Treebeard's String-Math - by Jack - 08-08-2022, 09:04 PM



Users browsing this thread: 4 Guest(s)