Author Topic: String Math  (Read 18650 times)

0 Members and 1 Guest are viewing this topic.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: String Math
« Reply #75 on: June 21, 2021, 09:13:43 pm »
Quote
bplus, please explain how you handle big numbers in your nInverse function

@jack  I did once already, in English, no code:
https://www.qb64.org/forum/index.php?topic=3984.msg133309#msg133309

Did you see that?

nInverse of string o 9's looks OK to me>
Code: QB64: [Select]
  1. _Title "String Math 2021-06-14" ' b+ from SM2 (2021 June) a bunch of experiments to fix and improve speeds.
  2.  
  3. Randomize Timer 'now that it's seems to be running silent
  4. Screen _NewImage(1200, 700, 32)
  5. _Delay .25
  6.  
  7. 'test new stuff
  8. For j = 1 To 40
  9.     Print nInverse$(String$(j, "9"), 100)
  10.  
  11.  
  12. ' This is used in nInverse$ not by Mr$ because there it saves time!
  13. Function subtr1$ (a$, b$)
  14.     Dim As Long la, lb, lResult, i, ca, cb, w
  15.     Dim result$, fa$, fb$
  16.  
  17.     la = Len(a$): lb = Len(b$)
  18.     If la > lb Then lResult = la Else lResult = lb
  19.     result$ = Space$(lResult)
  20.     fa$ = result$: fb$ = result$
  21.     Mid$(fa$, lResult - la + 1) = a$
  22.     Mid$(fb$, lResult - lb + 1) = b$
  23.     For i = lResult To 1 Step -1
  24.         ca = Val(Mid$(fa$, i, 1))
  25.         cb = Val(Mid$(fb$, i, 1))
  26.         If cb > ca Then ' borrow 10
  27.             Mid$(result$, i, 1) = Right$(Str$(10 + ca - cb), 1)
  28.             w = i - 1
  29.             While w > 0 And Mid$(fa$, w, 1) = "0"
  30.                 Mid$(fa$, w, 1) = "9"
  31.                 w = w - 1
  32.             Wend
  33.             Mid$(fa$, w, 1) = Right$(Str$(Val(Mid$(fa$, w, 1)) - 1), 1)
  34.         Else
  35.             Mid$(result$, i, 1) = Right$(Str$(ca - cb), 1)
  36.         End If
  37.     Next
  38.     subtr1$ = result$
  39.  
  40. Function TrimLead0$ (s$) 'for treating strings as number (pos integers)
  41.     Dim copys$
  42.     Dim As Long i, find
  43.     copys$ = _Trim$(s$) 'might as well remove spaces too
  44.     i = 1: find = 0
  45.     While i < Len(copys$) And Mid$(copys$, i, 1) = "0"
  46.         i = i + 1: find = 1
  47.     Wend
  48.     If find = 1 Then copys$ = Mid$(copys$, i)
  49.     If copys$ = "" Then TrimLead0$ = "0" Else TrimLead0$ = copys$
  50.  
  51. ' catchy? mr$ for math regulator  cop$ = " + - * / " 1 of 4 basic arithmetics
  52.  
  53. ' This uses Subtr1$ is Positive Integer only!
  54. ' DP = Decimal places = says when to quit if don't find perfect divisor before
  55. Function nInverse$ (n$, DP As Long) 'assume decimal at very start of the string of digits returned
  56.     Dim m$(1 To 9), si$, r$, outstr$, d$
  57.     Dim i As Long
  58.     For i = 1 To 9
  59.         si$ = _Trim$(Str$(i))
  60.         m$(i) = mult$(si$, n$)
  61.     Next
  62.     outstr$ = ""
  63.     If n$ = "0" Then nInverse$ = "Div 0": Exit Function
  64.     If n$ = "1" Then nInverse$ = "1": Exit Function
  65.     outstr$ = "." 'everything else n > 1 is decimal 8/17
  66.     r$ = "10"
  67.     Do
  68.         While LT(r$, n$) ' 2021-06-08 this should be strictly Less Than
  69.             outstr$ = outstr$ + "0" '            add 0 to the  output string
  70.             If Len(outstr$) = DP + 1 Then nInverse$ = outstr$: Exit Function 'DP length?
  71.             r$ = r$ + "0"
  72.         Wend
  73.         For i = 9 To 1 Step -1
  74.             If LTE(m$(i), r$) Then d$ = _Trim$(Str$(i)): Exit For
  75.         Next
  76.         outstr$ = outstr$ + d$
  77.         If Len(outstr$) = DP + 1 Then nInverse$ = outstr$: Exit Function
  78.         r$ = subtr1$(r$, mult$(d$, n$)) 'r = r -d*n    ' 2021-06-08 subtr1 works faster
  79.         If TrimLead0$(r$) = "0" Then nInverse$ = outstr$: Exit Function ' add trimlead0$ 6/08
  80.         r$ = r$ + "0" 'add another place
  81.     Loop
  82.  
  83. Function mult$ (a$, b$) 'assume both positive integers prechecked as all digits strings
  84.     Dim As Long la, lb, m, g, dp
  85.     Dim As _Unsigned _Integer64 v18, sd, co
  86.     Dim f18$, f1$, t$, build$, accum$
  87.  
  88.     If a$ = "0" Then mult$ = "0": Exit Function
  89.     If b$ = "0" Then mult$ = "0": Exit Function
  90.     If a$ = "1" Then mult$ = b$: Exit Function
  91.     If b$ = "1" Then mult$ = a$: Exit Function
  92.     'find the longer number and make it a mult of 18 to take 18 digits at a time from it
  93.     la = Len(a$): lb = Len(b$)
  94.     If la > lb Then
  95.         m = Int(la / 18) + 1
  96.         f18$ = Right$(String$(m * 18, "0") + a$, m * 18)
  97.         f1$ = b$
  98.     Else
  99.         m = Int(lb / 18) + 1
  100.         f18$ = Right$(String$(m * 18, "0") + b$, m * 18)
  101.         f1$ = a$
  102.     End If
  103.     For dp = Len(f1$) To 1 Step -1 'dp = digit position of the f1$
  104.         build$ = "" 'line builder
  105.         co = 0
  106.         'now taking 18 digits at a time Thanks Steve McNeill
  107.         For g = 1 To m
  108.             v18 = Val(Mid$(f18$, (m - g) * 18 + 1, 18))
  109.             sd = Val(Mid$(f1$, dp, 1))
  110.             t$ = Right$(String$(19, "0") + _Trim$(Str$(v18 * sd + co)), 19)
  111.             co = Val(Mid$(t$, 1, 1))
  112.             build$ = Mid$(t$, 2) + build$
  113.         Next g
  114.         If co Then build$ = _Trim$(Str$(co)) + build$
  115.         If dp = Len(f1$) Then
  116.             accum$ = build$
  117.         Else
  118.             accum$ = add$(accum$, build$ + String$(Len(f1$) - dp, "0"))
  119.         End If
  120.     Next dp
  121.     mult$ = accum$
  122.  
  123. ' LTE    Less Than or Equal for 2 Big numbers in String form
  124. 'this function needs TrimLead0$(s$)
  125. Function LTE (a$, b$) ' a$ Less Than or Equal b$  comparison of 2 strings
  126.     Dim ca$, cb$
  127.     Dim As Long la, lb, i
  128.     ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
  129.     la = Len(ca$): lb = Len(cb$)
  130.     If ca$ = cb$ Then
  131.         LTE = -1
  132.     ElseIf la < lb Then ' a is smaller
  133.         LTE = -1
  134.     ElseIf la > lb Then ' a is bigger
  135.         LTE = 0
  136.     ElseIf la = lb Then ' equal lengths
  137.         For i = 1 To Len(ca$)
  138.             If Val(Mid$(ca$, i, 1)) > Val(Mid$(cb$, i, 1)) Then
  139.                 LTE = 0: Exit Function
  140.             ElseIf Val(Mid$(ca$, i, 1)) < Val(Mid$(cb$, i, 1)) Then
  141.                 LTE = -1: Exit Function
  142.             End If
  143.         Next
  144.     End If
  145.  
  146.  
  147. 'need this for ninverse faster than subtr$ for sign
  148. Function LT (a$, b$) ' a$ Less Than or Equal b$  comparison of 2 strings
  149.     Dim ca$, cb$
  150.     Dim As Long la, lb, i
  151.     ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
  152.     la = Len(ca$): lb = Len(cb$)
  153.     If la < lb Then ' a is smaller
  154.         LT = -1
  155.     ElseIf la > lb Then ' a is bigger
  156.         LT = 0
  157.     ElseIf la = lb Then ' equal lengths
  158.         For i = 1 To Len(ca$)
  159.             If Val(Mid$(ca$, i, 1)) > Val(Mid$(cb$, i, 1)) Then
  160.                 LT = 0: Exit Function
  161.             ElseIf Val(Mid$(ca$, i, 1)) < Val(Mid$(cb$, i, 1)) Then
  162.                 LT = -1: Exit Function
  163.             End If
  164.         Next
  165.     End If
  166.  
  167. Function add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no - signs
  168.     'set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
  169.     Dim As Long la, lb, m, g
  170.     Dim fa$, fb$, t$, new$, result$
  171.     la = Len(a$): lb = Len(b$)
  172.     If la > lb Then m = Int(la / 18) + 1 Else m = Int(lb / 18) + 1
  173.     fa$ = Right$(String$(m * 18, "0") + a$, m * 18)
  174.     fb$ = Right$(String$(m * 18, "0") + b$, m * 18)
  175.  
  176.     'now taking 18 digits at a time Thanks Steve McNeill
  177.     For g = 1 To m
  178.         sa = Val(Mid$(fa$, (m - g) * 18 + 1, 18))
  179.         sb = Val(Mid$(fb$, (m - g) * 18 + 1, 18))
  180.         t$ = Right$(String$(36, "0") + _Trim$(Str$(sa + sb + co)), 36)
  181.         co = Val(Mid$(t$, 1, 18))
  182.         new$ = Mid$(t$, 19)
  183.         result$ = new$ + result$
  184.     Next
  185.     If co Then result$ = Str$(co) + result$
  186.     add$ = result$
  187.  
  188.  

 
