Perpetual string math calculator. - Pete - 08-10-2022
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
RE: Perpetual string math calculator. - Pete - 08-11-2022
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://qb64phoenix.com/forum/showthread.php?tid=756&pid=5236#pid5236
Pete
RE: Perpetual string math calculator. - Pete - 08-11-2022
Next addition will be a routine to recognize and report repetends.
RE: Perpetual string math calculator. - Kernelpanic - 08-11-2022
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.
RE: Perpetual string math calculator. - Kernelpanic - 08-11-2022
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.
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
RE: Perpetual string math calculator. - Pete - 08-11-2022
(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.
That's why I coded my string math routine. You simply cannot "count" on binary computer math. (Please enjoy the pun.)
Pete
RE: Perpetual string math calculator. - Kernelpanic - 08-11-2022
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 , 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.
RE: Perpetual string math calculator. - bplus - 08-11-2022
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).
RE: Perpetual string math calculator. - Pete - 08-11-2022
(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 , 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
RE: Perpetual string math calculator. - Jack - 08-11-2022
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
|