_Title "String Math 2021-06-14" ' b+ from SM2 (2021 June) a bunch of experiments to fix and improve speeds. ' June 2021 fix some old String Math procedures, better nInverse with new LT frunction, remove experimental procedures.
' Now with decent sqrRoot it works independent of Mr$() = Math Regulator that handles signs and decimals and calls to
' add$(), subtr$, mult$, divide$ (100 significant digits), add$(), subtr$, mult$ are exact!
' If you need higher precsion divide, I recommend use nInverse on denominator (integer)
' then add sign and decimal and mult$() that number with numerator to get divsion answer in higher precision than 100.
' (See how Mr$() handles division and just call nInverse$ with what precision you need.)
' The final function showDP$() is for displaying these number to a set amount of Decimal Places.
' The main code is sampler of tests performed with these functions.
'test new stuff
inv$
= nInverse$
(String$(j
, "9"), 1000) invinv$ = mr$("1", "/", inv$)
Print showDP$
(invinv$
, 2)
Function add$
(a$
, b$
) 'add 2 positive integers assume a and b are just numbers no - signs 'set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
Dim fa$
, fb$
, t$
, new$
, result$
'now taking 18 digits at a time Thanks Steve McNeill
sa
= Val(Mid$(fa$
, (m
- g
) * 18 + 1, 18)) sb
= Val(Mid$(fb$
, (m
- g
) * 18 + 1, 18)) result$ = new$ + result$
add$ = result$
' This is used in nInverse$ not by Mr$ because there it saves time!
fa$ = result$: fb$ = result$
Mid$(fa$
, lResult
- la
+ 1) = a$
Mid$(fb$
, lResult
- lb
+ 1) = b$
w = i - 1
w = w - 1
subtr1$ = result$
' 2021-06-08 fix up with new mr call that decides the sign and puts the greater number first
Function subtr$
(sum$
, minus$
) ' assume both numbers are positive all digits Dim ts$
, tm$
, sign$
, LG$
, sm$
, t$
, result$
ts$
= _Trim$(sum$
): tm$
= _Trim$(minus$
) ' fixed subtr$ 2021-06-05 tenE18 = 1000000000000000000 'yes!!! no dang E's
sign$ = ""
'now taking 18 digits at a time From Steve I learned we can do more than 1 digit at a time
VB
= Val(Mid$(LG$
, m
* 18 - g
* 18 + 1, 18)) vs
= Val(Mid$(sm$
, m
* 18 - g
* 18 + 1, 18)) p = (m - g) * 18
p = p - 1
result$ = t$ + result$
subtr$ = result$
Function TrimLead0$
(s$
) 'for treating strings as number (pos integers) copys$
= _Trim$(s$
) 'might as well remove spaces too i = 1: find = 0
i = i + 1: find = 1
If copys$
= "" Then TrimLead0$
= "0" Else TrimLead0$
= copys$
' catchy? mr$ for math regulator cop$ = " + - * / " 1 of 4 basic arithmetics
' Fixed so that add and subtract have signs calc'd in Mr and correct call to add or subtract made
' with bigger minus smaller in subtr$() call
Dim op$
, ca$
, cb$
, aSgn$
, bSgn$
, postOp$
, sgn$
op$
= _Trim$(cop$
) 'save fixing each time ca$
= _Trim$(a$
): cb$
= _Trim$(b$
) 'make copies in case we change 'strip signs and decimals
aSgn$
= "-": ca$
= Mid$(ca$
, 2) aSgn$ = ""
ca$
= Mid$(ca$
, 1, dp
- 1) + Mid$(ca$
, dp
+ 1) adp = 0
bSgn$
= "-": cb$
= Mid$(cb$
, 2) bSgn$ = ""
cb$
= Mid$(cb$
, 1, dp
- 1) + Mid$(cb$
, dp
+ 1) bdp = 0
If op$
= "+" Or op$
= "-" Or op$
= "/" Then 'add or subtr even up strings on right of decimal 'even up the right sides of decimals if any
dp = adp + bdp
'now according to signs and op$ call add$ or subtr$
If op$
= "-" Then ' make it adding according to signs because that is done for + next! If bSgn$
= "-" Then bSgn$
= "" Else bSgn$
= "-" ' flip bSgn$ with op$ op$ = "+" ' turn this over to + op already done! below
If aSgn$
= bSgn$
Then 'really add postOp$ = add$(ca$, cb$)
ElseIf aSgn$
<> bSgn$
Then 'have a case of subtraction 'but which is first and which is 2nd and should final sign be pos or neg
If TrimLead0$
(ca$
) = TrimLead0
(cb$
) Then 'remove case a = b aLTb = LTE(ca$, cb$)
postOp$ = subtr$(cb$, ca$)
Else ' a > b so a - sgn wins - (a - b) postOp$ = subtr$(ca$, cb$)
postOp$ = subtr$(cb$, ca$)
postOp$ = subtr$(ca$, cb$)
postOp$ = mult$(ca$, cb$)
postOp$ = divide$(ca$, cb$)
If op$
<> "/" Then 'put dp back lpop
= Len(postOp$
) ' put decimal back if there is non zero stuff following it If Len(Mid$(postOp$
, lpop
- dp
+ 1)) Then ' fix 1 extra dot appearing in 10000! ?! If TrimLead0$
(Mid$(postOp$
, lpop
- dp
+ 1)) <> "0" Then ' .0 or .00 or .000 ?? postOp$
= Mid$(postOp$
, 1, lpop
- dp
) + "." + Mid$(postOp$
, lpop
- dp
+ 1) mr$ = trim0$(postOp$) 'trim lead 0's then tack on sign
Function divide$
(n$
, d$
) ' goal here is 100 digits precision not 100 digits past decimal
' aha! found a bug when d$ gets really huge 100 is no where near enough!!!!
' 2021-06-03 fix by adding 100 to len(d$), plus have to go a little past 100 like 200
di$
= Mid$(nInverse$
(d$
, Len(d$
) + 200), 2) 'chop off decimal point after ndi$ = mult$(n$, di$)
divide$ = ndi$
' This uses Subtr1$ is Positive Integer only!
' DP = Decimal places = says when to quit if don't find perfect divisor before
Function nInverse$
(n$
, DP
As Long) 'assume decimal at very start of the string of digits returned Dim m$
(1 To 9), si$
, r$
, outstr$
, d$
m$(i) = mult$(si$, n$)
outstr$ = ""
outstr$ = "." 'everything else n > 1 is decimal 8/17
r$ = "10"
While LT
(r$
, n$
) ' 2021-06-08 this should be strictly Less Than outstr$ = outstr$ + "0" ' add 0 to the output string
r$ = r$ + "0"
outstr$ = outstr$ + d$
r$ = subtr1$(r$, mult$(d$, n$)) 'r = r -d*n ' 2021-06-08 subtr1 works faster
r$ = r$ + "0" 'add another place
Function mult$
(a$
, b$
) 'assume both positive integers prechecked as all digits strings Dim f18$
, f1$
, t$
, build$
, accum$
'find the longer number and make it a mult of 18 to take 18 digits at a time from it
f1$ = b$
f1$ = a$
For dp
= Len(f1$
) To 1 Step -1 'dp = digit position of the f1$ build$ = "" 'line builder
co = 0
'now taking 18 digits at a time Thanks Steve McNeill
v18
= Val(Mid$(f18$
, (m
- g
) * 18 + 1, 18)) build$
= Mid$(t$
, 2) + build$
accum$ = build$
accum$
= add$
(accum$
, build$
+ String$(Len(f1$
) - dp
, "0")) mult$ = accum$
'this function needs TrimLead0$(s$)
Function LTE
(a$
, b$
) ' a$ Less Than or Equal b$ comparison of 2 strings ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
LTE = -1
LTE = -1
LTE = 0
'need this for ninverse faster than subtr$ for sign
Function LT
(a$
, b$
) ' a$ Less Than or Equal b$ comparison of 2 strings ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
If la
< lb
Then ' a is smaller LT = -1
LT = 0
copys$
= _Trim$(s$
) 'might as well remove spaces too TrimTail0$ = copys$
i
= Len(copys$
): find
= 0 i = i - 1: find = 1
TrimTail0$
= Mid$(copys$
, 1, dp
- 1) TrimTail0$
= Mid$(copys$
, 1, i
)
cs$ = s$
cs$ = TrimLead0$(cs$)
cs$ = TrimTail0$(cs$)
If si$
= "-" Then trim0$
= si$
+ cs$
Else trim0$
= cs$
' for displaying truncated numbers say to 60 digits
cNum$ = num$ 'since num$ could get changed
showDP$ = num$
d
= Val(Mid$(cNum$
, dp
+ nDP
+ 1, 1)) cNum$ = "0" + cNum$ ' tack on another 0 just in case 9's all the way to left
dp = dp + 1
i = dp + nDP
i = i - 1
cNum$
= Mid$(cNum$
, 1, dp
+ nDP
) 'chop it showDP$ = trim0$(cNum$)
showDP$
= Mid$(cNum$
, 1, dp
+ nDP
)