Perpetual string math calculator.
#1
I've seen online calculators that do this:

1 / 3 * 3 = .999...

and this:

1 / 3 * 3 = 1

Obviously, the second one takes into account 1 / 3  as an infinite repetend. So I was wondering if there was some algorithm I could apply to properly round all repetend situations. Well, I punted on that concept, as to identify a repetend that goes over 10,000 digits before repeating the number sequence, simply takes too much calculation time. Not to mention how far can we go? After all, Pi is a transcendental number, so that could take an eternity... or an eternity and a half, if you're using FreeBASIC.

So, I decided to approach this problem by going old-school. Although decimals can never be depended on to divide and multiply back symmetrically, fractions can. So I designed a string math calculator system that works with numerators and denominators, but displays the results in decimal form.

This is roughed out, which means I did not put much thought into variable and line number names, tricks to speed it up, or extensive optimization. Goal #1 for me is to always get it working, and I think this is either close or does meet my first goal...

Code: (Select All)
DIM SHARED betatest%
REM betatest% = -1
WIDTH 160, 42
_SCREENMOVE 0, 0
DIM SHARED operator$, stringmatha$, stringmathb$, runningtotal$, limit&&
start:
display_as&& = 15
limit&& = 100
DO
    IF sa$ = "" OR LEN(op$) THEN
        LINE INPUT "Number: "; n$
        IF op$ = "" THEN
            sa$ = n$
        ELSE
            sb$ = n$
            GOSUB calculate
            op$ = ""
        END IF
    ELSE
        ' Input operation.
        PRINT "[+-*/]: ";
        DO
            _LIMIT 30
            mykey$ = INKEY$
            IF LEN(mykey$) THEN
                SELECT CASE mykey$
                    CASE "+", "="
                        op$ = "+": EXIT DO
                    CASE "-", "_"
                        op$ = "-": EXIT DO
                    CASE "*", "8"
                        op$ = "*": EXIT DO
                    CASE "/", "?"
                        op$ = "/": EXIT DO
                    CASE "c", "C"
                        PRINT: PRINT "Total = 0": CLEAR: GOTO start
                    CASE CHR$(27)
                        SYSTEM
                END SELECT
            END IF
        LOOP
        PRINT op$
    END IF
LOOP