string$9s.PNG


All you guys are posting allot of interesting things for handling Big Numbers, wish I had more time to study all these  wonderful ideas, soon I hope.
« Last Edit: June 21, 2021, 09:18:10 pm by bplus »

Offline jack

  • Seasoned Forum Regular
  • Posts: 408
    • View Profile
Re: String Math
« Reply #76 on: June 21, 2021, 09:38:39 pm »
bplus, yes the string of 9's work ok but if you feedback the result of that into nInverse to get back the original number it fails
BTW TreeBeard's division is pretty fast, 1/999999999999999999999998999999999999999999999999 to 2785 digits in 0.054 seconds

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: String Math
« Reply #77 on: June 21, 2021, 09:53:48 pm »
bplus, yes the string of 9's work ok but if you feedback the result of that into nInverse to get back the original number it fails
BTW TreeBeard's division is pretty fast, 1/999999999999999999999998999999999999999999999999 to 2785 digits in 0.054 seconds

nInverse only meant to work on positive Integers.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: String Math
« Reply #78 on: June 21, 2021, 10:18:04 pm »
OK nInverse$ can only do positive integers but Mr$ can Divide$ any number into 1 so this!

An inverse of a string o 9s and then divide$ through mr$ the result into 1 reversing the inverse!
BUT only show 1 or 2 places in decimal and when nInverse$ use way more than 100 Decimal places but only show first 100 so screens continue to make sense, when see ZZZ that means SLEEPing press any key...