calculate:
SELECT CASE op$
    CASE "+", "-"
        IF nator_a$ = "" THEN
            nator_a$ = sa$: nator_b$ = sb$
            dator_a$ = "1": dator_b$ = "1"
        ELSE
            nator_b$ = sb$: dator_b$ = "1"
        END IF

        IF INSTR(nator_a$, ".") THEN
            n$ = nator_a$
            GOSUB convert_to_fraction
            nator_a$ = numerator$: dator_a$ = denominator$
        END IF

        IF INSTR(sb$, ".") THEN
            n$ = sb$
            GOSUB convert_to_fraction
            nator_b$ = numerator$: dator_b$ = denominator$
        ELSE
            nator_b$ = sb$: dator_b$ = "1"
        END IF

        ' Cross multiply
        IF dator_a$ <> datorb$ THEN
            stringmatha$ = nator_a$: stringmathb$ = dator_b$: operator$ = "*"
            CALL string_math
            a$ = runningtotal$
            stringmatha$ = dator_a$: stringmathb$ = dator_b$: operator$ = "*"
            CALL string_math
            dator_c$ = runningtotal$ ' Common denominator.

            stringmatha$ = nator_b$: stringmathb$ = dator_a$: operator$ = "*"
            CALL string_math
            b$ = runningtotal$

            stringmatha$ = a$: stringmathb$ = b$: operator$ = op$
            CALL string_math
            nator_c$ = runningtotal$
        END IF

    CASE "*"
        IF nator_a$ = "" THEN
            nator_a$ = sa$: nator_b$ = sb$
            dator_a$ = "1": dator_b$ = "1"
        ELSE
            nator_b$ = sb$: dator_b$ = "1"
        END IF

        IF INSTR(nator_a$, ".") THEN
            n$ = nator_a$
            GOSUB convert_to_fraction
            nator_a$ = numerator$: dator_a$ = denominator$
        END IF

        IF INSTR(sb$, ".") THEN
            n$ = sb$
            GOSUB convert_to_fraction
            nator_b$ = numerator$: dator_b$ = denominator$
        ELSE
            nator_b$ = sb$: dator_b$ = "1"
        END IF

        stringmatha$ = nator_a$: stringmathb$ = nator_b$: operator$ = "*"
        CALL string_math
        nator_c$ = runningtotal$
        stringmatha$ = dator_a$: stringmathb$ = dator_b$: operator$ = "*"
        CALL string_math
        dator_c$ = runningtotal$

    CASE "/"
        IF nator_a$ = "" THEN
            nator_a$ = sa$: nator_b$ = sb$
            dator_a$ = "1": dator_b$ = "1"
        ELSE
            nator_b$ = sb$: dator_b$ = "1"
        END IF

        IF INSTR(nator_a$, ".") THEN
            n$ = nator_a$
            GOSUB convert_to_fraction
            nator_a$ = numerator$: dator_a$ = denominator$
        END IF

        IF INSTR(sb$, ".") THEN
            n$ = sb$
            GOSUB convert_to_fraction
            nator_b$ = numerator$: dator_b$ = denominator$
        ELSE
            nator_b$ = sb$: dator_b$ = "1"
        END IF

        SWAP nator_b$, dator_b$

        stringmatha$ = nator_a$: stringmathb$ = nator_b$: operator$ = "*"
        CALL string_math
        nator_c$ = runningtotal$
        stringmatha$ = dator_a$: stringmathb$ = dator_b$: operator$ = "*"
        CALL string_math
        dator_c$ = runningtotal$

END SELECT

IF betatest% THEN
    PRINT "nator_a$: "; nator_a$
    PRINT "dator_a$: "; dator_a$
    PRINT "nator_b$: "; nator_b$
    PRINT "dator_b$: "; dator_b$
    PRINT "nator_c$: "; nator_c$
    PRINT "dator_c$: "; dator_c$
END IF

a$ = nator_c$: b$ = dator_c$: GOSUB greatest_common_factor
nator_c$ = numerator$: dator_c$ = denominator$

stringmatha$ = nator_c$: stringmathb$ = dator_c$: operator$ = "/"
CALL string_math
sa$ = runningtotal$

COLOR 15, 0: PRINT:
IF LEFT$(sa$, 1) = "-" THEN PRINT "Total: "; sa$ ELSE PRINT "Total:  "; sa$
COLOR 7, 0

nator_a$ = nator_c$
dator_a$ = dator_c$
IF betatest% THEN COLOR 2, 0: PRINT: PRINT "nator_a$ ="; nator_a$, "dator_a$ = "; dator_a$: PRINT: COLOR 7, 0
RETURN

'=================================================================================

convert_to_fraction:
i = 0: j = 0: k = 0: msg$ = ""

IF MID$(n$, 1, 1) = "-" THEN j = 3 ELSE j = 2 ' Look for negative sign.
x1$ = MID$(n$, 1, INSTR(n$, ".") - 1)
IF j = 3 THEN x1$ = MID$(x1$, 2)
x2$ = MID$(n$, INSTR(n$, ".") + 1)
b$ = "1" + STRING$(LEN(x2$), "0")
x1$ = x1$ + x2$
DO UNTIL LEFT$(x1$, 1) <> "0"
    x1$ = MID$(x1$, 2) ' Strip off any leading zeros
LOOP
IF j = 2 THEN a$ = x1$ ELSE a$ = "-" + x1$

z$ = ""

IF betatest% THEN PRINT "numerator and denomintor: "; a$, b$
numerator$ = a$: denominator$ = b$
RETURN

greatest_common_factor:
' GFC algorithm. -------------------------------------------------------------
gfca$ = a$: gfcb$ = b$
IF betatest% THEN PRINT "PRE GFC "; a$; " / "; b$
' Make both numbers positive.
IF MID$(gfca$, 1, 1) = "-" THEN gfca$ = MID$(gfca$, 2)
IF MID$(gfcb$, 1, 1) = "-" THEN gfcb$ = MID$(gfcb$, 2)
' STRING MATH < or > EVAL NOT NEEDED AS NEG NUMBERS ARE CONVERTED TO POS AND NO CHANCE OF 0 AND < 1 > 0 LIKE 0 AND .1 OCCURRING.
IF gfca$ < gfcb$ THEN SWAP gfca$, gfcb$

' MOD operation in string math.
DO
    stringmatha$ = gfca$: stringmathb$ = gfcb$
    operator$ = "/": CALL string_math
    m1$ = runningtotal$
    IF INSTR(m1$, ".") THEN m1$ = MID$(m1$, 1, INSTR(m1$, ".") - 1)
    stringmatha$ = m1$
    stringmathb$ = gfcb$
    operator$ = "*": CALL string_math
    m2$ = runningtotal$
    stringmatha$ = gfca$: stringmathb$ = m2$
    operator$ = "-": CALL string_math
    SWAP gfca$, gfcb$: gfcb$ = runningtotal$
    IF runningtotal$ = "0" THEN EXIT DO
LOOP

stringmatha$ = a$: stringmathb$ = gfca$
operator$ = "/": CALL string_math
numerator$ = runningtotal$
stringmatha$ = b$: stringmathb$ = gfca$
operator$ = "/": CALL string_math
denominator$ = runningtotal$
IF betatest% THEN COLOR 14, 0: PRINT "GFC "; numerator$; " / "; denominator$: COLOR 7, 0
RETURN

'===============================================================================