Thusly we can run pretty impressive string of 9's.
Code: QB64: [Select]
  1. _Title "String Math 2021-06-14" ' b+ from SM2 (2021 June) a bunch of experiments to fix and improve speeds.
  2. ' June 2021 fix some old String Math procedures, better nInverse with new LT frunction, remove experimental procedures.
  3. ' Now with decent sqrRoot it works independent of Mr$() = Math Regulator that handles signs and decimals and calls to
  4. ' add$(), subtr$, mult$, divide$ (100 significant digits),  add$(), subtr$, mult$ are exact!
  5. ' If you need higher precsion divide, I recommend use nInverse on denominator (integer)
  6. ' then add sign and decimal and mult$() that number with numerator to get divsion answer in higher precision than 100.
  7. ' (See how Mr$() handles division and just call nInverse$ with what precision you need.)
  8. ' The final function showDP$() is for displaying these number to a set amount of Decimal Places.
  9.  
  10. ' The main code is sampler of tests performed with these functions.
  11.  
  12. Randomize Timer 'now that it's seems to be running silent
  13. Screen _NewImage(1200, 700, 32)
  14. _Delay .25
  15.  
  16. 'test new stuff
  17. Dim j As Long, inv$, invinv$
  18. For j = 1 To 200
  19.     inv$ = nInverse$(String$(j, "9"), 1000)
  20.     invinv$ = mr$("1", "/", inv$)
  21.     Print showDP(inv$, 100)
  22.     Print showDP$(invinv$, 2)
  23.     If j Mod 20 = 0 Then Print "ZZZ...": Sleep
  24.  
  25.  
  26. Function add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no - signs
  27.     'set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
  28.     Dim As Long la, lb, m, g
  29.     Dim fa$, fb$, t$, new$, result$
  30.     la = Len(a$): lb = Len(b$)
  31.     If la > lb Then m = Int(la / 18) + 1 Else m = Int(lb / 18) + 1
  32.     fa$ = Right$(String$(m * 18, "0") + a$, m * 18)
  33.     fb$ = Right$(String$(m * 18, "0") + b$, m * 18)
  34.  
  35.     'now taking 18 digits at a time Thanks Steve McNeill
  36.     For g = 1 To m
  37.         sa = Val(Mid$(fa$, (m - g) * 18 + 1, 18))
  38.         sb = Val(Mid$(fb$, (m - g) * 18 + 1, 18))
  39.         t$ = Right$(String$(36, "0") + _Trim$(Str$(sa + sb + co)), 36)
  40.         co = Val(Mid$(t$, 1, 18))
  41.         new$ = Mid$(t$, 19)
  42.         result$ = new$ + result$
  43.     Next
  44.     If co Then result$ = Str$(co) + result$
  45.     add$ = result$
  46.  
  47. ' This is used in nInverse$ not by Mr$ because there it saves time!
  48. Function subtr1$ (a$, b$)
  49.     Dim As Long la, lb, lResult, i, ca, cb, w
  50.     Dim result$, fa$, fb$
  51.  
  52.     la = Len(a$): lb = Len(b$)
  53.     If la > lb Then lResult = la Else lResult = lb
  54.     result$ = Space$(lResult)
  55.     fa$ = result$: fb$ = result$
  56.     Mid$(fa$, lResult - la + 1) = a$
  57.     Mid$(fb$, lResult - lb + 1) = b$
  58.     For i = lResult To 1 Step -1
  59.         ca = Val(Mid$(fa$, i, 1))
  60.         cb = Val(Mid$(fb$, i, 1))
  61.         If cb > ca Then ' borrow 10
  62.             Mid$(result$, i, 1) = Right$(Str$(10 + ca - cb), 1)
  63.             w = i - 1
  64.             While w > 0 And Mid$(fa$, w, 1) = "0"
  65.                 Mid$(fa$, w, 1) = "9"
  66.                 w = w - 1
  67.             Wend
  68.             Mid$(fa$, w, 1) = Right$(Str$(Val(Mid$(fa$, w, 1)) - 1), 1)
  69.         Else
  70.             Mid$(result$, i, 1) = Right$(Str$(ca - cb), 1)
  71.         End If
  72.     Next
  73.     subtr1$ = result$
  74.  
  75. ' 2021-06-08 fix up with new mr call that decides the sign and puts the greater number first
  76. Function subtr$ (sum$, minus$) ' assume both numbers are positive all digits
  77.     Dim As Long m, g, p
  78.     Dim ts$, tm$, sign$, LG$, sm$, t$, result$
  79.  
  80.     ts$ = _Trim$(sum$): tm$ = _Trim$(minus$) ' fixed subtr$ 2021-06-05
  81.     If trim0(ts$) = trim0$(tm$) Then subtr$ = "0": Exit Function 'proceed knowing not equal
  82.     tenE18 = 1000000000000000000 'yes!!! no dang E's
  83.     sign$ = ""
  84.     m = Int(Len(ts$) / 18) + 1
  85.     LG$ = Right$(String$(m * 18, "0") + ts$, m * 18)
  86.     sm$ = Right$(String$(m * 18, "0") + tm$, m * 18)
  87.  
  88.     'now taking 18 digits at a time From Steve I learned we can do more than 1 digit at a time
  89.     For g = 1 To m
  90.         VB = Val(Mid$(LG$, m * 18 - g * 18 + 1, 18))
  91.         vs = Val(Mid$(sm$, m * 18 - g * 18 + 1, 18))
  92.         If vs > VB Then
  93.             t$ = Right$(String$(18, "0") + _Trim$(Str$(tenE18 - vs + VB)), 18)
  94.             p = (m - g) * 18
  95.             While p > 0 And Mid$(LG$, p, 1) = "0"
  96.                 Mid$(LG$, p, 1) = "9"
  97.                 p = p - 1
  98.             Wend
  99.             If p > 0 Then Mid$(LG$, p, 1) = _Trim$(Str$(Val(Mid$(LG$, p, 1)) - 1))
  100.         Else
  101.             t$ = Right$(String$(18, "0") + _Trim$(Str$(VB - vs)), 18)
  102.         End If
  103.         result$ = t$ + result$
  104.     Next
  105.     subtr$ = result$
  106.  
  107. Function TrimLead0$ (s$) 'for treating strings as number (pos integers)
  108.     Dim copys$
  109.     Dim As Long i, find
  110.     copys$ = _Trim$(s$) 'might as well remove spaces too
  111.     i = 1: find = 0
  112.     While i < Len(copys$) And Mid$(copys$, i, 1) = "0"
  113.         i = i + 1: find = 1
  114.     Wend
  115.     If find = 1 Then copys$ = Mid$(copys$, i)
  116.     If copys$ = "" Then TrimLead0$ = "0" Else TrimLead0$ = copys$
  117.  
  118. ' catchy? mr$ for math regulator  cop$ = " + - * / " 1 of 4 basic arithmetics
  119. ' Fixed so that add and subtract have signs calc'd in Mr and correct call to add or subtract made
  120. ' with bigger minus smaller in subtr$() call
  121. Function mr$ (a$, cop$, b$)
  122.     Dim op$, ca$, cb$, aSgn$, bSgn$, postOp$, sgn$
  123.     Dim As Long adp, bdp, dp, lpop, aLTb
  124.  
  125.     op$ = _Trim$(cop$) 'save fixing each time
  126.     ca$ = _Trim$(a$): cb$ = _Trim$(b$) 'make copies in case we change
  127.     'strip signs and decimals
  128.     If Left$(ca$, 1) = "-" Then
  129.         aSgn$ = "-": ca$ = Mid$(ca$, 2)
  130.     Else
  131.         aSgn$ = ""
  132.     End If
  133.     dp = InStr(ca$, ".")
  134.     If dp > 0 Then
  135.         adp = Len(ca$) - dp
  136.         ca$ = Mid$(ca$, 1, dp - 1) + Mid$(ca$, dp + 1)
  137.     Else
  138.         adp = 0
  139.     End If
  140.     If Left$(cb$, 1) = "-" Then
  141.         bSgn$ = "-": cb$ = Mid$(cb$, 2)
  142.     Else
  143.         bSgn$ = ""
  144.     End If
  145.     dp = InStr(cb$, ".")
  146.     If dp > 0 Then
  147.         bdp = Len(cb$) - dp
  148.         cb$ = Mid$(cb$, 1, dp - 1) + Mid$(cb$, dp + 1)
  149.     Else
  150.         bdp = 0
  151.     End If
  152.     If op$ = "+" Or op$ = "-" Or op$ = "/" Then 'add or subtr  even up strings on right of decimal
  153.         'even up the right sides of decimals if any
  154.         If adp > bdp Then dp = adp Else dp = bdp
  155.         If adp < dp Then ca$ = ca$ + String$(dp - adp, "0")
  156.         If bdp < dp Then cb$ = cb$ + String$(dp - bdp, "0")
  157.     ElseIf op$ = "*" Then
  158.         dp = adp + bdp
  159.     End If
  160.     If op$ = "*" Or op$ = "/" Then
  161.         If aSgn$ = bSgn$ Then sgn$ = "" Else sgn$ = "-"
  162.     End If
  163.  
  164.     'now according to signs and op$ call add$ or subtr$
  165.     If op$ = "-" Then ' make it adding according to signs because that is done for + next!
  166.         If bSgn$ = "-" Then bSgn$ = "" Else bSgn$ = "-" ' flip bSgn$ with op$
  167.         op$ = "+" ' turn this over to + op already done! below
  168.     End If
  169.     If op$ = "+" Then
  170.         If aSgn$ = bSgn$ Then 'really add
  171.             postOp$ = add$(ca$, cb$)
  172.             sgn$ = aSgn$
  173.         ElseIf aSgn$ <> bSgn$ Then 'have a case of subtraction
  174.             'but which is first and which is 2nd and should final sign be pos or neg
  175.             If TrimLead0$(ca$) = TrimLead0(cb$) Then 'remove case a = b
  176.                 mr$ = "0": Exit Function
  177.             Else
  178.                 aLTb = LTE(ca$, cb$)
  179.                 If aSgn$ = "-" Then
  180.                     If aLTb Then ' b - a = pos
  181.                         postOp$ = subtr$(cb$, ca$)
  182.                         sgn$ = ""
  183.                     Else '  a > b so a - sgn wins  - (a - b)
  184.                         postOp$ = subtr$(ca$, cb$)
  185.                         sgn$ = "-"
  186.                     End If
  187.                 Else ' b has the - sgn
  188.                     If aLTb Then ' result is -
  189.                         postOp$ = subtr$(cb$, ca$)
  190.                         sgn$ = "-"
  191.                     Else ' result is pos
  192.                         postOp$ = subtr$(ca$, cb$)
  193.                         sgn$ = ""
  194.                     End If
  195.                 End If
  196.             End If
  197.         End If
  198.     ElseIf op$ = "*" Then
  199.         postOp$ = mult$(ca$, cb$)
  200.     ElseIf op$ = "/" Then
  201.         postOp$ = divide$(ca$, cb$)
  202.     End If ' which op
  203.     If op$ <> "/" Then 'put dp back
  204.         lpop = Len(postOp$) ' put decimal back if there is non zero stuff following it
  205.         If Len(Mid$(postOp$, lpop - dp + 1)) Then ' fix 1 extra dot appearing in 10000! ?!
  206.             If TrimLead0$(Mid$(postOp$, lpop - dp + 1)) <> "0" Then '  .0   or .00 or .000  ??
  207.                 postOp$ = Mid$(postOp$, 1, lpop - dp) + "." + Mid$(postOp$, lpop - dp + 1)
  208.             End If
  209.         End If
  210.     End If
  211.     mr$ = trim0$(postOp$) 'trim lead 0's then tack on sign
  212.     If mr$ <> "0" Then mr$ = sgn$ + mr$
  213.  
  214. Function divide$ (n$, d$) ' goal here is 100 digits precision not 100 digits past decimal
  215.     Dim di$, ndi$
  216.     Dim As Long nD
  217.     If n$ = "0" Then divide$ = "0": Exit Function
  218.     If d$ = "0" Then divide$ = "div 0": Exit Function
  219.     If d$ = "1" Then divide$ = n$: Exit Function
  220.  
  221.     ' aha! found a bug when d$ gets really huge 100 is no where near enough!!!!
  222.     ' 2021-06-03 fix by adding 100 to len(d$), plus have to go a little past 100 like 200
  223.     di$ = Mid$(nInverse$(d$, Len(d$) + 200), 2) 'chop off decimal point after
  224.     nD = Len(di$)
  225.     ndi$ = mult$(n$, di$)
  226.     ndi$ = Mid$(ndi$, 1, Len(ndi$) - nD) + "." + Right$(String$(nD, "0") + Right$(ndi$, nD), nD)
  227.     divide$ = ndi$
  228.  
  229. ' This uses Subtr1$ is Positive Integer only!
  230. ' DP = Decimal places = says when to quit if don't find perfect divisor before
  231. Function nInverse$ (n$, DP As Long) 'assume decimal at very start of the string of digits returned
  232.     Dim m$(1 To 9), si$, r$, outstr$, d$
  233.     Dim i As Long
  234.     For i = 1 To 9
  235.         si$ = _Trim$(Str$(i))
  236.         m$(i) = mult$(si$, n$)
  237.     Next
  238.     outstr$ = ""
  239.     If n$ = "0" Then nInverse$ = "Div 0": Exit Function
  240.     If n$ = "1" Then nInverse$ = "1": Exit Function
  241.     outstr$ = "." 'everything else n > 1 is decimal 8/17
  242.     r$ = "10"
  243.     Do
  244.         While LT(r$, n$) ' 2021-06-08 this should be strictly Less Than
  245.             outstr$ = outstr$ + "0" '            add 0 to the  output string
  246.             If Len(outstr$) = DP + 1 Then nInverse$ = outstr$: Exit Function 'DP length?
  247.             r$ = r$ + "0"
  248.         Wend
  249.         For i = 9 To 1 Step -1
  250.             If LTE(m$(i), r$) Then d$ = _Trim$(Str$(i)): Exit For
  251.         Next
  252.         outstr$ = outstr$ + d$
  253.         If Len(outstr$) = DP + 1 Then nInverse$ = outstr$: Exit Function
  254.         r$ = subtr1$(r$, mult$(d$, n$)) 'r = r -d*n    ' 2021-06-08 subtr1 works faster
  255.         If TrimLead0$(r$) = "0" Then nInverse$ = outstr$: Exit Function ' add trimlead0$ 6/08
  256.         r$ = r$ + "0" 'add another place
  257.     Loop
  258.  
  259. Function mult$ (a$, b$) 'assume both positive integers prechecked as all digits strings
  260.     Dim As Long la, lb, m, g, dp
  261.     Dim As _Unsigned _Integer64 v18, sd, co
  262.     Dim f18$, f1$, t$, build$, accum$
  263.  
  264.     If a$ = "0" Then mult$ = "0": Exit Function
  265.     If b$ = "0" Then mult$ = "0": Exit Function
  266.     If a$ = "1" Then mult$ = b$: Exit Function
  267.     If b$ = "1" Then mult$ = a$: Exit Function
  268.     'find the longer number and make it a mult of 18 to take 18 digits at a time from it
  269.     la = Len(a$): lb = Len(b$)
  270.     If la > lb Then
  271.         m = Int(la / 18) + 1
  272.         f18$ = Right$(String$(m * 18, "0") + a$, m * 18)
  273.         f1$ = b$
  274.     Else
  275.         m = Int(lb / 18) + 1
  276.         f18$ = Right$(String$(m * 18, "0") + b$, m * 18)
  277.         f1$ = a$
  278.     End If
  279.     For dp = Len(f1$) To 1 Step -1 'dp = digit position of the f1$
  280.         build$ = "" 'line builder
  281.         co = 0
  282.         'now taking 18 digits at a time Thanks Steve McNeill
  283.         For g = 1 To m
  284.             v18 = Val(Mid$(f18$, (m - g) * 18 + 1, 18))
  285.             sd = Val(Mid$(f1$, dp, 1))
  286.             t$ = Right$(String$(19, "0") + _Trim$(Str$(v18 * sd + co)), 19)
  287.             co = Val(Mid$(t$, 1, 1))
  288.             build$ = Mid$(t$, 2) + build$
  289.         Next g
  290.         If co Then build$ = _Trim$(Str$(co)) + build$
  291.         If dp = Len(f1$) Then
  292.             accum$ = build$
  293.         Else
  294.             accum$ = add$(accum$, build$ + String$(Len(f1$) - dp, "0"))
  295.         End If
  296.     Next dp
  297.     mult$ = accum$
  298.  
  299. 'this function needs TrimLead0$(s$)
  300. Function LTE (a$, b$) ' a$ Less Than or Equal b$  comparison of 2 strings
  301.     Dim ca$, cb$
  302.     Dim As Long la, lb, i
  303.     ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
  304.     la = Len(ca$): lb = Len(cb$)
  305.     If ca$ = cb$ Then
  306.         LTE = -1
  307.     ElseIf la < lb Then ' a is smaller
  308.         LTE = -1
  309.     ElseIf la > lb Then ' a is bigger
  310.         LTE = 0
  311.     ElseIf la = lb Then ' equal lengths
  312.         For i = 1 To Len(ca$)
  313.             If Val(Mid$(ca$, i, 1)) > Val(Mid$(cb$, i, 1)) Then
  314.                 LTE = 0: Exit Function
  315.             ElseIf Val(Mid$(ca$, i, 1)) < Val(Mid$(cb$, i, 1)) Then
  316.                 LTE = -1: Exit Function
  317.             End If
  318.         Next
  319.     End If
  320.  
  321. 'need this for ninverse faster than subtr$ for sign
  322. Function LT (a$, b$) ' a$ Less Than or Equal b$  comparison of 2 strings
  323.     Dim ca$, cb$
  324.     Dim As Long la, lb, i
  325.     ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
  326.     la = Len(ca$): lb = Len(cb$)
  327.     If la < lb Then ' a is smaller
  328.         LT = -1
  329.     ElseIf la > lb Then ' a is bigger
  330.         LT = 0
  331.     ElseIf la = lb Then ' equal lengths
  332.         For i = 1 To Len(ca$)
  333.             If Val(Mid$(ca$, i, 1)) > Val(Mid$(cb$, i, 1)) Then
  334.                 LT = 0: Exit Function
  335.             ElseIf Val(Mid$(ca$, i, 1)) < Val(Mid$(cb$, i, 1)) Then
  336.                 LT = -1: Exit Function
  337.             End If
  338.         Next
  339.     End If
  340.  
  341. Function TrimTail0$ (s$)
  342.     Dim copys$
  343.     Dim As Long dp, i, find
  344.     copys$ = _Trim$(s$) 'might as well remove spaces too
  345.     TrimTail0$ = copys$
  346.     dp = InStr(copys$, ".")
  347.     If dp > 0 Then
  348.         i = Len(copys$): find = 0
  349.         While i > dp And Mid$(copys$, i, 1) = "0"
  350.             i = i - 1: find = 1
  351.         Wend
  352.         If find = 1 Then
  353.             If i = dp Then
  354.                 TrimTail0$ = Mid$(copys$, 1, dp - 1)
  355.             Else
  356.                 TrimTail0$ = Mid$(copys$, 1, i)
  357.             End If
  358.         End If
  359.     End If
  360.  
  361. Function trim0$ (s$)
  362.     Dim cs$, si$
  363.     cs$ = s$
  364.     si$ = Left$(cs$, 1)
  365.     If si$ = "-" Then cs$ = Mid$(cs$, 2)
  366.     cs$ = TrimLead0$(cs$)
  367.     cs$ = TrimTail0$(cs$)
  368.     If Right$(cs$, 1) = "." Then cs$ = Mid$(cs$, 1, Len(cs$) - 1)
  369.     If si$ = "-" Then trim0$ = si$ + cs$ Else trim0$ = cs$
  370.  
  371. ' for displaying truncated numbers say to 60 digits
  372. Function showDP$ (num$, nDP As Long)
  373.     Dim cNum$
  374.     Dim As Long dp, d, i
  375.     cNum$ = num$ 'since num$ could get changed
  376.     showDP$ = num$
  377.     dp = InStr(num$, ".")
  378.     If dp > 0 Then
  379.         If Len(Mid$(cNum$, dp + 1)) > nDP Then
  380.             d = Val(Mid$(cNum$, dp + nDP + 1, 1))
  381.             If d > 4 Then
  382.                 cNum$ = "0" + cNum$ ' tack on another 0 just in case 9's all the way to left
  383.                 dp = dp + 1
  384.                 i = dp + nDP
  385.                 While Mid$(cNum$, i, 1) = "9" Or Mid$(cNum$, i, 1) = "."
  386.                     If Mid$(cNum$, i, 1) = "9" Then
  387.                         Mid$(cNum$, i, 1) = "0"
  388.                     End If
  389.                     i = i - 1
  390.                 Wend
  391.                 Mid$(cNum$, i, 1) = _Trim$(Str$(Val(Mid$(cNum$, i, 1)) + 1)) 'last non 9 digit
  392.                 cNum$ = Mid$(cNum$, 1, dp + nDP) 'chop it
  393.                 showDP$ = trim0$(cNum$)
  394.             Else
  395.                 showDP$ = Mid$(cNum$, 1, dp + nDP)
  396.             End If
  397.         End If
  398.     End If
  399.  
« Last Edit: June 21, 2021, 10:19:32 pm by bplus »