SUB string_math
    SELECT CASE operator$
        CASE "+", "-"
            GOTO string_add_subtract
        CASE "*"
            GOTO string_multiply
        CASE "/"
            GOTO string_divide
        CASE ELSE
            PRINT "Error, no operator selected. operator$ = "; operator$
    END SELECT

    string_divide:
    divsign% = 0 '''''''''''''''
    divremainder& = 0: divremainder$ = "": divplace& = 0 AND divplace2& = 0: quotient$ = "": divcarry& = 0
    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.": divsign% = 0: operationdivision% = 0: EXIT SUB: RETURN '*'
    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: operationdivision% = 0: EXIT SUB: RETURN '*'
    '''GOSUB sm_converter
    runningtotal$ = stringmathb$: stringmathb$ = ""
    IF divsign% THEN runningtotal$ = "-" + runningtotal$

    IF stringmathround$ <> "" THEN runningtotal$ = runningtotal$ + stringmathround$
    operationdivision% = 0 '''''
    EXIT SUB ''' or RETURN to select case if goto changed to gosub.

    string_multiply:
    m_decimal_places& = 0: m_product$ = "" ''''''''''''''''''''''
    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: RETURN '*'
    '''GOSUB sm_converter
    runningtotal$ = stringmathb$: stringmathb$ = ""
    IF m_sign% THEN runningtotal$ = "-" + runningtotal$: m_sign% = 0
    EXIT SUB ''' or RETURN to select case if goto changed to gosub.

    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: RETURN '*'
    ''' GOSUB sm_converter
    runningtotal$ = n2sign$ + stringmathb$: n2sign$ = ""
    EXIT SUB ''' or RETURN to select case if goto changed to gosub.

    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
END SUB


Pete
Reply
#2
Here is a little demo of some of the related string math elements for functions like rounding, calculating the greatest common fraction, converting form numeric to scientific notation and back, from decimal to fraction notation and back, and comparing string values.

Edit: Code updated in post #15, here...

https://staging.qb64phoenix.com/showthre...36#pid5236

Pete
Reply
#3
Next addition will be a routine to recognize and report repetends.
Reply
#4
A lot of work, but the rounding was correct. In QB64 it doesn't work with 34,545. The correct result is 34.55 not 34.54. With 34.546 it works correct.

[Image: Rundungsfehler-QB64.jpg]
Reply
#5
It represent it mathematically correctly in Visual Basic 5.0. Then that probably also applies to QuickBasic 4.5.

There is a bug in the QB64 math rounding routines.  Dodgy

Code: (Select All)
Option Explicit

Private Sub cmdCommand_Click()

  Dim Zahl1, Zahl2, Ergebnis As Double
 
  Ergebnis = Val(txtZahl1.Text + txtZahl2.Text)
  txtErgebnis.Text = Format$(Ergebnis, "###.##")

End Sub

Private Sub cmdEnde_Click()
  End
End Sub

Private Sub cmdLoeschen_Click()
  txtZahl1.Text = ""
  txtZahl2.Text = ""
  txtErgebnis.Text = ""
End Sub

[Image: Korrektes-Runden2022-08-11-221242.jpg]
Reply
#6
(08-11-2022, 12:49 PM)Kernelpanic Wrote: A lot of work, but the rounding was correct. In QB64 it doesn't work with 34,545. The correct result is 34.55 not 34.54. With 34.546 it works correct.

[Image: Rundungsfehler-QB64.jpg]

That's why I coded my string math routine. You simply cannot "count" on binary computer math. (Please enjoy the pun.)

Pete
Reply
#7
Quote:That's why I coded my string math routine. You simply cannot "count" on binary computer math. (Please enjoy the pun.)


I enjoy it Rolleyes , but QuickBasic 4.5 could it - sure. In any case, it would be for . . . 99%(?) of all cases ok.

34.545 must not become 34.54. I had to do financial math once and there would be something like this - probably a case for lawyers because it turns out to be a few thousand dollars more on a loan payment, a mortgage, for example.

PS: It depends on where the error occurs. In the worst case, it really runs into the thousands and more, depending on the amount of the loan. That is not a trifle.
Reply
#8
I did show you guys how to round to get expected results. All you have to do is say how much error you can tolerate (and stay within digit limits of the Type).
b = b + ...
Reply
#9
(08-11-2022, 08:49 PM)Kernelpanic Wrote:
Quote:That's why I coded my string math routine. You simply cannot "count" on binary computer math. (Please enjoy the pun.)


I enjoy it Rolleyes , but QuickBasic 4.5 could it - sure. In any case, it would be for . . . 99%(?) of all cases ok.

34.545 must not become 34.54. I had to do financial math once and there would be something like this - probably a case for lawyers because it turns out to be a few thousand dollars more on a loan payment, a mortgage, for example.

PS: It depends on where the error occurs. In the worst case, it really runs into the thousands and more, depending on the amount of the loan. That is not a trifle.

What you are referring to is called banker's rounding. Simply put, it is an algorithm that rounds .5 up or down depending on the number. so after many rounds there is a better chance the banker's rounded figures would add up to the actual total amount if all of the transactions were calculated at one time on a precision calculator.

Here is an online example of how the algorithm would apply:
  • 0.5 rounds down to 0. Why? Because 0 is the nearest even integer.
  • 1.5 rounds up to 2. Why? Because 2 is the nearest even integer. 
  • 73.5 rounds up to 74. Why? Because 74 is the nearest even integer. 
  • 74.5 rounds down to 74. Why? Because 74 is the nearest even integer. 
  • 75.5 rounds up to 76. Why? Because 76 is the nearest even integer. 
  • 76.5 rounds down to 76. Why? Because 76 is the nearest even integer. 

I may, at some point, code this algorithm for my string math routine but for now, I'm fixated on the rounding used to display the last digit in a calculator display.

Thanks,

Pete
Reply
#10
what I did in one of my decnumber versions was to see if the digit after the last displayed digit was 5 or greater, if so then add 1
the details are tricky especially if the number is in floating point format
Reply




Users browsing this thread: 5 Guest(s)