QB64.org Forum

Active Forums => Programs => Topic started by: bplus on August 14, 2020, 05:05:05 pm

Title: String Math
Post by: bplus on August 14, 2020, 05:05:05 pm
Well I know Pete started a thread on this subject but started on wrong foot maybe, doing 1 character at a time.
https://www.qb64.org/forum/index.php?topic=1093.0

Anyway I thought I take another shot at it with more than 1 character at a time...
Here is the easiest first step adding 2 integers, no checks on the strings yet for - signs or non numbers.

Add$(a$, b$) with tester code:
Code: QB64: [Select]
  1. _TITLE "String Math" 'b+ started 2020-08-14
  2. ' 2020-08-14 start with add 2 arbitrary long strings
  3. RANDOMIZE TIMER 'now that it's seems to be running silent
  4. ' special or hard cases found while testing
  5. PRINT add$("0", "0") ' just 1 0?
  6. PRINT add$("1", "10023") 'fixed
  7. PRINT add$(STRING$(19, "9"), "1"), LEN(add$(STRING$(19, "9"), "1")) ' 1 and 19 0's
  8. PRINT add$("980", "401374340") 'fixed
  9.  
  10. 'random testing
  11. DIM al AS LONG, bl AS LONG, sum AS _INTEGER64, e1 AS INTEGER, e2 AS INTEGER
  12.     'pick two numbers
  13.     e1 = INT(10 * RND): e2 = INT(10 * RND)
  14.     al = RND * 10 ^ e1: bl = RND * 10 ^ e2: sum = al + bl
  15.     a$ = _TRIM$(STR$(al)): b$ = _TRIM$(STR$(bl))
  16.     PRINT
  17.     PRINT a$; " plus "; b$; " ="
  18.     addStr$ = add$(a$, b$)
  19.     PRINT addStr$; " according to add function we are testing."
  20.     qbCalc$ = _TRIM$(STR$(sum))
  21.     PRINT qbCalc$; " according to QB64 math"
  22.     IF qbCalc$ <> addStr$ THEN BEEP: SLEEP ' <<<<<<<<<< stop when find an discrepency
  23.     PRINT "Next sum check in 10 secs, or press key,  press escape to quit..."
  24.     _LIMIT 100 ' OK running silent so speed up
  25.  
  26. FUNCTION add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no spaces or - signs
  27.     'first thing is to set a and b numbers to same length and multiple of 8 so can take 8 digits at a time
  28.     DIM la AS INTEGER, bl AS INTEGER, m AS INTEGER, g AS INTEGER, sa AS LONG, sb AS LONG, co AS LONG
  29.     DIM t$, new$, result$
  30.     la = LEN(a$): lb = LEN(b$)
  31.     IF la > lb THEN m = INT(la / 8) + 1 ELSE m = INT(lb / 8) + 1
  32.     fa$ = RIGHT$(STRING$(m * 8, "0") + a$, m * 8)
  33.     fb$ = RIGHT$(STRING$(m * 8, "0") + b$, m * 8)
  34.  
  35.     'now taking 4 digits at a time From Steve I learned we can do more than 1 digit at a time
  36.     FOR g = 1 TO m
  37.         sa = VAL(MID$(fa$, m * 8 - g * 8 + 1, 8))
  38.         sb = VAL(MID$(fb$, m * 8 - g * 8 + 1, 8))
  39.         t$ = RIGHT$(STRING$(16, "0") + _TRIM$(STR$(sa + sb + co)), 16)
  40.         co = VAL(MID$(t$, 1, 8))
  41.         new$ = MID$(t$, 9)
  42.         result$ = new$ + result$
  43.  
  44.         'debug
  45.         'PRINT m * 8, sa, sb, t$, co, result$
  46.         'INPUT "OK "; w$ 'OK
  47.     NEXT
  48.  
  49.     IF co THEN result$ = STR$(co) + result$
  50.     result$ = _TRIM$(result$)
  51.     'strip 0
  52.     i = 1: find = 0
  53.     WHILE i < LEN(result$) AND MID$(result$, i, 1) = "0"
  54.         i = i + 1: find = 1
  55.     WEND
  56.     IF find = 1 THEN result$ = MID$(result$, i)
  57.     add$ = result$

It's running silent so it's not finding problems (anymore) ;-))

EDIT: I did this for INTEGERS first taking 4 at a time then switched to LONG taking 8 at a time.


Title: Re: String Math
Post by: SMcNeill on August 14, 2020, 07:04:08 pm
Easiest way to do this is with _UNSIGNED_INTEGER64 values, so you can read and work with 18 digits at a time.

For example:

1234567890.9078563412 is the same as 000000001234567890.907856341200000000.  <-- This scaled our string to a limit of digits so we can work with it in two chunks (left side of decimal once, right side of decimal once).

It's a ton faster than reading and adding a single digit at a time.

(Greater precision should be processed in batches of 18, then reduced to set limit, as desired.)

I'm occupied here taking care of mom for the next few days, but if you need me to write a quick demo, I'll do it when I get back home after.  ;)



Unsigned int64 is 18,446,744,073,709,551,615, which gives you room to add two 18-digit values together and have the 19th digit for the carry-over.
Title: Re: String Math
Post by: bplus on August 14, 2020, 09:19:13 pm
Easiest way to do this is with _UNSIGNED_INTEGER64 values, so you can read and work with 18 digits at a time.

For example:

1234567890.9078563412 is the same as 000000001234567890.907856341200000000.  <-- This scaled our string to a limit of digits so we can work with it in two chunks (left side of decimal once, right side of decimal once).

It's a ton faster than reading and adding a single digit at a time.

(Greater precision should be processed in batches of 18, then reduced to set limit, as desired.)

I'm occupied here taking care of mom for the next few days, but if you need me to write a quick demo, I'll do it when I get back home after.  ;)



Unsigned int64 is 18,446,744,073,709,551,615, which gives you room to add two 18-digit values together and have the 19th digit for the carry-over.


Nice idea but VAL() fails us for 18 or even 16 digits.

Code: QB64: [Select]
  1. _TITLE "VAL Limit"
  2. FOR i = 1 TO 20
  3.     PRINT i, VAL(STRING$(i, "9"))
  4.  
  5.  

OK will try 14 places but it's that 14-16 digits thing again, no, I feel safer with 12 places at a time.
Title: Re: String Math
Post by: bplus on August 14, 2020, 09:30:05 pm
Oh hell I had it right the first time, VAL() only gives us 9 digits when assigning to a variable!?!?

Code: QB64: [Select]
  1. _TITLE "VAL Limit"
  2. FOR i = 1 TO 20
  3.     a = VAL(STRING$(i, "9"))
  4.     b = VAL(STRING$(i, "9"))
  5.     PRINT i, a, b, VAL(STRING$(i, "9"))
  6.  

9 Digits is the limit? Is this a bug?
Title: Re: String Math
Post by: SMcNeill on August 14, 2020, 10:23:03 pm
Oh hell I had it right the first time, VAL() only gives us 9 digits when assigning to a variable!?!?

Code: QB64: [Select]
  1. _TITLE "VAL Limit"
  2. FOR i = 1 TO 20
  3.     a = VAL(STRING$(i, "9"))
  4.     b = VAL(STRING$(i, "9"))
  5.     PRINT i, a, b, VAL(STRING$(i, "9"))
  6.  

9 Digits is the limit? Is this a bug?

No bug.  https://www.qb64.org/wiki/Data_types

DIM a AS _UNSIGNED LONG, b AS LONG

Use _INTEGER64, not LONG
Title: Re: String Math
Post by: bplus on August 14, 2020, 10:33:34 pm
No bug.  https://www.qb64.org/wiki/Data_types

DIM a AS _UNSIGNED LONG, b AS LONG

Use _INTEGER64, not LONG

Did you run that little code snippet?

I showed that  VAL() wont give an UNSIGNED LONG variable more that 9 digits, repeat only 9 digits!

9 digits is the limit to VAL() assigned variables.

It doesn't seem right, that's why I asked if it is a bug.

 


AT 10 digits VAL() turns the 9's to gook!
Title: Re: String Math
Post by: SMcNeill on August 14, 2020, 10:38:13 pm
It's not a bug.  It's the limits of your data type.

A byte is from 0 to 255.
An integer is from 0 to 65535.
A long is from 0 to 4,294,967,295.  Anything greater than that value overflows.

Does the following glitch out for you?

Code: [Select]
_TITLE "VAL Limit"
DIM a AS _UNSIGNED _INTEGER64, b AS _INTEGER64
FOR i = 1 TO 20
    a = VAL(STRING$(i, "9"))
    b = VAL(STRING$(i, "9"))
    PRINT i, a, b, VAL(STRING$(i, "9"))
NEXT
Title: Re: String Math
Post by: bplus on August 14, 2020, 10:40:26 pm
Oh crap _INTEGER64 dang it! I got the two types mixed up! Sorry.


Ok so 18 digits looks OK but 19 does not:
 
Title: Re: String Math
Post by: SMcNeill on August 14, 2020, 10:42:13 pm
Oh crap _INTEGER64 dang it! I got the two types mixed up! Sorry.

That's why I even posted it in bold and italics for you.  ;D
Title: Re: String Math
Post by: bplus on August 14, 2020, 10:56:10 pm
Sorry again, home life has got me distracted big time.

But I don't know about the carry over 19th digit.
Title: Re: String Math
Post by: SMcNeill on August 14, 2020, 11:03:22 pm
Oh crap _INTEGER64 dang it! I got the two types mixed up! Sorry.


Ok so 18 digits looks OK but 19 does not:
 


Int64 maxes at 9,223,372,036,854,775,807

If you add 999,999,999,999,999,999 to 999,999,999,999,999,999, the total will be less than 9,223,372,036,854,775,807.

19 nines overflows, but 18 nines plus 18 nines won't.

It's why I said work with 18 digits and reserve the 19th for overflow. :)
Title: Re: String Math
Post by: bplus on August 14, 2020, 11:59:40 pm
Quote
19 nines overflows, but 18 nines plus 18 nines won't.

I am convinced! very good argument Steve, and it is working with 18 digits at a time:
Code: QB64: [Select]
  1. _TITLE "String Math add$ 2020-08-14_9P" 'b+ started 2020-08-14
  2. ' 2020-08-14 start with add 2 arbitrary long strings
  3. ' 2020-08-14 well it was 9PM when I started  as per Steve switch to _UNSIGNED _INTEGER64
  4.  
  5. RANDOMIZE TIMER 'now that it's seems to be running silent
  6. ' special or hard cases found while testing
  7. PRINT add$("0", "0") ' just 1 0?
  8. PRINT add$("1", "10023") 'fixed
  9.  
  10. DIM aa$
  11. aa$ = STRING$(19, "9")
  12. PRINT "aa$ = "; aa$ ' fine!!
  13. PRINT "aa$ + 1 = "; add$(aa$, "1"), "that's "; LEN(add$(aa$, "1")); " long."
  14. PRINT "aa$ + aa$ = "; add$(aa$, aa$)
  15. PRINT add$("980", "401374340") 'fixed
  16. PRINT " press any to for random testing that can be checked..."
  17.  
  18. 'random testing
  19. DIM al AS LONG, bl AS LONG, sum AS _INTEGER64, e1 AS INTEGER, e2 AS INTEGER, a$, b$, addStr$, qbCalc$
  20.     'pick two numbers
  21.     e1 = INT(10 * RND): e2 = INT(10 * RND)
  22.     al = RND * 10 ^ e1: bl = RND * 10 ^ e2: sum = al + bl
  23.     a$ = _TRIM$(STR$(al)): b$ = _TRIM$(STR$(bl))
  24.     PRINT
  25.     PRINT a$; " plus "; b$; " ="
  26.     addStr$ = add$(a$, b$)
  27.     PRINT addStr$; " according to add function we are testing."
  28.     qbCalc$ = _TRIM$(STR$(sum))
  29.     PRINT qbCalc$; " according to QB64 math"
  30.     IF qbCalc$ <> addStr$ THEN BEEP: SLEEP ' <<<<<<<<<< stop when find an discrepency
  31.     PRINT "Next sum check in 10 secs, or press key,  press escape to quit..."
  32.     _LIMIT 100 ' OK running silent so speed up
  33.  
  34. FUNCTION add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no spaces or - signs
  35.     'first thing is to set a and b numbers to same length and multiple of 8 so can take 8 digits at a time
  36.     DIM la AS INTEGER, lb AS INTEGER, m AS INTEGER, g AS INTEGER, i AS INTEGER, find AS INTEGER
  37.     DIM fa$, fb$, t$, new$, result$
  38.     la = LEN(a$): lb = LEN(b$)
  39.     IF la > lb THEN m = INT(la / 18) + 1 ELSE m = INT(lb / 18) + 1
  40.     fa$ = RIGHT$(STRING$(m * 18, "0") + a$, m * 18)
  41.     fb$ = RIGHT$(STRING$(m * 18, "0") + b$, m * 18)
  42.  
  43.     'now taking 4 digits at a time From Steve I learned we can do more than 1 digit at a time
  44.     FOR g = 1 TO m
  45.         sa = VAL(MID$(fa$, m * 18 - g * 18 + 1, 18))
  46.         sb = VAL(MID$(fb$, m * 18 - g * 18 + 1, 18))
  47.         t$ = RIGHT$(STRING$(36, "0") + _TRIM$(STR$(sa + sb + co)), 36)
  48.         co = VAL(MID$(t$, 1, 18))
  49.         new$ = MID$(t$, 19)
  50.         result$ = new$ + result$
  51.  
  52.         'debug
  53.         'DIM w$
  54.         'PRINT a$, m * 18, sa, sb, t$, co, result$
  55.         'INPUT "OK "; w$ 'OK
  56.     NEXT
  57.  
  58.     IF co THEN result$ = STR$(co) + result$
  59.     result$ = _TRIM$(result$)
  60.     'strip 0
  61.     i = 1: find = 0
  62.     WHILE i < LEN(result$) AND MID$(result$, i, 1) = "0"
  63.         i = i + 1: find = 1
  64.     WEND
  65.     IF find = 1 THEN result$ = MID$(result$, i)
  66.     add$ = result$
  67.  
  68.  

Now back to that other distraction Subtraction, it's a little more tricky handling the borrowing.

Title: Re: String Math
Post by: SMcNeill on August 15, 2020, 12:14:08 am
If you decide to multiply, I think your limit then is 9 digits.  Anything higher may overflow.

Subtraction isn't any real difference from addition, with 18(+1) digits in use.  (Take 19 digits from the larger number, 18 from the smaller, subtract.  Recycle the 19th digit as overflow.  Result has sign of largest number.)
Title: Re: String Math
Post by: bplus on August 15, 2020, 01:08:05 am
The borrowing had me concerned but I had worked that out in SmallBASIC (doing 1 digit at a time), beautiful use of MID$ SUB not Function! Plus I had to create an 18 0's string _UNSIGNED _INTEGER64*  variable otherwise the math was returning answers with dang E notation.

Here is subst$ function:
Code: QB64: [Select]
  1. _TITLE "String Math subtr$" 'b+ started 2020-08-14
  2. ' 2020-08-14 start with add 2 arbitrary long strings
  3. ' 2020-08-14 add$ posted 5 PM or so
  4. ' 2020-08-14 opt explicit, start subtr$ function
  5. ' 2020-08-15 1 AM subst$ function looking good!
  6.  
  7. RANDOMIZE TIMER 'now that it's seems to be running silent
  8. ' special or hard cases found while testing
  9. PRINT subtr$("0", "0")
  10. PRINT subtr$("11111111111111111375", "11111111111111111374")
  11. PRINT subtr$("11111111111111111374", "11111111111111111375")
  12. PRINT subtr$("1", "10023")
  13. PRINT subtr$("1" + STRING$(50, "0"), "1"), LEN(subtr$("1" + STRING$(50, "0"), "1"))
  14. PRINT subtr$("980", "5")
  15. PRINT subtr$("1" + STRING$(99, "0"), STRING$(99, "9"))
  16. PRINT subtr$(STRING$(99, "9"), "1" + STRING$(99, "0"))
  17. PRINT "press any for Random testing against QB64 math calculations..."
  18.  
  19. 'random testing
  20. DIM al AS LONG, bl AS LONG, sum AS _INTEGER64, e1 AS INTEGER, e2 AS INTEGER, a$, b$, subtrStr$, qbCalc$
  21.     'pick two numbers
  22.     e1 = INT(10 * RND): e2 = INT(10 * RND)
  23.     al = RND * 10 ^ e1: bl = RND * 10 ^ e2: sum = al - bl
  24.     a$ = _TRIM$(STR$(al)): b$ = _TRIM$(STR$(bl))
  25.     PRINT
  26.     PRINT a$; " minus "; b$; " ="
  27.     subtrStr$ = subtr$(a$, b$)
  28.     PRINT subtrStr$; " according to subtr$ function we are testing."
  29.     qbCalc$ = _TRIM$(STR$(sum))
  30.     PRINT qbCalc$; " according to QB64 math"
  31.     IF qbCalc$ <> subtrStr$ THEN BEEP: SLEEP ' <<<<<<<<<< stop when find an discrepency
  32.     PRINT "Next sum check in 10 secs, or press key,  press escape to quit..."
  33.     SLEEP 10 '
  34.  
  35. FUNCTION subtr$ (sum$, minus$) ' assume both numbers are positive all digits
  36.     IF sum$ = minus$ THEN subtr$ = "0": EXIT SUB
  37.  
  38.     DIM ls AS INTEGER, lm AS INTEGER, m AS INTEGER, g AS INTEGER, i AS INTEGER, find AS INTEGER, p AS INTEGER
  39.     DIM sign$, LG$, sm$, t$, new$, result$
  40.  
  41.     tenE18 = 1000000000000000000 'yes!!! no dang E's
  42.     ' which is bigger?
  43.     ls = LEN(sum$): lm = LEN(minus$)
  44.     IF ls > lm THEN
  45.         sign$ = ""
  46.         m = INT(ls / 18) + 1
  47.         LG$ = RIGHT$(STRING$(m * 18, "0") + sum$, m * 18)
  48.         sm$ = RIGHT$(STRING$(m * 18, "0") + minus$, m * 18)
  49.     ELSEIF ls < lm THEN
  50.         sign$ = "-"
  51.         m = INT(lm / 18) + 1
  52.         LG$ = RIGHT$(STRING$(m * 18, "0") + minus$, m * 18)
  53.         sm$ = RIGHT$(STRING$(m * 18, "0") + sum$, m * 18)
  54.     ELSEIF ls = lm THEN
  55.         FOR i = 1 TO LEN(sum$)
  56.             IF VAL(MID$(sum$, i, 1)) > VAL(MID$(minus$, i, 1)) THEN
  57.                 sign$ = ""
  58.                 m = INT(ls / 8) + 1
  59.                 LG$ = RIGHT$(STRING$(m * 18, "0") + sum$, m * 18)
  60.                 sm$ = RIGHT$(STRING$(m * 18, "0") + minus$, m * 18)
  61.                 EXIT FOR
  62.             ELSEIF VAL(MID$(sum$, i, 1)) < VAL(MID$(minus$, i, 1)) THEN
  63.                 sign$ = "-"
  64.                 m = INT(lm / 18) + 1
  65.                 LG$ = RIGHT$(STRING$(m * 18, "0") + minus$, m * 18)
  66.                 sm$ = RIGHT$(STRING$(m * 18, "0") + sum$, m * 18)
  67.                 EXIT FOR
  68.             END IF
  69.         NEXT
  70.     END IF
  71.     'now taking 18 digits at a time From Steve I learned we can do more than 1 digit at a time
  72.     FOR g = 1 TO m
  73.         VB = VAL(MID$(LG$, m * 18 - g * 18 + 1, 18))
  74.         vs = VAL(MID$(sm$, m * 18 - g * 18 + 1, 18))
  75.         IF vs > VB THEN
  76.             t$ = RIGHT$(STRING$(18, "0") + _TRIM$(STR$(tenE18 - vs + VB)), 18)
  77.  
  78.             'debug
  79.             'PRINT VB, tenE18, tenE18 - vs + VB, " t$ = "; t$
  80.  
  81.             ''borrow 1 = rewrite string
  82.             p = (m - g) * 18
  83.             WHILE p > 0 AND MID$(LG$, p, 1) = "0"
  84.                 MID$(LG$, p, 1) = "9"
  85.                 p = p - 1
  86.             WEND
  87.             IF p > 0 THEN MID$(LG$, p, 1) = _TRIM$(STR$(VAL(MID$(LG$, p, 1)) - 1))
  88.         ELSE
  89.             t$ = RIGHT$(STRING$(18, "0") + _TRIM$(STR$(VB - vs)), 18)
  90.         END IF
  91.         result$ = t$ + result$
  92.     NEXT
  93.     result$ = _TRIM$(result$)
  94.     'strip 0
  95.     i = 1: find = 0
  96.     WHILE i < LEN(result$) AND MID$(result$, i, 1) = "0"
  97.         i = i + 1: find = 1
  98.     WEND
  99.     IF find = 1 THEN result$ = MID$(result$, i)
  100.     subtr$ = sign$ + result$
  101.  
  102. FUNCTION add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no spaces or - signs
  103.     'first thing is to set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
  104.     DIM la AS INTEGER, lb AS INTEGER, m AS INTEGER, g AS INTEGER, i AS INTEGER, find AS INTEGER
  105.     DIM fa$, fb$, t$, new$, result$
  106.     la = LEN(a$): lb = LEN(b$)
  107.     IF la > lb THEN m = INT(la / 18) + 1 ELSE m = INT(lb / 18) + 1
  108.     fa$ = RIGHT$(STRING$(m * 18, "0") + a$, m * 18)
  109.     fb$ = RIGHT$(STRING$(m * 18, "0") + b$, m * 18)
  110.  
  111.     'now taking 18 digits at a time Thanks Steve McNeill
  112.     FOR g = 1 TO m
  113.         sa = VAL(MID$(fa$, m * 18 - g * 18 + 1, 18))
  114.         sb = VAL(MID$(fb$, m * 18 - g * 18 + 1, 18))
  115.         t$ = RIGHT$(STRING$(36, "0") + _TRIM$(STR$(sa + sb + co)), 36)
  116.         co = VAL(MID$(t$, 1, 18))
  117.         new$ = MID$(t$, 19)
  118.         result$ = new$ + result$
  119.  
  120.         'debug
  121.         'DIM w$
  122.         'PRINT a$, m * 18, sa, sb, t$, co, result$
  123.         'INPUT "OK "; w$ 'OK
  124.     NEXT
  125.     IF co THEN result$ = STR$(co) + result$
  126.     result$ = _TRIM$(result$)
  127.     'strip 0
  128.     i = 1: find = 0
  129.     WHILE i < LEN(result$) AND MID$(result$, i, 1) = "0"
  130.         i = i + 1: find = 1
  131.     WEND
  132.     IF find = 1 THEN result$ = MID$(result$, i)
  133.     add$ = result$
  134.  

Thanks Steve for your help tonight, nice to know someone whose been through this is looking over my shoulder.

*EDIT: for some reason my brain is constantly writing _UNSIGNED LONG when I mean to write _UNSIGNED _INTEGER64. Guess I am in habit of using _UNSIGNED LONG for color.

EDIT2: While starting the mult$ function, I noticed I did not update the subst$ test code with the latest add$ function that uses 18 digits at a time. Added a couple more special tests to substr$ function test code.
Title: Re: String Math
Post by: bplus on August 15, 2020, 03:43:25 pm
OK I think I have mult$ function working, can anyone find a test that points to problem?

Code: QB64: [Select]
  1. _TITLE "String Math mult$" 'b+ started 2020-08-15 adding to add$() and subtr$()
  2. ' 2020-08-14 start with add 2 arbitrary long strings
  3. ' 2020-08-14 add$ posted 5 PM or so
  4. ' 2020-08-14 opt explicit, start subtr$ function
  5. ' 2020-08-15 1 AM subst$ function looking good!
  6. ' 2020-08-15 fix some code from previous subs then attempt mult$ sub
  7.  
  8. RANDOMIZE TIMER 'now that it's seems to be running silent
  9. SCREEN _NEWIMAGE(1024, 700, 32)
  10. _DELAY .25
  11.  
  12. ''test that VAL can do 9 * 18 nines
  13. 'DIM a AS _UNSIGNED _INTEGER64, b AS _UNSIGNED _INTEGER64, c AS _UNSIGNED _INTEGER64
  14. 'b = 9
  15. 'a = VAL(STRING$(18, "9"))
  16. 'c = a * b
  17. 'PRINT c 'OK
  18.  
  19. ' special or hard cases found while testing
  20. DIM test$
  21. PRINT mult$(STRING$(54, "3"), "0") 'OK
  22. test$ = mult$(STRING$(54, "3"), "1")
  23. PRINT test$, LEN(test$)
  24. test$ = mult$(STRING$(54, "3"), "5")
  25. PRINT test$, LEN(test$)
  26. test$ = mult$(STRING$(54, "3"), "1" + STRING$(50, "0"))
  27. PRINT test$, LEN(test$)
  28. test$ = mult$(STRING$(50, "9"), STRING$(50, "9")) ' n-1 nines +8 then n-1 zeros + 1 = 2 * n length
  29. PRINT test$, LEN(test$)
  30. PRINT: PRINT "Press any for Random testing against QB64 calculations..."
  31.  
  32. 'random testing
  33. DIM product AS _UNSIGNED _INTEGER64, e1 AS INTEGER, e2 AS INTEGER, a$, b$, multStr$, qbCalc$
  34.     'pick two numbers
  35.     e1 = INT(10 * RND): e2 = INT(10 * RND)
  36.     al = RND * 10 ^ e1: bl = RND * 10 ^ e2: product = al * bl
  37.     a$ = _TRIM$(STR$(al)): b$ = _TRIM$(STR$(bl))
  38.     PRINT
  39.     PRINT a$; " times "; b$; " ="
  40.     multStr$ = mult$(a$, b$)
  41.     PRINT multStr$; " according to mult$ function we are testing."
  42.     qbCalc$ = _TRIM$(STR$(product))
  43.     PRINT qbCalc$; " according to QB64 math"
  44.     IF qbCalc$ <> multStr$ THEN BEEP: SLEEP ' <<<<<<<<<< stop when find an discrepency
  45.     PRINT "Next sum check in 10 secs, or press key,  press escape to quit..."
  46.     SLEEP 10
  47.  
  48. FUNCTION mult$ (a$, b$) 'assume both positive integers prechecked as all digits strings
  49.     DIM la AS INTEGER, lb AS INTEGER, m AS INTEGER, g AS INTEGER, i AS INTEGER, find AS INTEGER, dp AS INTEGER
  50.     DIM f18$, f1$, t$, build$, accum$
  51.  
  52.     'find the longer number and make it a mult of 18 to take 18 digits at a time from it
  53.     la = LEN(a$): lb = LEN(b$)
  54.     IF la > lb THEN
  55.         m = INT(la / 18) + 1
  56.         f18$ = RIGHT$(STRING$(m * 18, "0") + a$, m * 18)
  57.         f1$ = b$
  58.     ELSE
  59.         m = INT(lb / 18) + 1
  60.         f18$ = RIGHT$(STRING$(m * 18, "0") + b$, m * 18)
  61.         f1$ = a$
  62.     END IF
  63.     FOR dp = LEN(f1$) TO 1 STEP -1 'dp = digit position of the f1$
  64.         build$ = "" 'line builder
  65.         co = 0
  66.         'now taking 18 digits at a time Thanks Steve McNeill
  67.         FOR g = 1 TO m
  68.             v18 = VAL(MID$(f18$, m * 18 - g * 18 + 1, 18))
  69.             sd = VAL(MID$(f1$, dp, 1))
  70.             t$ = RIGHT$(STRING$(19, "0") + _TRIM$(STR$(v18 * sd + co)), 19)
  71.             co = VAL(MID$(t$, 1, 1))
  72.             build$ = MID$(t$, 2) + build$
  73.         NEXT g
  74.         IF co THEN build$ = _TRIM$(STR$(co)) + build$
  75.         IF dp = LEN(f1$) THEN
  76.             accum$ = build$
  77.         ELSE
  78.             accum$ = add$(accum$, build$ + STRING$(LEN(f1$) - dp, "0"))
  79.         END IF
  80.     NEXT dp
  81.     'strip 0
  82.     i = 1: find = 0
  83.     WHILE i < LEN(accum$) AND MID$(accum$, i, 1) = "0"
  84.         i = i + 1: find = 1
  85.     WEND
  86.     IF find = 1 THEN accum$ = MID$(accum$, i)
  87.     mult$ = accum$
  88.  
  89. FUNCTION subtr$ (sum$, minus$) ' assume both numbers are positive all digits
  90.     IF sum$ = minus$ THEN subtr$ = "0": EXIT SUB
  91.  
  92.     DIM ls AS INTEGER, lm AS INTEGER, m AS INTEGER, g AS INTEGER, i AS INTEGER, find AS INTEGER, p AS INTEGER
  93.     DIM sign$, LG$, sm$, t$, result$
  94.  
  95.     tenE18 = 1000000000000000000 'yes!!! no dang E's
  96.     ' which is bigger?
  97.     ls = LEN(sum$): lm = LEN(minus$)
  98.     IF ls > lm THEN
  99.         sign$ = ""
  100.         m = INT(ls / 18) + 1
  101.         LG$ = RIGHT$(STRING$(m * 18, "0") + sum$, m * 18)
  102.         sm$ = RIGHT$(STRING$(m * 18, "0") + minus$, m * 18)
  103.     ELSEIF ls < lm THEN
  104.         sign$ = "-"
  105.         m = INT(lm / 18) + 1
  106.         LG$ = RIGHT$(STRING$(m * 18, "0") + minus$, m * 18)
  107.         sm$ = RIGHT$(STRING$(m * 18, "0") + sum$, m * 18)
  108.     ELSEIF ls = lm THEN
  109.         FOR i = 1 TO LEN(sum$)
  110.             IF VAL(MID$(sum$, i, 1)) > VAL(MID$(minus$, i, 1)) THEN
  111.                 sign$ = ""
  112.                 m = INT(ls / 8) + 1
  113.                 LG$ = RIGHT$(STRING$(m * 18, "0") + sum$, m * 18)
  114.                 sm$ = RIGHT$(STRING$(m * 18, "0") + minus$, m * 18)
  115.                 EXIT FOR
  116.             ELSEIF VAL(MID$(sum$, i, 1)) < VAL(MID$(minus$, i, 1)) THEN
  117.                 sign$ = "-"
  118.                 m = INT(lm / 18) + 1
  119.                 LG$ = RIGHT$(STRING$(m * 18, "0") + minus$, m * 18)
  120.                 sm$ = RIGHT$(STRING$(m * 18, "0") + sum$, m * 18)
  121.                 EXIT FOR
  122.             END IF
  123.         NEXT
  124.     END IF
  125.     'now taking 18 digits at a time From Steve I learned we can do 18
  126.     FOR g = 1 TO m
  127.         VB = VAL(MID$(LG$, m * 18 - g * 18 + 1, 18))
  128.         vs = VAL(MID$(sm$, m * 18 - g * 18 + 1, 18))
  129.         IF vs > VB THEN
  130.             t$ = RIGHT$(STRING$(18, "0") + _TRIM$(STR$(tenE18 - vs + VB)), 18)
  131.  
  132.             'debug
  133.             'PRINT VB, tenE18, tenE18 - vs + VB, " t$ = "; t$
  134.  
  135.             ''borrow 1 = rewrite string
  136.             p = (m - g) * 18
  137.             WHILE p > 0 AND MID$(LG$, p, 1) = "0"
  138.                 MID$(LG$, p, 1) = "9"
  139.                 p = p - 1
  140.             WEND
  141.             IF p > 0 THEN MID$(LG$, p, 1) = _TRIM$(STR$(VAL(MID$(LG$, p, 1)) - 1))
  142.         ELSE
  143.             t$ = RIGHT$(STRING$(18, "0") + _TRIM$(STR$(VB - vs)), 18)
  144.         END IF
  145.         result$ = t$ + result$
  146.     NEXT
  147.     result$ = _TRIM$(result$)
  148.     'strip 0
  149.     i = 1: find = 0
  150.     WHILE i < LEN(result$) AND MID$(result$, i, 1) = "0"
  151.         i = i + 1: find = 1
  152.     WEND
  153.     IF find = 1 THEN result$ = MID$(result$, i)
  154.     subtr$ = sign$ + result$
  155.  
  156. FUNCTION add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no spaces or - signs
  157.     DIM la AS INTEGER, lb AS INTEGER, m AS INTEGER, g AS INTEGER, i AS INTEGER, find AS INTEGER
  158.     DIM fa$, fb$, t$, new$, result$
  159.  
  160.     'first thing is to set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
  161.     la = LEN(a$): lb = LEN(b$)
  162.     IF la > lb THEN m = INT(la / 18) + 1 ELSE m = INT(lb / 18) + 1
  163.     fa$ = RIGHT$(STRING$(m * 18, "0") + a$, m * 18)
  164.     fb$ = RIGHT$(STRING$(m * 18, "0") + b$, m * 18)
  165.  
  166.     'now taking 18 digits at a time Thanks Steve McNeill
  167.     FOR g = 1 TO m
  168.         sa = VAL(MID$(fa$, m * 18 - g * 18 + 1, 18))
  169.         sb = VAL(MID$(fb$, m * 18 - g * 18 + 1, 18))
  170.         t$ = RIGHT$(STRING$(36, "0") + _TRIM$(STR$(sa + sb + co)), 36)
  171.         co = VAL(MID$(t$, 1, 18))
  172.         new$ = MID$(t$, 19)
  173.         result$ = new$ + result$
  174.  
  175.         'debug
  176.         'DIM w$
  177.         'PRINT a$, m * 18, sa, sb, t$, co, result$
  178.         'INPUT "OK "; w$ 'OK
  179.     NEXT
  180.     IF co THEN result$ = STR$(co) + result$
  181.     result$ = _TRIM$(result$)
  182.     'strip 0
  183.     i = 1: find = 0
  184.     WHILE i < LEN(result$) AND MID$(result$, i, 1) = "0"
  185.         i = i + 1: find = 1
  186.     WEND
  187.     IF find = 1 THEN result$ = MID$(result$, i)
  188.     add$ = result$
  189.  
  190.  
  191.  
  192.  

@SMcNeill I went with 18 digits at a time with longest string and 1 digit at a time for shorter one. Doing two groups looked hairy as you would have to add$() strings up at least as many times, I think, as going digit by digit.

EDIT: found cool test to add
test$ = mult$(STRING$(50, "9"), STRING$(50, "9")) ' = n-1 nines +8 then n-1 zeros + 1 = 2 * n length
PRINT test$, LEN(test$)
Title: Re: String Math
Post by: bplus on August 15, 2020, 05:12:46 pm
Just thought of a great way to test mult$ function. How about first 500 factorials check?

If the 500th is correct then it is pretty good assumption that all that went before it is correct.
1135 digits all good! And done in a sneeze.
Code: QB64: [Select]
  1. _TITLE "String Math Factorial test" 'b+ started 2020-08-15 using to add$() and mult$ for Factorials
  2. ' 2020-08-14 start with add 2 arbitrary long strings
  3. ' 2020-08-14 add$ posted 5 PM or so
  4. ' 2020-08-14 opt explicit, start subtr$ function
  5. ' 2020-08-15 1 AM subst$ function looking good!
  6. ' 2020-08-15 fix some code from previous subs then attempt mult$ sub
  7. ' 2020-08-15 Factorials! a wonderful test for String Math mult$
  8. ' 2020-08-15 WOW! 500 factorials in an instant and it's matching 500!
  9. '            at  https://www.calculatorsoup.com/calculators/discretemathematics/factorials.php
  10.  
  11. RANDOMIZE TIMER 'now that it's seems to be running silent
  12. SCREEN _NEWIMAGE(1024, 700, 32)
  13. _DELAY .25
  14.  
  15. FactorialSetup
  16.  
  17.  
  18. FUNCTION Factorial$ (n AS INTEGER)
  19.     DIM top AS INTEGER, fline$
  20.     'find the highest factorial we have so far
  21.     OPEN "Factorial Data.txt" FOR INPUT AS #1
  22.     WHILE EOF(1) = 0
  23.         LINE INPUT #1, fline$
  24.         top = top + 1
  25.         IF n = top THEN Factorial$ = rightOf$(fline$, "="): EXIT FUNCTION
  26.     WEND
  27.  
  28.     'finish later
  29.  
  30.  
  31. SUB FactorialSetup
  32.     DIM i AS INTEGER, fac$
  33.     IF _FILEEXISTS("Factorial Data.txt") THEN 'assume its started and correct
  34.         EXIT SUB
  35.     ELSE
  36.         OPEN "Factorial Data.txt" FOR OUTPUT AS #1
  37.         fac$ = "1"
  38.         PRINT #1, "1=1"
  39.         FOR i = 2 TO 500
  40.             fac$ = mult$(_TRIM$(STR$(i)), fac$)
  41.             PRINT #1, _TRIM$(STR$(i)) + "=" + fac$
  42.             PRINT i, fac$
  43.         NEXT
  44.         CLOSE #1
  45.     END IF
  46.  
  47. FUNCTION mult$ (a$, b$) 'assume both positive integers prechecked as all digits strings
  48.     DIM la AS INTEGER, lb AS INTEGER, m AS INTEGER, g AS INTEGER, i AS INTEGER, find AS INTEGER, dp AS INTEGER
  49.     DIM f18$, f1$, t$, build$, accum$
  50.  
  51.     'find the longer number and make it a mult of 18 to take 18 digits at a time from it
  52.     la = LEN(a$): lb = LEN(b$)
  53.     IF la > lb THEN
  54.         m = INT(la / 18) + 1
  55.         f18$ = RIGHT$(STRING$(m * 18, "0") + a$, m * 18)
  56.         f1$ = b$
  57.     ELSE
  58.         m = INT(lb / 18) + 1
  59.         f18$ = RIGHT$(STRING$(m * 18, "0") + b$, m * 18)
  60.         f1$ = a$
  61.     END IF
  62.     FOR dp = LEN(f1$) TO 1 STEP -1 'dp = digit position of the f1$
  63.         build$ = "" 'line builder
  64.         co = 0
  65.         'now taking 18 digits at a time Thanks Steve McNeill
  66.         FOR g = 1 TO m
  67.             v18 = VAL(MID$(f18$, m * 18 - g * 18 + 1, 18))
  68.             sd = VAL(MID$(f1$, dp, 1))
  69.             t$ = RIGHT$(STRING$(19, "0") + _TRIM$(STR$(v18 * sd + co)), 19)
  70.             co = VAL(MID$(t$, 1, 1))
  71.             build$ = MID$(t$, 2) + build$
  72.         NEXT g
  73.         IF co THEN build$ = _TRIM$(STR$(co)) + build$
  74.         IF dp = LEN(f1$) THEN
  75.             accum$ = build$
  76.         ELSE
  77.             accum$ = add$(accum$, build$ + STRING$(LEN(f1$) - dp, "0"))
  78.         END IF
  79.     NEXT dp
  80.     'strip 0
  81.     i = 1: find = 0
  82.     WHILE i < LEN(accum$) AND MID$(accum$, i, 1) = "0"
  83.         i = i + 1: find = 1
  84.     WEND
  85.     IF find = 1 THEN accum$ = MID$(accum$, i)
  86.     mult$ = accum$
  87.  
  88. FUNCTION subtr$ (sum$, minus$) ' assume both numbers are positive all digits
  89.     IF sum$ = minus$ THEN subtr$ = "0": EXIT SUB
  90.  
  91.     DIM ls AS INTEGER, lm AS INTEGER, m AS INTEGER, g AS INTEGER, i AS INTEGER, find AS INTEGER, p AS INTEGER
  92.     DIM sign$, LG$, sm$, t$, result$
  93.  
  94.     tenE18 = 1000000000000000000 'yes!!! no dang E's
  95.     ' which is bigger?
  96.     ls = LEN(sum$): lm = LEN(minus$)
  97.     IF ls > lm THEN
  98.         sign$ = ""
  99.         m = INT(ls / 18) + 1
  100.         LG$ = RIGHT$(STRING$(m * 18, "0") + sum$, m * 18)
  101.         sm$ = RIGHT$(STRING$(m * 18, "0") + minus$, m * 18)
  102.     ELSEIF ls < lm THEN
  103.         sign$ = "-"
  104.         m = INT(lm / 18) + 1
  105.         LG$ = RIGHT$(STRING$(m * 18, "0") + minus$, m * 18)
  106.         sm$ = RIGHT$(STRING$(m * 18, "0") + sum$, m * 18)
  107.     ELSEIF ls = lm THEN
  108.         FOR i = 1 TO LEN(sum$)
  109.             IF VAL(MID$(sum$, i, 1)) > VAL(MID$(minus$, i, 1)) THEN
  110.                 sign$ = ""
  111.                 m = INT(ls / 8) + 1
  112.                 LG$ = RIGHT$(STRING$(m * 18, "0") + sum$, m * 18)
  113.                 sm$ = RIGHT$(STRING$(m * 18, "0") + minus$, m * 18)
  114.                 EXIT FOR
  115.             ELSEIF VAL(MID$(sum$, i, 1)) < VAL(MID$(minus$, i, 1)) THEN
  116.                 sign$ = "-"
  117.                 m = INT(lm / 18) + 1
  118.                 LG$ = RIGHT$(STRING$(m * 18, "0") + minus$, m * 18)
  119.                 sm$ = RIGHT$(STRING$(m * 18, "0") + sum$, m * 18)
  120.                 EXIT FOR
  121.             END IF
  122.         NEXT
  123.     END IF
  124.     'now taking 18 digits at a time From Steve I learned we can do 18
  125.     FOR g = 1 TO m
  126.         VB = VAL(MID$(LG$, m * 18 - g * 18 + 1, 18))
  127.         vs = VAL(MID$(sm$, m * 18 - g * 18 + 1, 18))
  128.         IF vs > VB THEN
  129.             t$ = RIGHT$(STRING$(18, "0") + _TRIM$(STR$(tenE18 - vs + VB)), 18)
  130.  
  131.             'debug
  132.             'PRINT VB, tenE18, tenE18 - vs + VB, " t$ = "; t$
  133.  
  134.             ''borrow 1 = rewrite string
  135.             p = (m - g) * 18
  136.             WHILE p > 0 AND MID$(LG$, p, 1) = "0"
  137.                 MID$(LG$, p, 1) = "9"
  138.                 p = p - 1
  139.             WEND
  140.             IF p > 0 THEN MID$(LG$, p, 1) = _TRIM$(STR$(VAL(MID$(LG$, p, 1)) - 1))
  141.         ELSE
  142.             t$ = RIGHT$(STRING$(18, "0") + _TRIM$(STR$(VB - vs)), 18)
  143.         END IF
  144.         result$ = t$ + result$
  145.     NEXT
  146.     result$ = _TRIM$(result$)
  147.     'strip 0
  148.     i = 1: find = 0
  149.     WHILE i < LEN(result$) AND MID$(result$, i, 1) = "0"
  150.         i = i + 1: find = 1
  151.     WEND
  152.     IF find = 1 THEN result$ = MID$(result$, i)
  153.     subtr$ = sign$ + result$
  154.  
  155. FUNCTION add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no spaces or - signs
  156.     DIM la AS INTEGER, lb AS INTEGER, m AS INTEGER, g AS INTEGER, i AS INTEGER, find AS INTEGER
  157.     DIM fa$, fb$, t$, new$, result$
  158.  
  159.     'first thing is to set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
  160.     la = LEN(a$): lb = LEN(b$)
  161.     IF la > lb THEN m = INT(la / 18) + 1 ELSE m = INT(lb / 18) + 1
  162.     fa$ = RIGHT$(STRING$(m * 18, "0") + a$, m * 18)
  163.     fb$ = RIGHT$(STRING$(m * 18, "0") + b$, m * 18)
  164.  
  165.     'now taking 18 digits at a time Thanks Steve McNeill
  166.     FOR g = 1 TO m
  167.         sa = VAL(MID$(fa$, m * 18 - g * 18 + 1, 18))
  168.         sb = VAL(MID$(fb$, m * 18 - g * 18 + 1, 18))
  169.         t$ = RIGHT$(STRING$(36, "0") + _TRIM$(STR$(sa + sb + co)), 36)
  170.         co = VAL(MID$(t$, 1, 18))
  171.         new$ = MID$(t$, 19)
  172.         result$ = new$ + result$
  173.  
  174.         'debug
  175.         'DIM w$
  176.         'PRINT a$, m * 18, sa, sb, t$, co, result$
  177.         'INPUT "OK "; w$ 'OK
  178.     NEXT
  179.     IF co THEN result$ = STR$(co) + result$
  180.     result$ = _TRIM$(result$)
  181.     'strip 0
  182.     i = 1: find = 0
  183.     WHILE i < LEN(result$) AND MID$(result$, i, 1) = "0"
  184.         i = i + 1: find = 1
  185.     WEND
  186.     IF find = 1 THEN result$ = MID$(result$, i)
  187.     add$ = result$
  188.  
  189. FUNCTION leftOf$ (source$, of$)
  190.     IF INSTR(source$, of$) > 0 THEN leftOf$ = MID$(source$, 1, INSTR(source$, of$) - 1)
  191.  
  192. FUNCTION rightOf$ (source$, of$)
  193.     IF INSTR(source$, of$) > 0 THEN rightOf$ = MID$(source$, INSTR(source$, of$) + LEN(of$))
  194.  
  195.  
  196.  

  [ This attachment cannot be displayed inline in 'Print Page' view ]  
Title: Re: String Math
Post by: bplus on August 16, 2020, 02:48:15 pm
Looks like I should change the code for subtr$() function.
Update, failed:
Code: QB64: [Select]
  1. PRINT "9 minus 3 but 3 is tricky! "; subtr$("9", "00003")
  2.  

Fix one thing and another goes bad:
Code: QB64: [Select]
  1. _TITLE "String Math subtr$" 'b+ started 2020-08-14
  2. ' 2020-08-14 start with add 2 arbitrary long strings
  3. ' 2020-08-14 add$ posted 5 PM or so
  4. ' 2020-08-14 opt explicit, start subtr$ function
  5. ' 2020-08-15 1 AM subst$ function looking good!
  6. ' 2020-08-16 problem found with string compare and QB64 fails compare with longer strings
  7.  
  8. 'RANDOMIZE TIMER 'now that it's seems to be running silent
  9. SCREEN _NEWIMAGE(1024, 700, 32)
  10. _DELAY .25
  11.  
  12. ' special or hard cases found while testing
  13. PRINT subtr$("0", "0")
  14. PRINT subtr$("11111111111111111375", "11111111111111111374")
  15. PRINT subtr$("11111111111111111374", "11111111111111111375")
  16. PRINT "9 minus 3 but 3 is tricky! "; subtr$("9", "00003")
  17. PRINT subtr$("1" + STRING$(50, "0"), "1"), LEN(subtr$("1" + STRING$(50, "0"), "1"))
  18. PRINT subtr$("980", "5")
  19. PRINT " Answer supposed to be 1: "; subtr$("1" + STRING$(99, "0"), STRING$(99, "9"))
  20. PRINT "Answer supposed to be -1: "; subtr$(STRING$(99, "9"), "1" + STRING$(99, "0"))
  21. PRINT "press any for Random testing against QB64 math calculations..."
  22.  
  23. 'random testing
  24. DIM al AS LONG, bl AS LONG, sum AS _INTEGER64, e1 AS INTEGER, e2 AS INTEGER, a$, b$, subtrStr$, qbCalc$
  25.     'pick two numbers
  26.     e1 = INT(10 * RND): e2 = INT(10 * RND)
  27.     al = RND * 10 ^ e1: bl = RND * 10 ^ e2: sum = al - bl
  28.     a$ = _TRIM$(STR$(al)): b$ = _TRIM$(STR$(bl))
  29.     PRINT
  30.     PRINT a$; " minus "; b$; " ="
  31.     subtrStr$ = subtr$(a$, b$)
  32.     PRINT subtrStr$; " according to subtr$ function we are testing."
  33.     qbCalc$ = _TRIM$(STR$(sum))
  34.     PRINT qbCalc$; " according to QB64 math"
  35.     IF qbCalc$ <> subtrStr$ THEN BEEP: SLEEP ' <<<<<<<<<< stop when find an discrepency
  36.     PRINT "Next sum check in 10 secs, or press key,  press escape to quit..."
  37.     SLEEP 10 '
  38.  
  39. FUNCTION subtr$ (sum$, minus$) ' assume both numbers are positive all digits
  40.     IF sum$ = minus$ THEN subtr$ = "0": EXIT SUB
  41.  
  42.     DIM ls AS INTEGER, lm AS INTEGER, m AS INTEGER, g AS INTEGER, i AS INTEGER, find AS INTEGER, p AS INTEGER
  43.     DIM sign$, LG$, sm$, t$, new$, result$
  44.  
  45.     tenE18 = 1000000000000000000 'yes!!! no dang E's
  46.     ' which is bigger?
  47.     ls = LEN(sum$): lm = LEN(minus$)
  48.     IF sum$ > minus$ THEN ' QB64 seems to do great comaparing number strings as if unsigned integers
  49.         sign$ = ""
  50.         m = INT(ls / 18) + 1
  51.         LG$ = RIGHT$(STRING$(m * 18, "0") + sum$, m * 18)
  52.         sm$ = RIGHT$(STRING$(m * 18, "0") + minus$, m * 18)
  53.     ELSE
  54.         sign$ = "-"
  55.         m = INT(lm / 18) + 1
  56.         LG$ = RIGHT$(STRING$(m * 18, "0") + minus$, m * 18)
  57.         sm$ = RIGHT$(STRING$(m * 18, "0") + sum$, m * 18)
  58.     END IF
  59.  
  60.  
  61.     'IF ls > lm THEN
  62.     '    sign$ = ""
  63.     '    m = INT(ls / 18) + 1
  64.     '    LG$ = RIGHT$(STRING$(m * 18, "0") + sum$, m * 18)
  65.     '    sm$ = RIGHT$(STRING$(m * 18, "0") + minus$, m * 18)
  66.     'ELSEIF ls < lm THEN
  67.     '    sign$ = "-"
  68.     '    m = INT(lm / 18) + 1
  69.     '    LG$ = RIGHT$(STRING$(m * 18, "0") + minus$, m * 18)
  70.     '    sm$ = RIGHT$(STRING$(m * 18, "0") + sum$, m * 18)
  71.     'ELSEIF ls = lm THEN
  72.     '    FOR i = 1 TO LEN(sum$)
  73.     '        IF VAL(MID$(sum$, i, 1)) > VAL(MID$(minus$, i, 1)) THEN
  74.     '            sign$ = ""
  75.     '            m = INT(ls / 8) + 1
  76.     '            LG$ = RIGHT$(STRING$(m * 18, "0") + sum$, m * 18)
  77.     '            sm$ = RIGHT$(STRING$(m * 18, "0") + minus$, m * 18)
  78.     '            EXIT FOR
  79.     '        ELSEIF VAL(MID$(sum$, i, 1)) < VAL(MID$(minus$, i, 1)) THEN
  80.     '            sign$ = "-"
  81.     '            m = INT(lm / 18) + 1
  82.     '            LG$ = RIGHT$(STRING$(m * 18, "0") + minus$, m * 18)
  83.     '            sm$ = RIGHT$(STRING$(m * 18, "0") + sum$, m * 18)
  84.     '            EXIT FOR
  85.     '        END IF
  86.     '    NEXT
  87.     'END IF
  88.  
  89.     'now taking 18 digits at a time From Steve I learned we can do more than 1 digit at a time
  90.     FOR g = 1 TO m
  91.         VB = VAL(MID$(LG$, m * 18 - g * 18 + 1, 18))
  92.         vs = VAL(MID$(sm$, m * 18 - g * 18 + 1, 18))
  93.         IF vs > VB THEN
  94.             t$ = RIGHT$(STRING$(18, "0") + _TRIM$(STR$(tenE18 - vs + VB)), 18)
  95.  
  96.             'debug
  97.             'PRINT VB, tenE18, tenE18 - vs + VB, " t$ = "; t$
  98.  
  99.             ''borrow 1 = rewrite string
  100.             p = (m - g) * 18
  101.             WHILE p > 0 AND MID$(LG$, p, 1) = "0"
  102.                 MID$(LG$, p, 1) = "9"
  103.                 p = p - 1
  104.             WEND
  105.             IF p > 0 THEN MID$(LG$, p, 1) = _TRIM$(STR$(VAL(MID$(LG$, p, 1)) - 1))
  106.         ELSE
  107.             t$ = RIGHT$(STRING$(18, "0") + _TRIM$(STR$(VB - vs)), 18)
  108.         END IF
  109.         result$ = t$ + result$
  110.     NEXT
  111.     result$ = _TRIM$(result$)
  112.     'strip 0
  113.     i = 1: find = 0
  114.     WHILE i < LEN(result$) AND MID$(result$, i, 1) = "0"
  115.         i = i + 1: find = 1
  116.     WEND
  117.     IF find = 1 THEN result$ = MID$(result$, i)
  118.     subtr$ = sign$ + result$
  119.  
  120. FUNCTION add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no spaces or - signs
  121.     'first thing is to set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
  122.     DIM la AS INTEGER, lb AS INTEGER, m AS INTEGER, g AS INTEGER, i AS INTEGER, find AS INTEGER
  123.     DIM fa$, fb$, t$, new$, result$
  124.     la = LEN(a$): lb = LEN(b$)
  125.     IF la > lb THEN m = INT(la / 18) + 1 ELSE m = INT(lb / 18) + 1
  126.     fa$ = RIGHT$(STRING$(m * 18, "0") + a$, m * 18)
  127.     fb$ = RIGHT$(STRING$(m * 18, "0") + b$, m * 18)
  128.  
  129.     'now taking 18 digits at a time Thanks Steve McNeill
  130.     FOR g = 1 TO m
  131.         sa = VAL(MID$(fa$, m * 18 - g * 18 + 1, 18))
  132.         sb = VAL(MID$(fb$, m * 18 - g * 18 + 1, 18))
  133.         t$ = RIGHT$(STRING$(36, "0") + _TRIM$(STR$(sa + sb + co)), 36)
  134.         co = VAL(MID$(t$, 1, 18))
  135.         new$ = MID$(t$, 19)
  136.         result$ = new$ + result$
  137.  
  138.         'debug
  139.         'DIM w$
  140.         'PRINT a$, m * 18, sa, sb, t$, co, result$
  141.         'INPUT "OK "; w$ 'OK
  142.     NEXT
  143.     IF co THEN result$ = STR$(co) + result$
  144.     result$ = _TRIM$(result$)
  145.     'strip 0
  146.     i = 1: find = 0
  147.     WHILE i < LEN(result$) AND MID$(result$, i, 1) = "0"
  148.         i = i + 1: find = 1
  149.     WEND
  150.     IF find = 1 THEN result$ = MID$(result$, i)
  151.     add$ = result$
  152.  

OK guess I have to roll my own.
Title: Re: String Math
Post by: SMcNeill on August 16, 2020, 04:11:00 pm
You need a sign checker routine in your subs.   (Cavet: I've been taking care of mama for the last several days and haven't looked at your code closely yet, so you may have the following already:)

First, a function to deal with doubles.  FUNCTION DWD (text$) searches text$ and converts double symbols to reverse symbols.  ++ becomes +, -- becomes +, -+ becomes -, +- becomes -.   

For example, 3 + -2 (Three plus negative two) becomes 3 - 2.

Then in your routines, check to make certain that your two values have the same sign.

-3 -4 -- this is addition, as the two values are both negative.  Just kick it from your subtraction routine to your addition routine and return the result from there.
-3 + 4 -- this is subtraction, even though the symbol between them in +.  Once again, just kick it to the appropriate function and return the result from there.

And a routine to convert scientific notion to string is nice as well, so you can add 3E+20 to 1234567899012345678987654321.

If you look in QB64.BAS, you'll find most of these little helper routines already worked up and in use inside there, as they're part of the CONST calculations we do.  (Also in my math evaluator, which works with strings as well.)
Title: Re: String Math
Post by: bplus on August 16, 2020, 04:28:09 pm
Hi Steve,

Yeah I am taking care of mom too (89), she can't remember anything new and plagued with aches and pains but old memories are hanging in there, she does crossword puzzles every day.

I am saving signs for later as well as decimal point handling. My first goal is the 4 main arithmetic subs going for arbitrary long positive integers. I save the best for last Division, then signs, then decimals...

I know I am reinventing the wheel but you learn inside stuff with DIY. Then if you have a bug or want to handle decimals different you know (maybe if you remember) how to get what you want without having to learn the whole thing anyway using someone else approach. Of course, someone else approach could be much more clever but how do you know if you didn't try it yourself?

I am working on 2 helper functions now and rewriting add$, subtr$, mult$ with lines of code savings! If things work out ;-))
Title: Re: String Math
Post by: bplus on August 16, 2020, 09:57:24 pm
I think I might have a unique approach for division, preliminary results are looking really good!

I am intending to use multiplicative inverses, ie for n / d, use n * 1/d.
Title: Re: String Math
Post by: bplus on August 17, 2020, 01:25:10 am
Getting divide$() from Multiplicative Inverse worked like a charm for getting nice set of digits but getting the decimal settled at right point (was that a pun?) took a little more doing including not stripping 0's until last, display, step.

Code: QB64: [Select]
  1. _TITLE "Multplicative Inverse: nInverse$() and divide$()" ' b+ 2020-08-16 from
  2. ' I always want to try doing division by multipling the numerator
  3. ' by the multiplicative inverse of denominator.
  4. ' n/d = n * 1/d so how bout a function that does 1/n
  5. ' Then divide$() is pretty easy with mult$
  6.  
  7. RANDOMIZE TIMER 'now that it's seems to be running silent
  8. SCREEN _NEWIMAGE(1200, 700, 32)
  9. _DELAY .25
  10.  
  11. 'special cases or hard cases
  12. PRINT nInverse$("0", 100)
  13. PRINT nInverse$("1", 100)
  14. PRINT nInverse$("2", 100) 'yeah this might work!
  15. PRINT nInverse$("7", 150)
  16. PRINT nInverse$("50", 150) ' good
  17. PRINT nInverse$("999999999", 150) ' oh boy!!!
  18. PRINT nInverse$("11111111111111111111", 150)
  19. PRINT nInverse$("1024", 150)
  20. PRINT nInverse$("717", 150)
  21. PRINT "  OK try some Divisions "
  22. PRINT ".5 ? "; divide$("1", "2") 'well that took some tuning
  23. PRINT ".125 ? "; divide$("1", "8") ' well more tuning!
  24. PRINT ".002 ? "; divide$("1", "500")
  25. PRINT "1.714285R714285... ? "; divide$("12", "7")
  26. PRINT "15555 ? "; divide$("108885", "7") 'close to 15555
  27. PRINT "2048 ? "; divide$("131072", "64") 'powers of 2 here 2048
  28. PRINT: PRINT " press any for some random tests comparing divisions to QB64 values"
  29.  
  30. 'random testing
  31. DIM quotient AS DOUBLE, e1 AS INTEGER, e2 AS INTEGER, num$, den$, divStr$, qbCalc$
  32.     'pick two numbers
  33.     e1 = INT(10 * RND): e2 = INT(10 * RND)
  34.     n = RND * 10 ^ e1: d = RND * 10 ^ e2: quotient = n / d
  35.     num$ = _TRIM$(STR$(n)): den$ = _TRIM$(STR$(d))
  36.     PRINT
  37.     PRINT num$; " divided by "; den$; " ="
  38.     divStr$ = divide$(num$, den$)
  39.     PRINT divStr$; " according to mult$ function we are testing."
  40.     qbCalc$ = _TRIM$(STR$(quotient))
  41.     PRINT qbCalc$; " according to QB64 math"
  42.     ' <<<<<<<<<< stop when find an discrepency
  43.     PRINT "Next divide$() check in 10 secs, or press key,  press escape to quit..."
  44.     SLEEP 10
  45.  
  46. FUNCTION divide$ (n$, d$)
  47.     DIM di$, ndi$, nD AS INTEGER
  48.     IF trim0$(n$) = "0" THEN divide$ = "0": EXIT FUNCTION
  49.     IF trim0$(d$) = "0" THEN divide$ = "div 0": EXIT FUNCTION
  50.     IF trim0$(d$) = "1" THEN divide$ = n$: EXIT FUNCTION
  51.     di$ = nInverse$(d$, 100)
  52.     nD = LEN(di$)
  53.     ndi$ = mult$(n$, di$)
  54.     ndi$ = MID$(ndi$, 1, LEN(ndi$) - nD) + "." + RIGHT$(ndi$, nD)
  55.     divide$ = trim0$(ndi$)
  56.  
  57. FUNCTION nInverse$ (n$, DP AS INTEGER) 'assume decimal at very start of the string of digits returned, no rounding
  58.     DIM m$(1 TO 9), si$, r$, outstr$, d$
  59.     DIM i AS INTEGER
  60.     FOR i = 1 TO 9
  61.         si$ = _TRIM$(STR$(i))
  62.         m$(i) = mult$(si$, n$)
  63.     NEXT
  64.     outstr$ = ""
  65.     IF n$ = "0" THEN nInverse$ = "Div 0": EXIT FUNCTION
  66.     IF n$ = "1" THEN nInverse$ = "1": EXIT FUNCTION
  67.     r$ = "10"
  68.     DO
  69.         WHILE LEFT$(subtr$(r$, n$), 1) = "-" '   r - n < 0
  70.             outstr$ = outstr$ + "0" '            add 0 to the  output string
  71.             IF LEN(outstr$) = DP THEN nInverse$ = outstr$: EXIT FUNCTION 'check if we've reached DP length
  72.             r$ = r$ + "0"
  73.         WEND
  74.         FOR i = 9 TO 1 STEP -1
  75.             IF LTE(m$(i), r$) THEN d$ = _TRIM$(STR$(i)): EXIT FOR
  76.         NEXT
  77.         outstr$ = outstr$ + d$
  78.         IF LEN(outstr$) = DP THEN nInverse$ = outstr$: EXIT FUNCTION
  79.         r$ = subtr$(r$, mult$(d$, n$)) 'r = r -d*n
  80.         IF r$ = "0" THEN nInverse$ = outstr$: EXIT FUNCTION 'found a perfect divisor
  81.         r$ = r$ + "0" 'add another place
  82.     LOOP
  83.  
  84. FUNCTION mult$ (a$, b$) 'assume both positive integers prechecked as all digits strings
  85.     DIM la AS INTEGER, lb AS INTEGER, m AS INTEGER, g AS INTEGER, dp AS INTEGER
  86.     DIM f18$, f1$, t$, build$, accum$
  87.  
  88.     IF trim0$(a$) = "0" THEN mult$ = "0": EXIT FUNCTION
  89.     IF trim0$(b$) = "0" THEN mult$ = "0": EXIT FUNCTION
  90.     IF trim0$(a$) = "1" THEN mult$ = trim0$(b$): EXIT FUNCTION
  91.     IF trim0$(b$) = "1" THEN mult$ = trim0$(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 * 18 - 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. FUNCTION subtr$ (sum$, minus$) ' assume both numbers are positive all digits
  124.     DIM m AS INTEGER, g AS INTEGER, p AS INTEGER
  125.     DIM ts$, tm$, sign$, LG$, sm$, t$, result$
  126.  
  127.     ts$ = TrimLead0$(sum$): tm$ = TrimLead0$(minus$)
  128.     IF trim0(ts$) = trim0$(tm$) THEN subtr$ = "0": EXIT FUNCTION 'OK proceed with function knowing they are not equal
  129.     tenE18 = 1000000000000000000 'yes!!! no dang E's
  130.     IF LTE(ts$, tm$) THEN ' which is bigger? minus is bigger
  131.         sign$ = "-"
  132.         m = INT(LEN(tm$) / 18) + 1
  133.         LG$ = RIGHT$(STRING$(m * 18, "0") + tm$, m * 18)
  134.         sm$ = RIGHT$(STRING$(m * 18, "0") + ts$, m * 18)
  135.     ELSE 'sum is bigger
  136.         sign$ = ""
  137.         m = INT(LEN(ts$) / 18) + 1
  138.         LG$ = RIGHT$(STRING$(m * 18, "0") + ts$, m * 18)
  139.         sm$ = RIGHT$(STRING$(m * 18, "0") + tm$, m * 18)
  140.     END IF
  141.     'now taking 18 digits at a time From Steve I learned we can do more than 1 digit at a time
  142.     FOR g = 1 TO m
  143.         VB = VAL(MID$(LG$, m * 18 - g * 18 + 1, 18))
  144.         vs = VAL(MID$(sm$, m * 18 - g * 18 + 1, 18))
  145.         IF vs > VB THEN
  146.             t$ = RIGHT$(STRING$(18, "0") + _TRIM$(STR$(tenE18 - vs + VB)), 18)
  147.  
  148.             'debug
  149.             'PRINT VB, tenE18, tenE18 - vs + VB, " t$ = "; t$
  150.  
  151.             ''borrow 1 = rewrite string
  152.             p = (m - g) * 18
  153.             WHILE p > 0 AND MID$(LG$, p, 1) = "0"
  154.                 MID$(LG$, p, 1) = "9"
  155.                 p = p - 1
  156.             WEND
  157.             IF p > 0 THEN MID$(LG$, p, 1) = _TRIM$(STR$(VAL(MID$(LG$, p, 1)) - 1))
  158.         ELSE
  159.             t$ = RIGHT$(STRING$(18, "0") + _TRIM$(STR$(VB - vs)), 18)
  160.         END IF
  161.         result$ = t$ + result$
  162.     NEXT
  163.     subtr$ = sign$ + result$
  164.  
  165. FUNCTION add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no spaces or - signs
  166.     'first thing is to set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
  167.     DIM la AS INTEGER, lb AS INTEGER, m AS INTEGER, g AS INTEGER
  168.     DIM fa$, fb$, t$, new$, result$
  169.     la = LEN(a$): lb = LEN(b$)
  170.     IF la > lb THEN m = INT(la / 18) + 1 ELSE m = INT(lb / 18) + 1
  171.     fa$ = RIGHT$(STRING$(m * 18, "0") + a$, m * 18)
  172.     fb$ = RIGHT$(STRING$(m * 18, "0") + b$, m * 18)
  173.  
  174.     'now taking 18 digits at a time Thanks Steve McNeill
  175.     FOR g = 1 TO m
  176.         sa = VAL(MID$(fa$, m * 18 - g * 18 + 1, 18))
  177.         sb = VAL(MID$(fb$, m * 18 - g * 18 + 1, 18))
  178.         t$ = RIGHT$(STRING$(36, "0") + _TRIM$(STR$(sa + sb + co)), 36)
  179.         co = VAL(MID$(t$, 1, 18))
  180.         new$ = MID$(t$, 19)
  181.         result$ = new$ + result$
  182.  
  183.         'debug
  184.         'DIM w$
  185.         'PRINT a$, m * 18, sa, sb, t$, co, result$
  186.         'INPUT "OK "; w$ 'OK
  187.     NEXT
  188.     IF co THEN result$ = STR$(co) + result$
  189.     add$ = result$
  190.  
  191. ' String Math Helpers -----------------------------------------------
  192.  
  193. 'this function needs TrimLead0$(s$)
  194. FUNCTION LTE (a$, b$) ' a$ Less Than or Equal b$  comparison of 2 strings
  195.     DIM ca$, cb$, la AS INTEGER, lb AS INTEGER, i AS INTEGER
  196.     ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
  197.     la = LEN(ca$): lb = LEN(cb$)
  198.     IF ca$ = cb$ THEN
  199.         LTE = -1
  200.     ELSEIF la < lb THEN ' a is smaller
  201.         LTE = -1
  202.     ELSEIF la > lb THEN ' a is bigger
  203.         LTE = 0
  204.     ELSEIF la = lb THEN ' equal lengths
  205.         FOR i = 1 TO LEN(ca$)
  206.             IF VAL(MID$(ca$, i, 1)) > VAL(MID$(cb$, i, 1)) THEN
  207.                 LTE = 0: EXIT FUNCTION
  208.             ELSEIF VAL(MID$(ca$, i, 1)) < VAL(MID$(cb$, i, 1)) THEN
  209.                 LTE = -1: EXIT FUNCTION
  210.             END IF
  211.         NEXT
  212.     END IF
  213.  
  214. ' ------------------------------------- use these for final display
  215.  
  216. FUNCTION TrimLead0$ (s$) 'for treating strings as number (pos integers)
  217.     DIM copys$, i AS INTEGER, find AS INTEGER
  218.     copys$ = _TRIM$(s$) 'might as well remove spaces too
  219.     i = 1: find = 0
  220.     WHILE i < LEN(copys$) AND MID$(copys$, i, 1) = "0"
  221.         i = i + 1: find = 1
  222.     WEND
  223.     IF find = 1 THEN copys$ = MID$(copys$, i)
  224.     IF copys$ = "" THEN TrimLead0$ = "0" ELSE TrimLead0$ = copys$
  225.  
  226. FUNCTION TrimTail0$ (s$)
  227.     DIM copys$, dp AS INTEGER, i AS INTEGER, find AS INTEGER
  228.     copys$ = _TRIM$(s$) 'might as well remove spaces too
  229.     TrimTail0$ = copys$
  230.     dp = INSTR(copys$, ".")
  231.     IF dp > 0 THEN
  232.         i = LEN(copys$): find = 0
  233.         WHILE i > dp AND MID$(copys$, i, 1) = "0"
  234.             i = i - 1: find = 1
  235.         WEND
  236.         IF find = 1 THEN
  237.             IF i = dp THEN
  238.                 TrimTail0$ = MID$(copys$, 1, dp - 1)
  239.             ELSE
  240.                 TrimTail0$ = MID$(copys$, 1, i)
  241.             END IF
  242.         END IF
  243.     END IF
  244.  
  245. FUNCTION trim0$ (s$)
  246.     DIM t$
  247.     t$ = TrimLead0$(s$)
  248.     trim0$ = TrimTail0$(t$)
  249.  
Title: Re: String Math
Post by: STxAxTIC on August 17, 2020, 03:16:17 am
Here's a test for ya from http://wfbarnes.net/sxript/docs/fibonacci2/main.php#Top (http://wfbarnes.net/sxript/docs/fibonacci2/main.php#Top)

Quote
It turns out that the first hundred (or so) Fibonacci Numbers can be produced by inverting the integer 999999999999999999999998999999999999999999999999 (bonus points for noticing that one of the digits is not a 9).

Code: [Select]
print_
  largediv(
    1.(for(<i,1,2495,1>,{0})),
    999999999999999999999998999999999999999999999999
  )

Code: [Select]
+0.000000000000000000000000000000000000000000000001000000000000000000000001000000000000000000000002000000000000000000000003000000000000000000000005000000000000000000000008000000000000000000000013000000000000000000000021000000000000000000000034000000000000000000000055000000000000000000000089000000000000000000000144000000000000000000000233000000000000000000000377000000000000000000000610000000000000000000000987000000000000000000001597000000000000000000002584000000000000000000004181000000000000000000006765000000000000000000010946000000000000000000017711000000000000000000028657000000000000000000046368000000000000000000075025000000000000000000121393000000000000000000196418000000000000000000317811000000000000000000514229000000000000000000832040000000000000000001346269000000000000000002178309000000000000000003524578000000000000000005702887000000000000000009227465000000000000000014930352000000000000000024157817000000000000000039088169000000000000000063245986000000000000000102334155000000000000000165580141000000000000000267914296000000000000000433494437000000000000000701408733000000000000001134903170000000000000001836311903000000000000002971215073000000000000004807526976000000000000007778742049000000000000012586269025000000000000020365011074000000000000032951280099000000000000053316291173000000000000086267571272000000000000139583862445000000000000225851433717000000000000365435296162000000000000591286729879000000000000956722026041000000000001548008755920000000000002504730781961000000000004052739537881000000000006557470319842000000000010610209857723000000000017167680177565000000000027777890035288000000000044945570212853000000000072723460248141000000000117669030460994000000000190392490709135000000000308061521170129000000000498454011879264000000000806515533049393000000001304969544928657000000002111485077978050000000003416454622906707000000005527939700884757000000008944394323791464000000014472334024676221000000023416728348467685000000037889062373143906000000061305790721611591000000099194853094755497000000160500643816367088000000259695496911122585000000420196140727489673000000679891637638612258000001100087778366101931000001779979416004714189000002880067194370816120000004660046610375530309000007540113804746346429000012200160415121876738000019740274219868223167000031940434634990099905000051680708854858323072000083621143489848422977000135301852344706746049000218922995834555169026000354224848179261915075000573147844013817084101000927372692193078999176001500520536206896083277002427893228399975082453003928413764606871165730006356306993006846248183010284720757613717413913
Title: Re: String Math
Post by: bplus on August 17, 2020, 09:20:09 am
Well there is an interesting experiment! Thanx STx :)

The first order of business today was fixing the nInverse function with a decimal point because I need to return 1 (without decimal) when n = 1

2nd thing is rounding when I get a repeating stream of 9's for divide, usually it means it should be infinitesimally small nudge more.
Title: Re: String Math
Post by: bplus on August 17, 2020, 11:56:07 am
Some nice results this morning, tweaking last nights nInverse$(), divide$, adding showDP$() for rounding at DP + 1 if have that many decimals.

Code: QB64: [Select]
  1. _TITLE "Multplicative Inverse: nInverse$() and divide$()" ' b+ 2020-08-16
  2. ' 2020-08-16 picking up from Sting Math add$(), subtr$(), mult$() onto divide$()
  3. ' I always wanted to try doing division by multipling the numerator
  4. ' by the multiplicative inverse of denominator.
  5. ' n/d = n * 1/d so how bout a function that does 1/n.
  6. ' Then divide$() is pretty easy with mult$  Yes works but need more decimal work.
  7. ' 2020-08-17 redo all this with a decimal point for nInverse just start outstr with "." instead ""
  8. ' Now somethings off with divide$() first is 1 / 500 should be .002 returning .2, debug."
  9. ' Much better than post of last night
  10. ' showDP rounds up to DP (Decimal Places IF there are more decimals past DP)
  11.  
  12. RANDOMIZE TIMER 'now that it's seems to be running silent
  13. SCREEN _NEWIMAGE(1200, 700, 32)
  14. _DELAY .25
  15.  
  16. 'special cases or hard cases
  17. PRINT "div 0 ? "; nInverse$("0", 100)
  18. PRINT "This is 2nd special case where no decimal is returned: 1 ? "; nInverse$("1", 100)
  19. PRINT "1/2 =.5 no more no less ? "; nInverse$("2", 100) 'yeah this might work!
  20. PRINT ".142857 and repeat forever ? for 1/7"
  21. PRINT nInverse$("7", 149)
  22. PRINT ".02 no more or less ? "; nInverse$("50", 149) ' good
  23. PRINT "1/ (9 nines) should b"
  24. PRINT ".000000001 repeated forever ?"
  25. PRINT nInverse$("999999999", 149) ' oh boy!!!
  26. PRINT "1/ (20 ones) should be 19 zeros nine repeated forever ?"
  27. PRINT ".00000000000000000009 repeat forever ?"
  28. PRINT nInverse$("11111111111111111111", 149)
  29. PRINT ".0009765625 ?"
  30. PRINT nInverse$("1024", 150)
  31. PRINT ".0013947 repeat forever ?"
  32. PRINT nInverse$("717", 150)
  33. PRINT "  OK try some Divisions "
  34. PRINT ".5 ? "; divide$("1", "2") 'well that took some tuning
  35. PRINT ".125 ? "; divide$("1", "8") ' well more tuning!
  36. PRINT ".002 ? "; divide$("1", "500") 'lost first 2 zeros?
  37. PRINT ".002222...? "; divide$("1", "450")
  38. PRINT "1.002222...? "; divide$("451", "450")
  39. PRINT "1.714285R714285... ? "; divide$("12", "7")
  40. PRINT "15555 ? "; divide$("108885", "7") 'close to 15555
  41. PRINT "test showDP fix 9's to 4 places?"
  42. PRINT "15555 ? "; showDP(divide$("108885", "7"), 4)
  43. PRINT "2048 ? "; divide$("131072", "64") 'powers of 2 here 2048
  44. PRINT "1 ? "; showDP(divide$("99999", "100000"), 2)
  45. PRINT: PRINT " press any for some random tests comparing divisions to QB64 values"
  46.  
  47. 'random testing
  48. DIM quotient AS DOUBLE, e1 AS INTEGER, e2 AS INTEGER, num$, den$, divStr$, qbCalc$
  49.     'pick two numbers
  50.     e1 = INT(10 * RND): e2 = INT(10 * RND)
  51.     n = RND * 10 ^ e1: d = RND * 10 ^ e2: quotient = n / d
  52.     num$ = _TRIM$(STR$(n)): den$ = _TRIM$(STR$(d))
  53.     PRINT
  54.     PRINT num$; " divided by "; den$; " ="
  55.     divStr$ = divide$(num$, den$)
  56.     PRINT showDP$(divStr$, 60); " LEN:"; LEN(showDP$(divStr$, 60)); " divide$() trunc. 60"
  57.     qbCalc$ = _TRIM$(STR$(quotient))
  58.     PRINT qbCalc$; " according to QB64 math"
  59.     ' <<<<<<<<<< stop when find an discrepency
  60.     PRINT "Next divide$() check in 10 secs, or press key,  press escape to quit..."
  61.     SLEEP 10
  62.  
  63. FUNCTION divide$ (n$, d$)
  64.     DIM di$, ndi$, nD AS INTEGER
  65.     IF trim0$(n$) = "0" THEN divide$ = "0": EXIT FUNCTION
  66.     IF trim0$(d$) = "0" THEN divide$ = "div 0": EXIT FUNCTION
  67.     IF trim0$(d$) = "1" THEN divide$ = trim0$(n$): EXIT FUNCTION '8/17 add trim0$
  68.     di$ = MID$(nInverse$(d$, 100), 2) 'chop off decimal point after
  69.     nD = LEN(di$)
  70.     ndi$ = mult$(n$, di$)
  71.     ndi$ = MID$(ndi$, 1, LEN(ndi$) - nD) + "." + RIGHT$(STRING$(nD, "0") + RIGHT$(ndi$, nD), nD)
  72.     divide$ = trim0$(ndi$)
  73.  
  74. FUNCTION nInverse$ (n$, DP AS INTEGER) 'assume decimal at very start of the string of digits returned, no rounding
  75.     DIM m$(1 TO 9), si$, r$, outstr$, d$
  76.     DIM i AS INTEGER
  77.     FOR i = 1 TO 9
  78.         si$ = _TRIM$(STR$(i))
  79.         m$(i) = mult$(si$, n$)
  80.     NEXT
  81.     outstr$ = ""
  82.     IF n$ = "0" THEN nInverse$ = "Div 0": EXIT FUNCTION
  83.     IF n$ = "1" THEN nInverse$ = "1": EXIT FUNCTION
  84.     outstr$ = "." 'everything else n > 1 is decimal 8/17
  85.     r$ = "10"
  86.     DO
  87.         WHILE LEFT$(subtr$(r$, n$), 1) = "-" '   r - n < 0
  88.             outstr$ = outstr$ + "0" '            add 0 to the  output string
  89.             IF LEN(outstr$) = DP THEN nInverse$ = outstr$: EXIT FUNCTION 'check if we've reached DP length
  90.             r$ = r$ + "0"
  91.         WEND
  92.         FOR i = 9 TO 1 STEP -1
  93.             IF LTE(m$(i), r$) THEN d$ = _TRIM$(STR$(i)): EXIT FOR
  94.         NEXT
  95.         outstr$ = outstr$ + d$
  96.         IF LEN(outstr$) = DP THEN nInverse$ = outstr$: EXIT FUNCTION
  97.         r$ = subtr$(r$, mult$(d$, n$)) 'r = r -d*n
  98.         IF r$ = "0" THEN nInverse$ = outstr$: EXIT FUNCTION 'found a perfect divisor
  99.         r$ = r$ + "0" 'add another place
  100.     LOOP
  101.  
  102. FUNCTION mult$ (a$, b$) 'assume both positive integers prechecked as all digits strings
  103.     DIM la AS INTEGER, lb AS INTEGER, m AS INTEGER, g AS INTEGER, dp AS INTEGER
  104.     DIM f18$, f1$, t$, build$, accum$
  105.  
  106.     IF trim0$(a$) = "0" THEN mult$ = "0": EXIT FUNCTION
  107.     IF trim0$(b$) = "0" THEN mult$ = "0": EXIT FUNCTION
  108.     IF trim0$(a$) = "1" THEN mult$ = trim0$(b$): EXIT FUNCTION
  109.     IF trim0$(b$) = "1" THEN mult$ = trim0$(a$): EXIT FUNCTION
  110.     'find the longer number and make it a mult of 18 to take 18 digits at a time from it
  111.     la = LEN(a$): lb = LEN(b$)
  112.     IF la > lb THEN
  113.         m = INT(la / 18) + 1
  114.         f18$ = RIGHT$(STRING$(m * 18, "0") + a$, m * 18)
  115.         f1$ = b$
  116.     ELSE
  117.         m = INT(lb / 18) + 1
  118.         f18$ = RIGHT$(STRING$(m * 18, "0") + b$, m * 18)
  119.         f1$ = a$
  120.     END IF
  121.     FOR dp = LEN(f1$) TO 1 STEP -1 'dp = digit position of the f1$
  122.         build$ = "" 'line builder
  123.         co = 0
  124.         'now taking 18 digits at a time Thanks Steve McNeill
  125.         FOR g = 1 TO m
  126.             v18 = VAL(MID$(f18$, m * 18 - g * 18 + 1, 18))
  127.             sd = VAL(MID$(f1$, dp, 1))
  128.             t$ = RIGHT$(STRING$(19, "0") + _TRIM$(STR$(v18 * sd + co)), 19)
  129.             co = VAL(MID$(t$, 1, 1))
  130.             build$ = MID$(t$, 2) + build$
  131.         NEXT g
  132.         IF co THEN build$ = _TRIM$(STR$(co)) + build$
  133.         IF dp = LEN(f1$) THEN
  134.             accum$ = build$
  135.         ELSE
  136.             accum$ = add$(accum$, build$ + STRING$(LEN(f1$) - dp, "0"))
  137.         END IF
  138.     NEXT dp
  139.     mult$ = accum$
  140.  
  141. FUNCTION subtr$ (sum$, minus$) ' assume both numbers are positive all digits
  142.     DIM m AS INTEGER, g AS INTEGER, p AS INTEGER
  143.     DIM ts$, tm$, sign$, LG$, sm$, t$, result$
  144.  
  145.     ts$ = TrimLead0$(sum$): tm$ = TrimLead0$(minus$)
  146.     IF trim0(ts$) = trim0$(tm$) THEN subtr$ = "0": EXIT FUNCTION 'OK proceed with function knowing they are not equal
  147.     tenE18 = 1000000000000000000 'yes!!! no dang E's
  148.     IF LTE(ts$, tm$) THEN ' which is bigger? minus is bigger
  149.         sign$ = "-"
  150.         m = INT(LEN(tm$) / 18) + 1
  151.         LG$ = RIGHT$(STRING$(m * 18, "0") + tm$, m * 18)
  152.         sm$ = RIGHT$(STRING$(m * 18, "0") + ts$, m * 18)
  153.     ELSE 'sum is bigger
  154.         sign$ = ""
  155.         m = INT(LEN(ts$) / 18) + 1
  156.         LG$ = RIGHT$(STRING$(m * 18, "0") + ts$, m * 18)
  157.         sm$ = RIGHT$(STRING$(m * 18, "0") + tm$, m * 18)
  158.     END IF
  159.     'now taking 18 digits at a time From Steve I learned we can do more than 1 digit at a time
  160.     FOR g = 1 TO m
  161.         VB = VAL(MID$(LG$, m * 18 - g * 18 + 1, 18))
  162.         vs = VAL(MID$(sm$, m * 18 - g * 18 + 1, 18))
  163.         IF vs > VB THEN
  164.             t$ = RIGHT$(STRING$(18, "0") + _TRIM$(STR$(tenE18 - vs + VB)), 18)
  165.  
  166.             'debug
  167.             'PRINT VB, tenE18, tenE18 - vs + VB, " t$ = "; t$
  168.  
  169.             ''borrow 1 = rewrite string
  170.             p = (m - g) * 18
  171.             WHILE p > 0 AND MID$(LG$, p, 1) = "0"
  172.                 MID$(LG$, p, 1) = "9"
  173.                 p = p - 1
  174.             WEND
  175.             IF p > 0 THEN MID$(LG$, p, 1) = _TRIM$(STR$(VAL(MID$(LG$, p, 1)) - 1))
  176.         ELSE
  177.             t$ = RIGHT$(STRING$(18, "0") + _TRIM$(STR$(VB - vs)), 18)
  178.         END IF
  179.         result$ = t$ + result$
  180.     NEXT
  181.     subtr$ = sign$ + result$
  182.  
  183. FUNCTION add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no spaces or - signs
  184.     'first thing is to set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
  185.     DIM la AS INTEGER, lb AS INTEGER, m AS INTEGER, g AS INTEGER
  186.     DIM fa$, fb$, t$, new$, result$
  187.     la = LEN(a$): lb = LEN(b$)
  188.     IF la > lb THEN m = INT(la / 18) + 1 ELSE m = INT(lb / 18) + 1
  189.     fa$ = RIGHT$(STRING$(m * 18, "0") + a$, m * 18)
  190.     fb$ = RIGHT$(STRING$(m * 18, "0") + b$, m * 18)
  191.  
  192.     'now taking 18 digits at a time Thanks Steve McNeill
  193.     FOR g = 1 TO m
  194.         sa = VAL(MID$(fa$, m * 18 - g * 18 + 1, 18))
  195.         sb = VAL(MID$(fb$, m * 18 - g * 18 + 1, 18))
  196.         t$ = RIGHT$(STRING$(36, "0") + _TRIM$(STR$(sa + sb + co)), 36)
  197.         co = VAL(MID$(t$, 1, 18))
  198.         new$ = MID$(t$, 19)
  199.         result$ = new$ + result$
  200.  
  201.         'debug
  202.         'DIM w$
  203.         'PRINT a$, m * 18, sa, sb, t$, co, result$
  204.         'INPUT "OK "; w$ 'OK
  205.     NEXT
  206.     IF co THEN result$ = STR$(co) + result$
  207.     add$ = result$
  208.  
  209. ' String Math Helpers -----------------------------------------------
  210.  
  211. 'this function needs TrimLead0$(s$)
  212. FUNCTION LTE (a$, b$) ' a$ Less Than or Equal b$  comparison of 2 strings
  213.     DIM ca$, cb$, la AS INTEGER, lb AS INTEGER, i AS INTEGER
  214.     ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
  215.     la = LEN(ca$): lb = LEN(cb$)
  216.     IF ca$ = cb$ THEN
  217.         LTE = -1
  218.     ELSEIF la < lb THEN ' a is smaller
  219.         LTE = -1
  220.     ELSEIF la > lb THEN ' a is bigger
  221.         LTE = 0
  222.     ELSEIF la = lb THEN ' equal lengths
  223.         FOR i = 1 TO LEN(ca$)
  224.             IF VAL(MID$(ca$, i, 1)) > VAL(MID$(cb$, i, 1)) THEN
  225.                 LTE = 0: EXIT FUNCTION
  226.             ELSEIF VAL(MID$(ca$, i, 1)) < VAL(MID$(cb$, i, 1)) THEN
  227.                 LTE = -1: EXIT FUNCTION
  228.             END IF
  229.         NEXT
  230.     END IF
  231.  
  232. ' ------------------------------------- use these for final display
  233.  
  234. FUNCTION TrimLead0$ (s$) 'for treating strings as number (pos integers)
  235.     DIM copys$, i AS INTEGER, find AS INTEGER
  236.     copys$ = _TRIM$(s$) 'might as well remove spaces too
  237.     i = 1: find = 0
  238.     WHILE i < LEN(copys$) AND MID$(copys$, i, 1) = "0"
  239.         i = i + 1: find = 1
  240.     WEND
  241.     IF find = 1 THEN copys$ = MID$(copys$, i)
  242.     IF copys$ = "" THEN TrimLead0$ = "0" ELSE TrimLead0$ = copys$
  243.  
  244. FUNCTION TrimTail0$ (s$)
  245.     DIM copys$, dp AS INTEGER, i AS INTEGER, find AS INTEGER
  246.     copys$ = _TRIM$(s$) 'might as well remove spaces too
  247.     TrimTail0$ = copys$
  248.     dp = INSTR(copys$, ".")
  249.     IF dp > 0 THEN
  250.         i = LEN(copys$): find = 0
  251.         WHILE i > dp AND MID$(copys$, i, 1) = "0"
  252.             i = i - 1: find = 1
  253.         WEND
  254.         IF find = 1 THEN
  255.             IF i = dp THEN
  256.                 TrimTail0$ = MID$(copys$, 1, dp - 1)
  257.             ELSE
  258.                 TrimTail0$ = MID$(copys$, 1, i)
  259.             END IF
  260.         END IF
  261.     END IF
  262.  
  263. FUNCTION trim0$ (s$)
  264.     DIM t$
  265.     t$ = TrimLead0$(s$)
  266.     trim0$ = TrimTail0$(t$)
  267.  
  268. ' for displaying truncated numbers say to 60 digits
  269. FUNCTION showDP$ (num$, nDP AS INTEGER)
  270.     DIM cNum$, dp AS INTEGER, d AS INTEGER, i AS INTEGER
  271.     cNum$ = num$ 'since num$ could get changed
  272.     showDP$ = num$
  273.     dp = INSTR(num$, ".")
  274.     IF dp > 0 THEN
  275.         IF LEN(MID$(cNum$, dp + 1)) > nDP THEN
  276.             d = VAL(MID$(cNum$, dp + nDP + 1, 1))
  277.             IF d > 4 THEN
  278.                 cNum$ = "0" + cNum$ ' tack on another 0 just in case 9's all the way to left
  279.                 dp = dp + 1
  280.                 i = dp + nDP
  281.                 WHILE MID$(cNum$, i, 1) = "9" OR MID$(cNum$, i, 1) = "."
  282.                     IF MID$(cNum$, i, 1) = "9" THEN
  283.                         MID$(cNum$, i, 1) = "0"
  284.                     END IF
  285.                     i = i - 1
  286.                 WEND
  287.                 MID$(cNum$, i, 1) = _TRIM$(STR$(VAL(MID$(cNum$, i, 1)) + 1)) 'last non 9 digit
  288.                 cNum$ = MID$(cNum$, 1, dp + nDP) 'chop it
  289.                 showDP$ = trim0$(cNum$)
  290.             ELSE
  291.                 showDP$ = MID$(cNum$, 1, dp + nDP)
  292.             END IF
  293.         END IF
  294.     END IF
  295.  

Milestone #1 the 4 Basic Arithmetic operations, plus display control of number (wo changing original number string).

Now for some fun with STx quiz ;-))
Title: Re: String Math
Post by: bplus on August 17, 2020, 12:44:30 pm
102 terms of sequence from inverse of STx Number:
(note: _UNSIGNED _INTEGER64 couldn't go the distance.)

Code: QB64: [Select]
  1. _TITLE "Multplicative Inverse: STx Fibonacci " ' b+ 2020-08-17
  2. ' 2020-08-16 picking up from Sting Math add$(), subtr$(), mult$() onto divide$()
  3. ' I always wanted to try doing division by multipling the numerator
  4. ' by the multiplicative inverse of denominator.
  5. ' n/d = n * 1/d so how bout a function that does 1/n.
  6. ' Then divide$() is pretty easy with mult$  Yes works but need more decimal work.
  7. ' 2020-08-17 redo all this with a decimal point for nInverse just start outstr with "." instead ""
  8. ' Now somethings off with divide$() first is 1 / 500 should be .002 returning .2, debug."
  9. ' Much better than post of last night
  10. ' showDP rounds up to DP (Decimal Places IF there are more decimals past DP)
  11.  
  12. ' 2020-08-17 from Multplicative Inverse: nInverse$ and divide$
  13. ' STx Fibonacci from
  14. ' ref: https://www.qb64.org/forum/index.php?topic=2921.msg121845#msg121845
  15.  
  16.  
  17. RANDOMIZE TIMER 'now that it's seems to be running silent
  18. SCREEN _NEWIMAGE(1200, 700, 32)
  19. _DELAY .25
  20.  
  21. 'special cases or hard cases
  22. DIM STxFib$
  23. STxFib$ = nInverse$("999999999999999999999998999999999999999999999999", 2495) '2445 DP? is what he has
  24. PRINT STxFib$
  25. 'OK starts good let's parse it
  26. DIM fibi(1 TO 200) AS STRING
  27. i = 3
  28. fibi(1) = "1"
  29. fibi(2) = "1"
  30. find = 0
  31.     fibi(i) = trim0$(add$(fibi(i - 2), fibi(i - 1)))
  32.     find = INSTR(find + 1, STxFib$, fibi(i))
  33.     IF find > 0 THEN
  34.         PRINT "Found Fibonacci"; i; ", "; fibi(i); " at"; find; "of len(STxFib$) ="; LEN(STxFib$)
  35.         i = i + 1
  36.     ELSE
  37.         PRINT "Failed to find next Fibonacci number"; i
  38.         dont = -1
  39.     END IF
  40.     PRINT "Press any to continue..."
  41.     SLEEP
  42. LOOP UNTIL dont = -1
  43.  
  44.  
  45.  
  46.  
  47. FUNCTION divide$ (n$, d$)
  48.     DIM di$, ndi$, nD AS INTEGER
  49.     IF trim0$(n$) = "0" THEN divide$ = "0": EXIT FUNCTION
  50.     IF trim0$(d$) = "0" THEN divide$ = "div 0": EXIT FUNCTION
  51.     IF trim0$(d$) = "1" THEN divide$ = trim0$(n$): EXIT FUNCTION '8/17 add trim0$
  52.     di$ = MID$(nInverse$(d$, 100), 2) 'chop off decimal point after
  53.     nD = LEN(di$)
  54.     ndi$ = mult$(n$, di$)
  55.     ndi$ = MID$(ndi$, 1, LEN(ndi$) - nD) + "." + RIGHT$(STRING$(nD, "0") + RIGHT$(ndi$, nD), nD)
  56.     divide$ = trim0$(ndi$)
  57.  
  58. FUNCTION nInverse$ (n$, DP AS INTEGER) 'assume decimal at very start of the string of digits returned, no rounding
  59.     DIM m$(1 TO 9), si$, r$, outstr$, d$
  60.     DIM i AS INTEGER
  61.     FOR i = 1 TO 9
  62.         si$ = _TRIM$(STR$(i))
  63.         m$(i) = mult$(si$, n$)
  64.     NEXT
  65.     outstr$ = ""
  66.     IF n$ = "0" THEN nInverse$ = "Div 0": EXIT FUNCTION
  67.     IF n$ = "1" THEN nInverse$ = "1": EXIT FUNCTION
  68.     outstr$ = "." 'everything else n > 1 is decimal 8/17
  69.     r$ = "10"
  70.     DO
  71.         WHILE LEFT$(subtr$(r$, n$), 1) = "-" '   r - n < 0
  72.             outstr$ = outstr$ + "0" '            add 0 to the  output string
  73.             IF LEN(outstr$) = DP THEN nInverse$ = outstr$: EXIT FUNCTION 'check if we've reached DP length
  74.             r$ = r$ + "0"
  75.         WEND
  76.         FOR i = 9 TO 1 STEP -1
  77.             IF LTE(m$(i), r$) THEN d$ = _TRIM$(STR$(i)): EXIT FOR
  78.         NEXT
  79.         outstr$ = outstr$ + d$
  80.         IF LEN(outstr$) = DP THEN nInverse$ = outstr$: EXIT FUNCTION
  81.         r$ = subtr$(r$, mult$(d$, n$)) 'r = r -d*n
  82.         IF r$ = "0" THEN nInverse$ = outstr$: EXIT FUNCTION 'found a perfect divisor
  83.         r$ = r$ + "0" 'add another place
  84.     LOOP
  85.  
  86. FUNCTION mult$ (a$, b$) 'assume both positive integers prechecked as all digits strings
  87.     DIM la AS INTEGER, lb AS INTEGER, m AS INTEGER, g AS INTEGER, dp AS INTEGER
  88.     DIM f18$, f1$, t$, build$, accum$
  89.  
  90.     IF trim0$(a$) = "0" THEN mult$ = "0": EXIT FUNCTION
  91.     IF trim0$(b$) = "0" THEN mult$ = "0": EXIT FUNCTION
  92.     IF trim0$(a$) = "1" THEN mult$ = trim0$(b$): EXIT FUNCTION
  93.     IF trim0$(b$) = "1" THEN mult$ = trim0$(a$): EXIT FUNCTION
  94.     'find the longer number and make it a mult of 18 to take 18 digits at a time from it
  95.     la = LEN(a$): lb = LEN(b$)
  96.     IF la > lb THEN
  97.         m = INT(la / 18) + 1
  98.         f18$ = RIGHT$(STRING$(m * 18, "0") + a$, m * 18)
  99.         f1$ = b$
  100.     ELSE
  101.         m = INT(lb / 18) + 1
  102.         f18$ = RIGHT$(STRING$(m * 18, "0") + b$, m * 18)
  103.         f1$ = a$
  104.     END IF
  105.     FOR dp = LEN(f1$) TO 1 STEP -1 'dp = digit position of the f1$
  106.         build$ = "" 'line builder
  107.         co = 0
  108.         'now taking 18 digits at a time Thanks Steve McNeill
  109.         FOR g = 1 TO m
  110.             v18 = VAL(MID$(f18$, m * 18 - g * 18 + 1, 18))
  111.             sd = VAL(MID$(f1$, dp, 1))
  112.             t$ = RIGHT$(STRING$(19, "0") + _TRIM$(STR$(v18 * sd + co)), 19)
  113.             co = VAL(MID$(t$, 1, 1))
  114.             build$ = MID$(t$, 2) + build$
  115.         NEXT g
  116.         IF co THEN build$ = _TRIM$(STR$(co)) + build$
  117.         IF dp = LEN(f1$) THEN
  118.             accum$ = build$
  119.         ELSE
  120.             accum$ = add$(accum$, build$ + STRING$(LEN(f1$) - dp, "0"))
  121.         END IF
  122.     NEXT dp
  123.     mult$ = accum$
  124.  
  125. FUNCTION subtr$ (sum$, minus$) ' assume both numbers are positive all digits
  126.     DIM m AS INTEGER, g AS INTEGER, p AS INTEGER
  127.     DIM ts$, tm$, sign$, LG$, sm$, t$, result$
  128.  
  129.     ts$ = TrimLead0$(sum$): tm$ = TrimLead0$(minus$)
  130.     IF trim0(ts$) = trim0$(tm$) THEN subtr$ = "0": EXIT FUNCTION 'OK proceed with function knowing they are not equal
  131.     tenE18 = 1000000000000000000 'yes!!! no dang E's
  132.     IF LTE(ts$, tm$) THEN ' which is bigger? minus is bigger
  133.         sign$ = "-"
  134.         m = INT(LEN(tm$) / 18) + 1
  135.         LG$ = RIGHT$(STRING$(m * 18, "0") + tm$, m * 18)
  136.         sm$ = RIGHT$(STRING$(m * 18, "0") + ts$, m * 18)
  137.     ELSE 'sum is bigger
  138.         sign$ = ""
  139.         m = INT(LEN(ts$) / 18) + 1
  140.         LG$ = RIGHT$(STRING$(m * 18, "0") + ts$, m * 18)
  141.         sm$ = RIGHT$(STRING$(m * 18, "0") + tm$, m * 18)
  142.     END IF
  143.     'now taking 18 digits at a time From Steve I learned we can do more than 1 digit at a time
  144.     FOR g = 1 TO m
  145.         VB = VAL(MID$(LG$, m * 18 - g * 18 + 1, 18))
  146.         vs = VAL(MID$(sm$, m * 18 - g * 18 + 1, 18))
  147.         IF vs > VB THEN
  148.             t$ = RIGHT$(STRING$(18, "0") + _TRIM$(STR$(tenE18 - vs + VB)), 18)
  149.  
  150.             'debug
  151.             'PRINT VB, tenE18, tenE18 - vs + VB, " t$ = "; t$
  152.  
  153.             ''borrow 1 = rewrite string
  154.             p = (m - g) * 18
  155.             WHILE p > 0 AND MID$(LG$, p, 1) = "0"
  156.                 MID$(LG$, p, 1) = "9"
  157.                 p = p - 1
  158.             WEND
  159.             IF p > 0 THEN MID$(LG$, p, 1) = _TRIM$(STR$(VAL(MID$(LG$, p, 1)) - 1))
  160.         ELSE
  161.             t$ = RIGHT$(STRING$(18, "0") + _TRIM$(STR$(VB - vs)), 18)
  162.         END IF
  163.         result$ = t$ + result$
  164.     NEXT
  165.     subtr$ = sign$ + result$
  166.  
  167. FUNCTION add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no spaces or - signs
  168.     'first thing is to set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
  169.     DIM la AS INTEGER, lb AS INTEGER, m AS INTEGER, g AS INTEGER
  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 * 18 - g * 18 + 1, 18))
  179.         sb = VAL(MID$(fb$, m * 18 - 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.  
  185.         'debug
  186.         'DIM w$
  187.         'PRINT a$, m * 18, sa, sb, t$, co, result$
  188.         'INPUT "OK "; w$ 'OK
  189.     NEXT
  190.     IF co THEN result$ = STR$(co) + result$
  191.     add$ = result$
  192.  
  193. ' String Math Helpers -----------------------------------------------
  194.  
  195. 'this function needs TrimLead0$(s$)
  196. FUNCTION LTE (a$, b$) ' a$ Less Than or Equal b$  comparison of 2 strings
  197.     DIM ca$, cb$, la AS INTEGER, lb AS INTEGER, i AS INTEGER
  198.     ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
  199.     la = LEN(ca$): lb = LEN(cb$)
  200.     IF ca$ = cb$ THEN
  201.         LTE = -1
  202.     ELSEIF la < lb THEN ' a is smaller
  203.         LTE = -1
  204.     ELSEIF la > lb THEN ' a is bigger
  205.         LTE = 0
  206.     ELSEIF la = lb THEN ' equal lengths
  207.         FOR i = 1 TO LEN(ca$)
  208.             IF VAL(MID$(ca$, i, 1)) > VAL(MID$(cb$, i, 1)) THEN
  209.                 LTE = 0: EXIT FUNCTION
  210.             ELSEIF VAL(MID$(ca$, i, 1)) < VAL(MID$(cb$, i, 1)) THEN
  211.                 LTE = -1: EXIT FUNCTION
  212.             END IF
  213.         NEXT
  214.     END IF
  215.  
  216. ' ------------------------------------- use these for final display
  217.  
  218. FUNCTION TrimLead0$ (s$) 'for treating strings as number (pos integers)
  219.     DIM copys$, i AS INTEGER, find AS INTEGER
  220.     copys$ = _TRIM$(s$) 'might as well remove spaces too
  221.     i = 1: find = 0
  222.     WHILE i < LEN(copys$) AND MID$(copys$, i, 1) = "0"
  223.         i = i + 1: find = 1
  224.     WEND
  225.     IF find = 1 THEN copys$ = MID$(copys$, i)
  226.     IF copys$ = "" THEN TrimLead0$ = "0" ELSE TrimLead0$ = copys$
  227.  
  228. FUNCTION TrimTail0$ (s$)
  229.     DIM copys$, dp AS INTEGER, i AS INTEGER, find AS INTEGER
  230.     copys$ = _TRIM$(s$) 'might as well remove spaces too
  231.     TrimTail0$ = copys$
  232.     dp = INSTR(copys$, ".")
  233.     IF dp > 0 THEN
  234.         i = LEN(copys$): find = 0
  235.         WHILE i > dp AND MID$(copys$, i, 1) = "0"
  236.             i = i - 1: find = 1
  237.         WEND
  238.         IF find = 1 THEN
  239.             IF i = dp THEN
  240.                 TrimTail0$ = MID$(copys$, 1, dp - 1)
  241.             ELSE
  242.                 TrimTail0$ = MID$(copys$, 1, i)
  243.             END IF
  244.         END IF
  245.     END IF
  246.  
  247. FUNCTION trim0$ (s$)
  248.     DIM t$
  249.     t$ = TrimLead0$(s$)
  250.     trim0$ = TrimTail0$(t$)
  251.  
  252. ' for displaying truncated numbers say to 60 digits
  253. FUNCTION showDP$ (num$, nDP AS INTEGER)
  254.     DIM cNum$, dp AS INTEGER, d AS INTEGER, i AS INTEGER
  255.     cNum$ = num$ 'since num$ could get changed
  256.     showDP$ = num$
  257.     dp = INSTR(num$, ".")
  258.     IF dp > 0 THEN
  259.         IF LEN(MID$(cNum$, dp + 1)) > nDP THEN
  260.             d = VAL(MID$(cNum$, dp + nDP + 1, 1))
  261.             IF d > 4 THEN
  262.                 cNum$ = "0" + cNum$ ' tack on another 0 just in case 9's all the way to left
  263.                 dp = dp + 1
  264.                 i = dp + nDP
  265.                 WHILE MID$(cNum$, i, 1) = "9" OR MID$(cNum$, i, 1) = "."
  266.                     IF MID$(cNum$, i, 1) = "9" THEN
  267.                         MID$(cNum$, i, 1) = "0"
  268.                     END IF
  269.                     i = i - 1
  270.                 WEND
  271.                 MID$(cNum$, i, 1) = _TRIM$(STR$(VAL(MID$(cNum$, i, 1)) + 1)) 'last non 9 digit
  272.                 cNum$ = MID$(cNum$, 1, dp + nDP) 'chop it
  273.                 showDP$ = trim0$(cNum$)
  274.             ELSE
  275.                 showDP$ = MID$(cNum$, 1, dp + nDP)
  276.             END IF
  277.         END IF
  278.     END IF
  279.  

 
Title: Re: String Math
Post by: bplus on August 18, 2020, 03:40:27 pm
Here it is! String math for 4 basic arithmetic operations with signs and decimal places in under 300 lines:

Code: QB64: [Select]
  1. _TITLE "Math regulator mr" ' b+ start 2020-08-17
  2. ' 2020-08-16 picking up from String Math add$(), subtr$(), mult$() onto divide$()
  3. ' I always wanted to try doing division by multipling the numerator
  4. ' by the multiplicative inverse of denominator.
  5. ' n/d = n * 1/d so how bout a function that does 1/n.
  6. ' Then divide$() is pretty easy with mult$  Yes works but need more decimal work.
  7. ' 2020-08-17 redo all this with a decimal point for nInverse just start outstr with "." instead ""
  8. ' Now somethings off with divide$() first is 1 / 500 should be .002 returning .2, debug."
  9. ' Much better than post of last night
  10. ' showDP rounds up to DP (Decimal Places IF there are more decimals past DP)
  11. ' 2020-08-17 Math regulator mr$(a$, ops$, b$) ' manages before and after calls to 4 basic arith operations
  12. ' note: 265 lines with just the set of functions so far
  13. ' 2020-08-18 last night subtr and signs confused hell out of me but I think I have all code I need for +!
  14. ' OK add and subtr seem to be working with signs and decimals but ugly code, can we combine lines?
  15. ' OH yeah, traded 12 redundant lines for 2! and a bunch of debug and other 389 lines to 354
  16. ' OK setup for * and /, mr$ finished adding 77 lines of code
  17. ' OK everything is simple after divide$() and substraction with signs and decimals ;-))
  18.  
  19. RANDOMIZE TIMER 'now that it's seems to be running silent
  20. SCREEN _NEWIMAGE(1200, 700, 32)
  21. _DELAY .25
  22.  
  23. ' debug tests
  24. 'PRINT "2105 ? "
  25. 'PRINT mr$("5", "+", "2100") ' OK
  26. 'PRINT "-10039.0965 ? "
  27. 'PRINT mr$("6.1035", "+", "-10045.2") ' OK
  28. 'PRINT "-801.99968 ?"
  29. 'PRINT mr$("-802", "+", ".00032")
  30. 'PRINT "-.0003500009 ?"
  31. 'PRINT mr$("-.00035", "+", "-.0000000009")
  32. 'PRINT "-5 ?"
  33. 'PRINT mr$("-10", "-", "-5")
  34. 'PRINT "-15 ?"
  35. 'PRINT mr$("-10", "-", "5") 'X fixed
  36. 'PRINT "15 ?"
  37. 'PRINT mr$("10", "-", "-5")
  38. 'PRINT "5 ?"
  39. 'PRINT mr$("10", "-", "5")
  40. 'PRINT "4.99 ?"
  41. 'PRINT mr$("-.010", "-", "-5")
  42. 'PRINT "-5.01 ?"
  43. 'PRINT mr$("-.010", "-", "5") 'X  fixed
  44. 'PRINT "5.01 ?"
  45. 'PRINT mr$(".010", "-", "-5")
  46. 'PRINT "-4.99 ?"
  47. 'PRINT mr$(".010", "-", "5")
  48. 'PRINT .00207 * 10000; "?"
  49. 'PRINT mr$(".00207", " * ", "10000") 'OK
  50. 'PRINT 2468.02468 / .02; "?"
  51. 'PRINT mr$("2468.02468", "/", ".02") 'OK
  52. 'PRINT -.333333333333333333 * -3.00000000000; "?"
  53. 'PRINT mr$("-.333333333333333333", "*", "-3.00000000000") 'OK
  54. 'PRINT -1.0000001 / .07; "?"
  55. 'PRINT mr$("-1.0000001", "/", ".07") 'OK
  56. 'PRINT 90.9 / -30; "?"
  57. 'PRINT mr$("90.9", "/", "-30") 'OK
  58.  
  59.  
  60. 'random testing for correct signs, decimal places and values
  61. DIM ad AS DOUBLE, bd AS DOUBLE, check AS DOUBLE, ea AS INTEGER, eb AS INTEGER, ma AS INTEGER, mb AS INTEGER
  62. DIM op$, a$, b$, mrStr$, ma$, mb$, qbCalc$
  63.     'pick two numbers
  64.     op$ = MID$("+-*/", INT(RND * 4) + 1, 1)
  65.     IF RND < .5 THEN
  66.         ma$ = "-": ma = -1
  67.     ELSE
  68.         ma$ = "": ma = 1
  69.     END IF
  70.     IF RND < .5 THEN
  71.         mb$ = "-": mb = -1
  72.     ELSE
  73.         mb$ = "": mb = 1
  74.     END IF
  75.  
  76.     ea = INT(10 * RND): eb = INT(10 * RND)
  77.     ad = ma * RND * 10 ^ ea: bd = mb * RND * 10 ^ eb
  78.     SELECT CASE op$
  79.         CASE "+": check = ad + bd
  80.         CASE "-": check = ad - bd
  81.         CASE "*": check = ad * bd
  82.         CASE "/": check = ad / bd
  83.     END SELECT
  84.     a$ = _TRIM$(STR$(ad)): b$ = _TRIM$(STR$(bd))
  85.     PRINT
  86.     PRINT a$; " "; op$; " "; b$; " ="
  87.     mrStr$ = mr$(a$, op$, b$)
  88.     PRINT mrStr$; " according to mr$ function we are testing."
  89.     qbCalc$ = _TRIM$(STR$(check))
  90.     PRINT qbCalc$; " according to QB64 math"
  91.     PRINT "diff ="; VAL(mrStr$) - check
  92.     PRINT "Next subtr$ check in 10 secs, or press key,  press escape to quit..."
  93.     SLEEP 10 ' comment out to see if any delay in execution of print
  94.  
  95. FUNCTION mr$ (a$, op$, b$) ' catchy? mr$ for math regulator
  96.     DIM ca$, cb$, aSgn$, bSgn$, postOp$, sgn$
  97.     DIM adp AS INTEGER, bdp AS INTEGER, dp AS INTEGER, lpop AS INTEGER
  98.  
  99.     op$ = _TRIM$(op$) 'save fixing each time
  100.     ca$ = _TRIM$(a$): cb$ = _TRIM$(b$) 'make copies in case we change
  101.     'strip signs and decimals
  102.     IF LEFT$(ca$, 1) = "-" THEN
  103.         aSgn$ = "-": ca$ = MID$(ca$, 2)
  104.     ELSE
  105.         aSgn$ = "": ca$ = ca$
  106.     END IF
  107.     dp = INSTR(ca$, ".")
  108.     IF dp > 0 THEN
  109.         adp = LEN(ca$) - dp
  110.         ca$ = MID$(ca$, 1, dp - 1) + MID$(ca$, dp + 1)
  111.     ELSE
  112.         adp = 0
  113.     END IF
  114.     IF LEFT$(cb$, 1) = "-" THEN
  115.         bSgn$ = "-": cb$ = MID$(cb$, 2)
  116.     ELSE
  117.         bSgn$ = "": cb$ = cb$
  118.     END IF
  119.     dp = INSTR(cb$, ".")
  120.     IF dp > 0 THEN
  121.         bdp = LEN(cb$) - dp
  122.         cb$ = MID$(cb$, 1, dp - 1) + MID$(cb$, dp + 1)
  123.     ELSE
  124.         bdp = 0
  125.     END IF
  126.  
  127.     IF op$ = "+" OR op$ = "-" OR op$ = "/" THEN 'add or subtr  even up strings on right of decimal
  128.         'even up the right sides of decimals if any
  129.         IF adp > bdp THEN dp = adp ELSE dp = bdp
  130.         IF adp < dp THEN ca$ = ca$ + STRING$(dp - adp, "0")
  131.         IF bdp < dp THEN cb$ = cb$ + STRING$(dp - bdp, "0")
  132.     ELSEIF op$ = "*" THEN
  133.         dp = adp + bdp
  134.     END IF
  135.     IF op$ = "*" OR op$ = "/" THEN
  136.         IF aSgn$ = bSgn$ THEN sgn$ = "" ELSE sgn$ = "-"
  137.     END IF
  138.     'now according to signs and op$ call add$ or subtr$
  139.     IF op$ = "+" THEN
  140.         IF aSgn$ = bSgn$ THEN 'really add
  141.             postOp$ = aSgn$ + add$(ca$, cb$)
  142.         ELSE 'have a case of subtraction
  143.             IF aSgn$ = "-" THEN postOp$ = subtr$(cb$, ca$) ELSE postOp$ = subtr$(ca$, cb$)
  144.         END IF
  145.     ELSEIF op$ = "-" THEN
  146.         IF bSgn$ = "-" THEN 'really add but switch b sign
  147.             bSgn$ = ""
  148.             IF aSgn$ = "-" THEN
  149.                 postOp$ = subtr$(cb$, ca$)
  150.             ELSE 'aSgn = ""
  151.                 postOp$ = add$(ca$, cb$)
  152.             END IF
  153.         ELSE 'bSgn$ =""
  154.             IF aSgn$ = "-" THEN
  155.                 bSgn$ = "-"
  156.                 postOp$ = aSgn$ + add$(ca$, cb$)
  157.             ELSE
  158.                 postOp$ = subtr$(ca$, cb$)
  159.             END IF
  160.         END IF
  161.     ELSEIF op$ = "*" THEN
  162.         postOp$ = sgn$ + mult$(ca$, cb$)
  163.     ELSEIF op$ = "/" THEN
  164.         postOp$ = sgn$ + divide$(ca$, cb$)
  165.     END IF ' which op
  166.     'put dp back
  167.     IF op$ <> "/" THEN
  168.         lpop = LEN(postOp$) ' put decimal back
  169.         postOp$ = MID$(postOp$, 1, lpop - dp) + "." + MID$(postOp$, lpop - dp + 1)
  170.     END IF
  171.     mr$ = trim0$(postOp$)
  172.  
  173. FUNCTION divide$ (n$, d$)
  174.     DIM di$, ndi$, nD AS INTEGER
  175.     IF trim0$(n$) = "0" THEN divide$ = "0": EXIT FUNCTION
  176.     IF trim0$(d$) = "0" THEN divide$ = "div 0": EXIT FUNCTION
  177.     IF trim0$(d$) = "1" THEN divide$ = trim0$(n$): EXIT FUNCTION '8/17 add trim0$
  178.     di$ = MID$(nInverse$(d$, 100), 2) 'chop off decimal point after
  179.     nD = LEN(di$)
  180.     ndi$ = mult$(n$, di$)
  181.     ndi$ = MID$(ndi$, 1, LEN(ndi$) - nD) + "." + RIGHT$(STRING$(nD, "0") + RIGHT$(ndi$, nD), nD)
  182.     divide$ = trim0$(ndi$)
  183.  
  184. FUNCTION nInverse$ (n$, DP AS INTEGER) 'assume decimal at very start of the string of digits returned, no rounding
  185.     DIM m$(1 TO 9), si$, r$, outstr$, d$
  186.     DIM i AS INTEGER
  187.     FOR i = 1 TO 9
  188.         si$ = _TRIM$(STR$(i))
  189.         m$(i) = mult$(si$, n$)
  190.     NEXT
  191.     outstr$ = ""
  192.     IF n$ = "0" THEN nInverse$ = "Div 0": EXIT FUNCTION
  193.     IF n$ = "1" THEN nInverse$ = "1": EXIT FUNCTION
  194.     outstr$ = "." 'everything else n > 1 is decimal 8/17
  195.     r$ = "10"
  196.     DO
  197.         WHILE LEFT$(subtr$(r$, n$), 1) = "-" '   r - n < 0
  198.             outstr$ = outstr$ + "0" '            add 0 to the  output string
  199.             IF LEN(outstr$) = DP THEN nInverse$ = outstr$: EXIT FUNCTION 'check if we've reached DP length
  200.             r$ = r$ + "0"
  201.         WEND
  202.         FOR i = 9 TO 1 STEP -1
  203.             IF LTE(m$(i), r$) THEN d$ = _TRIM$(STR$(i)): EXIT FOR
  204.         NEXT
  205.         outstr$ = outstr$ + d$
  206.         IF LEN(outstr$) = DP THEN nInverse$ = outstr$: EXIT FUNCTION
  207.         r$ = subtr$(r$, mult$(d$, n$)) 'r = r -d*n
  208.         IF r$ = "0" THEN nInverse$ = outstr$: EXIT FUNCTION 'found a perfect divisor
  209.         r$ = r$ + "0" 'add another place
  210.     LOOP
  211.  
  212. FUNCTION mult$ (a$, b$) 'assume both positive integers prechecked as all digits strings
  213.     DIM la AS INTEGER, lb AS INTEGER, m AS INTEGER, g AS INTEGER, dp AS INTEGER
  214.     DIM f18$, f1$, t$, build$, accum$
  215.  
  216.     IF trim0$(a$) = "0" THEN mult$ = "0": EXIT FUNCTION
  217.     IF trim0$(b$) = "0" THEN mult$ = "0": EXIT FUNCTION
  218.     IF trim0$(a$) = "1" THEN mult$ = trim0$(b$): EXIT FUNCTION
  219.     IF trim0$(b$) = "1" THEN mult$ = trim0$(a$): EXIT FUNCTION
  220.     'find the longer number and make it a mult of 18 to take 18 digits at a time from it
  221.     la = LEN(a$): lb = LEN(b$)
  222.     IF la > lb THEN
  223.         m = INT(la / 18) + 1
  224.         f18$ = RIGHT$(STRING$(m * 18, "0") + a$, m * 18)
  225.         f1$ = b$
  226.     ELSE
  227.         m = INT(lb / 18) + 1
  228.         f18$ = RIGHT$(STRING$(m * 18, "0") + b$, m * 18)
  229.         f1$ = a$
  230.     END IF
  231.     FOR dp = LEN(f1$) TO 1 STEP -1 'dp = digit position of the f1$
  232.         build$ = "" 'line builder
  233.         co = 0
  234.         'now taking 18 digits at a time Thanks Steve McNeill
  235.         FOR g = 1 TO m
  236.             v18 = VAL(MID$(f18$, m * 18 - g * 18 + 1, 18))
  237.             sd = VAL(MID$(f1$, dp, 1))
  238.             t$ = RIGHT$(STRING$(19, "0") + _TRIM$(STR$(v18 * sd + co)), 19)
  239.             co = VAL(MID$(t$, 1, 1))
  240.             build$ = MID$(t$, 2) + build$
  241.         NEXT g
  242.         IF co THEN build$ = _TRIM$(STR$(co)) + build$
  243.         IF dp = LEN(f1$) THEN
  244.             accum$ = build$
  245.         ELSE
  246.             accum$ = add$(accum$, build$ + STRING$(LEN(f1$) - dp, "0"))
  247.         END IF
  248.     NEXT dp
  249.     mult$ = accum$
  250.  
  251. FUNCTION subtr$ (sum$, minus$) ' assume both numbers are positive all digits
  252.     DIM m AS INTEGER, g AS INTEGER, p AS INTEGER
  253.     DIM ts$, tm$, sign$, LG$, sm$, t$, result$
  254.  
  255.     ts$ = TrimLead0$(sum$): tm$ = TrimLead0$(minus$)
  256.     IF trim0(ts$) = trim0$(tm$) THEN subtr$ = "0": EXIT FUNCTION 'OK proceed with function knowing they are not equal
  257.     tenE18 = 1000000000000000000 'yes!!! no dang E's
  258.     IF LTE(ts$, tm$) THEN ' which is bigger? minus is bigger
  259.         sign$ = "-"
  260.         m = INT(LEN(tm$) / 18) + 1
  261.         LG$ = RIGHT$(STRING$(m * 18, "0") + tm$, m * 18)
  262.         sm$ = RIGHT$(STRING$(m * 18, "0") + ts$, m * 18)
  263.     ELSE 'sum is bigger
  264.         sign$ = ""
  265.         m = INT(LEN(ts$) / 18) + 1
  266.         LG$ = RIGHT$(STRING$(m * 18, "0") + ts$, m * 18)
  267.         sm$ = RIGHT$(STRING$(m * 18, "0") + tm$, m * 18)
  268.     END IF
  269.     'now taking 18 digits at a time From Steve I learned we can do more than 1 digit at a time
  270.     FOR g = 1 TO m
  271.         VB = VAL(MID$(LG$, m * 18 - g * 18 + 1, 18))
  272.         vs = VAL(MID$(sm$, m * 18 - g * 18 + 1, 18))
  273.         IF vs > VB THEN
  274.             t$ = RIGHT$(STRING$(18, "0") + _TRIM$(STR$(tenE18 - vs + VB)), 18)
  275.             p = (m - g) * 18
  276.             WHILE p > 0 AND MID$(LG$, p, 1) = "0"
  277.                 MID$(LG$, p, 1) = "9"
  278.                 p = p - 1
  279.             WEND
  280.             IF p > 0 THEN MID$(LG$, p, 1) = _TRIM$(STR$(VAL(MID$(LG$, p, 1)) - 1))
  281.         ELSE
  282.             t$ = RIGHT$(STRING$(18, "0") + _TRIM$(STR$(VB - vs)), 18)
  283.         END IF
  284.         result$ = t$ + result$
  285.     NEXT
  286.     subtr$ = sign$ + result$
  287.  
  288. FUNCTION add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no spaces or - signs
  289.     'first thing is to set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
  290.     DIM la AS INTEGER, lb AS INTEGER, m AS INTEGER, g AS INTEGER
  291.     DIM fa$, fb$, t$, new$, result$
  292.     la = LEN(a$): lb = LEN(b$)
  293.     IF la > lb THEN m = INT(la / 18) + 1 ELSE m = INT(lb / 18) + 1
  294.     fa$ = RIGHT$(STRING$(m * 18, "0") + a$, m * 18)
  295.     fb$ = RIGHT$(STRING$(m * 18, "0") + b$, m * 18)
  296.  
  297.     'now taking 18 digits at a time Thanks Steve McNeill
  298.     FOR g = 1 TO m
  299.         sa = VAL(MID$(fa$, m * 18 - g * 18 + 1, 18))
  300.         sb = VAL(MID$(fb$, m * 18 - g * 18 + 1, 18))
  301.         t$ = RIGHT$(STRING$(36, "0") + _TRIM$(STR$(sa + sb + co)), 36)
  302.         co = VAL(MID$(t$, 1, 18))
  303.         new$ = MID$(t$, 19)
  304.         result$ = new$ + result$
  305.     NEXT
  306.     IF co THEN result$ = STR$(co) + result$
  307.     add$ = result$
  308.  
  309. ' String Math Helpers -----------------------------------------------
  310.  
  311. 'this function needs TrimLead0$(s$)
  312. FUNCTION LTE (a$, b$) ' a$ Less Than or Equal b$  comparison of 2 strings
  313.     DIM ca$, cb$, la AS INTEGER, lb AS INTEGER, i AS INTEGER
  314.     ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
  315.     la = LEN(ca$): lb = LEN(cb$)
  316.     IF ca$ = cb$ THEN
  317.         LTE = -1
  318.     ELSEIF la < lb THEN ' a is smaller
  319.         LTE = -1
  320.     ELSEIF la > lb THEN ' a is bigger
  321.         LTE = 0
  322.     ELSEIF la = lb THEN ' equal lengths
  323.         FOR i = 1 TO LEN(ca$)
  324.             IF VAL(MID$(ca$, i, 1)) > VAL(MID$(cb$, i, 1)) THEN
  325.                 LTE = 0: EXIT FUNCTION
  326.             ELSEIF VAL(MID$(ca$, i, 1)) < VAL(MID$(cb$, i, 1)) THEN
  327.                 LTE = -1: EXIT FUNCTION
  328.             END IF
  329.         NEXT
  330.     END IF
  331.  
  332. ' ------------------------------------- use these for final display
  333.  
  334. FUNCTION TrimLead0$ (s$) 'for treating strings as number (pos integers)
  335.     DIM copys$, i AS INTEGER, find AS INTEGER
  336.     copys$ = _TRIM$(s$) 'might as well remove spaces too
  337.     i = 1: find = 0
  338.     WHILE i < LEN(copys$) AND MID$(copys$, i, 1) = "0"
  339.         i = i + 1: find = 1
  340.     WEND
  341.     IF find = 1 THEN copys$ = MID$(copys$, i)
  342.     IF copys$ = "" THEN TrimLead0$ = "0" ELSE TrimLead0$ = copys$
  343.  
  344. FUNCTION TrimTail0$ (s$)
  345.     DIM copys$, dp AS INTEGER, i AS INTEGER, find AS INTEGER
  346.     copys$ = _TRIM$(s$) 'might as well remove spaces too
  347.     TrimTail0$ = copys$
  348.     dp = INSTR(copys$, ".")
  349.     IF dp > 0 THEN
  350.         i = LEN(copys$): find = 0
  351.         WHILE i > dp AND MID$(copys$, i, 1) = "0"
  352.             i = i - 1: find = 1
  353.         WEND
  354.         IF find = 1 THEN
  355.             IF i = dp THEN
  356.                 TrimTail0$ = MID$(copys$, 1, dp - 1)
  357.             ELSE
  358.                 TrimTail0$ = MID$(copys$, 1, i)
  359.             END IF
  360.         END IF
  361.     END IF
  362.  
  363. FUNCTION trim0$ (s$)
  364.     DIM cs$, si$
  365.     cs$ = s$
  366.     si$ = LEFT$(cs$, 1)
  367.     IF si$ = "-" THEN cs$ = MID$(cs$, 2)
  368.     cs$ = TrimLead0$(cs$)
  369.     cs$ = TrimTail0$(cs$)
  370.     IF RIGHT$(cs$, 1) = "." THEN cs$ = MID$(cs$, 1, LEN(cs$) - 1)
  371.     IF si$ = "-" THEN trim0$ = si$ + cs$ ELSE trim0$ = cs$
  372.  
  373. ' for displaying truncated numbers say to 60 digits
  374. FUNCTION showDP$ (num$, nDP AS INTEGER)
  375.     DIM cNum$, dp AS INTEGER, d AS INTEGER, i AS INTEGER
  376.     cNum$ = num$ 'since num$ could get changed
  377.     showDP$ = num$
  378.     dp = INSTR(num$, ".")
  379.     IF dp > 0 THEN
  380.         IF LEN(MID$(cNum$, dp + 1)) > nDP THEN
  381.             d = VAL(MID$(cNum$, dp + nDP + 1, 1))
  382.             IF d > 4 THEN
  383.                 cNum$ = "0" + cNum$ ' tack on another 0 just in case 9's all the way to left
  384.                 dp = dp + 1
  385.                 i = dp + nDP
  386.                 WHILE MID$(cNum$, i, 1) = "9" OR MID$(cNum$, i, 1) = "."
  387.                     IF MID$(cNum$, i, 1) = "9" THEN
  388.                         MID$(cNum$, i, 1) = "0"
  389.                     END IF
  390.                     i = i - 1
  391.                 WEND
  392.                 MID$(cNum$, i, 1) = _TRIM$(STR$(VAL(MID$(cNum$, i, 1)) + 1)) 'last non 9 digit
  393.                 cNum$ = MID$(cNum$, 1, dp + nDP) 'chop it
  394.                 showDP$ = trim0$(cNum$)
  395.             ELSE
  396.                 showDP$ = MID$(cNum$, 1, dp + nDP)
  397.             END IF
  398.         END IF
  399.     END IF
  400.  


Update 2021-06-03: Nope! Sorry the divide$ function was buggy. Had to pull the link to this code from Library.
Title: Re: String Math
Post by: bplus on June 03, 2021, 09:24:02 am
Attempting to extend String Math into Square Roots, I discovered a huge bug with Divide$ Function, it's all commented in code below. I think I have it patched but maybe not. This is tricky without Boolean Compares for String Math.

Main code is testing a 100 digit precision Square Root Code and Divide$ has the "fixed"? String Math patch
Code: QB64: [Select]
  1. _Title "Math regulator mr test sqr estimating" ' b+ start 2021-06-02
  2. Randomize Timer 'now that it's seems to be running silent
  3. Screen _NewImage(1200, 700, 32)
  4. _Delay .25
  5.  
  6. Dim n$, guess$, other$, cont$, sum$, loopCnt, lastGuess$
  7.     'remember everything is strings
  8.     Input "Enter a number to find it's square root "; n$
  9.     If n$ = "" Then End
  10.     guess$ = mr$(n$, "/", "2") ' for n$ = 2
  11.     other$ = n$
  12.     loopCnt = 0
  13.     Do
  14.         loopCnt = loopCnt + 1
  15.         Print loopCnt; " guess: "; guess$
  16.         'Input "Continue press enter, any other to quit "; cont$
  17.         'If cont$ = "" Then
  18.         If (Mid$(guess$, 1, 105) = Mid$(lastGuess$, 1, 105)) Then ' go past 100 matching digits for 100 digit precision
  19.             Print
  20.             Print "100 digits precision goal, not 100 digits past decimal: Square root of "; n$; " is:"
  21.             Print Mid$(guess$, 1, 101) ' one char for decimal
  22.             _Clipboard$ = Mid$(guess$, 1, 101) ' ditto
  23.             Print
  24.             Exit Do
  25.         Else
  26.             lastGuess$ = guess$
  27.             'Print "guess$ "; guess$
  28.             'Print "other$ "; other$
  29.             sum$ = mr$(guess$, "+", other$)
  30.             'Print "sum$ "; sum$
  31.             'Print "Call divide for guess$, sum$ / 2"
  32.             guess$ = mr$(sum$, "/", "2")
  33.             Print "New guess$ "; Mid$(guess$, 1, 105)
  34.             'Print "Call n$ divide  new guess$ for other$, n$ / guess$"
  35.             other$ = mr$(n$, "/", guess$)
  36.             Print "New other$ "; Mid$(other$, 1, 105)
  37.         End If
  38.         'Else
  39.         '    Exit Do
  40.         'End If
  41.     Loop
  42.  
  43. Function mr$ (a$, op$, b$) ' catchy? mr$ for math regulator
  44.     Dim ca$, cb$, aSgn$, bSgn$, postOp$, sgn$
  45.     Dim adp As Integer, bdp As Integer, dp As Integer, lpop As Integer
  46.  
  47.     op$ = _Trim$(op$) 'save fixing each time
  48.     ca$ = _Trim$(a$): cb$ = _Trim$(b$) 'make copies in case we change
  49.     'strip signs and decimals
  50.     If Left$(ca$, 1) = "-" Then
  51.         aSgn$ = "-": ca$ = Mid$(ca$, 2)
  52.     Else
  53.         aSgn$ = "": ca$ = ca$
  54.     End If
  55.     dp = InStr(ca$, ".")
  56.     If dp > 0 Then
  57.         adp = Len(ca$) - dp
  58.         ca$ = Mid$(ca$, 1, dp - 1) + Mid$(ca$, dp + 1)
  59.     Else
  60.         adp = 0
  61.     End If
  62.     If Left$(cb$, 1) = "-" Then
  63.         bSgn$ = "-": cb$ = Mid$(cb$, 2)
  64.     Else
  65.         bSgn$ = "": cb$ = cb$
  66.     End If
  67.     dp = InStr(cb$, ".")
  68.     If dp > 0 Then
  69.         bdp = Len(cb$) - dp
  70.         cb$ = Mid$(cb$, 1, dp - 1) + Mid$(cb$, dp + 1)
  71.     Else
  72.         bdp = 0
  73.     End If
  74.  
  75.     If op$ = "+" Or op$ = "-" Or op$ = "/" Then 'add or subtr  even up strings on right of decimal
  76.         'even up the right sides of decimals if any
  77.         If adp > bdp Then dp = adp Else dp = bdp
  78.         If adp < dp Then ca$ = ca$ + String$(dp - adp, "0")
  79.         If bdp < dp Then cb$ = cb$ + String$(dp - bdp, "0")
  80.     ElseIf op$ = "*" Then
  81.         dp = adp + bdp
  82.     End If
  83.     If op$ = "*" Or op$ = "/" Then
  84.         If aSgn$ = bSgn$ Then sgn$ = "" Else sgn$ = "-"
  85.     End If
  86.     'now according to signs and op$ call add$ or subtr$
  87.     If op$ = "+" Then
  88.         If aSgn$ = bSgn$ Then 'really add
  89.             postOp$ = aSgn$ + add$(ca$, cb$)
  90.         Else 'have a case of subtraction
  91.             If aSgn$ = "-" Then postOp$ = subtr$(cb$, ca$) Else postOp$ = subtr$(ca$, cb$)
  92.         End If
  93.     ElseIf op$ = "-" Then
  94.         If bSgn$ = "-" Then 'really add but switch b sign
  95.             bSgn$ = ""
  96.             If aSgn$ = "-" Then
  97.                 postOp$ = subtr$(cb$, ca$)
  98.             Else 'aSgn = ""
  99.                 postOp$ = add$(ca$, cb$)
  100.             End If
  101.         Else 'bSgn$ =""
  102.             If aSgn$ = "-" Then
  103.                 bSgn$ = "-"
  104.                 postOp$ = aSgn$ + add$(ca$, cb$)
  105.             Else
  106.                 postOp$ = subtr$(ca$, cb$)
  107.             End If
  108.         End If
  109.     ElseIf op$ = "*" Then
  110.         postOp$ = sgn$ + mult$(ca$, cb$)
  111.     ElseIf op$ = "/" Then
  112.         postOp$ = sgn$ + divide$(ca$, cb$)
  113.     End If ' which op
  114.     'put dp back
  115.     If op$ <> "/" Then
  116.         lpop = Len(postOp$) ' put decimal back
  117.         postOp$ = Mid$(postOp$, 1, lpop - dp) + "." + Mid$(postOp$, lpop - dp + 1)
  118.     End If
  119.     mr$ = trim0$(postOp$)
  120.  
  121. Function divide$ (n$, d$) ' goal here is 100 digits precision not 100 digits past decimal
  122.     'Print "divide$ got n$ = "; n$; " and d$ = "; d$
  123.     Dim di$, ndi$, nD As Integer
  124.     If trim0$(n$) = "0" Then divide$ = "0": Exit Function
  125.     If trim0$(d$) = "0" Then divide$ = "div 0": Exit Function
  126.     If trim0$(d$) = "1" Then divide$ = trim0$(n$): Exit Function '8/17 add trim0$
  127.  
  128.     ' aha! found a bug when d$ gets really huge 100 is no where near enough!!!!
  129.     ' 2021-06-03 fix by adding 100 to len(d$), plus have to go a little past 100 for 100 digit precision
  130.     ' need to go past 100 for 100 precise digits (not decimal places)
  131.     di$ = Mid$(nInverse$(d$, Len(d$) + 200), 2) 'chop off decimal point after
  132.     'Print "divide$ inverse of d$ = di$ = "; di$
  133.  
  134.     nD = Len(di$)
  135.     ndi$ = mult$(n$, di$)
  136.     ndi$ = Mid$(ndi$, 1, Len(ndi$) - nD) + "." + Right$(String$(nD, "0") + Right$(ndi$, nD), nD)
  137.     divide$ = trim0$(ndi$)
  138.  
  139. Function nInverse$ (n$, DP As Integer) 'assume decimal at very start of the string of digits returned, no rounding
  140.     Dim m$(1 To 9), si$, r$, outstr$, d$
  141.     Dim i As Integer
  142.     For i = 1 To 9
  143.         si$ = _Trim$(Str$(i))
  144.         m$(i) = mult$(si$, n$)
  145.     Next
  146.     outstr$ = ""
  147.     If n$ = "0" Then nInverse$ = "Div 0": Exit Function
  148.     If n$ = "1" Then nInverse$ = "1": Exit Function
  149.     outstr$ = "." 'everything else n > 1 is decimal 8/17
  150.     r$ = "10"
  151.     Do
  152.         While Left$(subtr$(r$, n$), 1) = "-" '   r - n < 0
  153.             outstr$ = outstr$ + "0" '            add 0 to the  output string
  154.             If Len(outstr$) = DP Then nInverse$ = outstr$: Exit Function 'check if we've reached DP length
  155.             r$ = r$ + "0"
  156.         Wend
  157.         For i = 9 To 1 Step -1
  158.             If LTE(m$(i), r$) Then d$ = _Trim$(Str$(i)): Exit For
  159.         Next
  160.         outstr$ = outstr$ + d$
  161.         If Len(outstr$) = DP Then nInverse$ = outstr$: Exit Function
  162.         r$ = subtr$(r$, mult$(d$, n$)) 'r = r -d*n
  163.         If r$ = "0" Then nInverse$ = outstr$: Exit Function 'found a perfect divisor
  164.         r$ = r$ + "0" 'add another place
  165.     Loop
  166.  
  167. Function mult$ (a$, b$) 'assume both positive integers prechecked as all digits strings
  168.     Dim la As Integer, lb As Integer, m As Integer, g As Integer, dp As Integer
  169.     Dim f18$, f1$, t$, build$, accum$
  170.  
  171.     If trim0$(a$) = "0" Then mult$ = "0": Exit Function
  172.     If trim0$(b$) = "0" Then mult$ = "0": Exit Function
  173.     If trim0$(a$) = "1" Then mult$ = trim0$(b$): Exit Function
  174.     If trim0$(b$) = "1" Then mult$ = trim0$(a$): Exit Function
  175.     'find the longer number and make it a mult of 18 to take 18 digits at a time from it
  176.     la = Len(a$): lb = Len(b$)
  177.     If la > lb Then
  178.         m = Int(la / 18) + 1
  179.         f18$ = Right$(String$(m * 18, "0") + a$, m * 18)
  180.         f1$ = b$
  181.     Else
  182.         m = Int(lb / 18) + 1
  183.         f18$ = Right$(String$(m * 18, "0") + b$, m * 18)
  184.         f1$ = a$
  185.     End If
  186.     For dp = Len(f1$) To 1 Step -1 'dp = digit position of the f1$
  187.         build$ = "" 'line builder
  188.         co = 0
  189.         'now taking 18 digits at a time Thanks Steve McNeill
  190.         For g = 1 To m
  191.             v18 = Val(Mid$(f18$, m * 18 - g * 18 + 1, 18))
  192.             sd = Val(Mid$(f1$, dp, 1))
  193.             t$ = Right$(String$(19, "0") + _Trim$(Str$(v18 * sd + co)), 19)
  194.             co = Val(Mid$(t$, 1, 1))
  195.             build$ = Mid$(t$, 2) + build$
  196.         Next g
  197.         If co Then build$ = _Trim$(Str$(co)) + build$
  198.         If dp = Len(f1$) Then
  199.             accum$ = build$
  200.         Else
  201.             accum$ = add$(accum$, build$ + String$(Len(f1$) - dp, "0"))
  202.         End If
  203.     Next dp
  204.     mult$ = accum$
  205.  
  206. Function subtr$ (sum$, minus$) ' assume both numbers are positive all digits
  207.     Dim m As Integer, g As Integer, p As Integer
  208.     Dim ts$, tm$, sign$, LG$, sm$, t$, result$
  209.  
  210.     ts$ = TrimLead0$(sum$): tm$ = TrimLead0$(minus$)
  211.     If trim0(ts$) = trim0$(tm$) Then subtr$ = "0": Exit Function 'OK proceed with function knowing they are not equal
  212.     tenE18 = 1000000000000000000 'yes!!! no dang E's
  213.     If LTE(ts$, tm$) Then ' which is bigger? minus is bigger
  214.         sign$ = "-"
  215.         m = Int(Len(tm$) / 18) + 1
  216.         LG$ = Right$(String$(m * 18, "0") + tm$, m * 18)
  217.         sm$ = Right$(String$(m * 18, "0") + ts$, m * 18)
  218.     Else 'sum is bigger
  219.         sign$ = ""
  220.         m = Int(Len(ts$) / 18) + 1
  221.         LG$ = Right$(String$(m * 18, "0") + ts$, m * 18)
  222.         sm$ = Right$(String$(m * 18, "0") + tm$, m * 18)
  223.     End If
  224.     'now taking 18 digits at a time From Steve I learned we can do more than 1 digit at a time
  225.     For g = 1 To m
  226.         VB = Val(Mid$(LG$, m * 18 - g * 18 + 1, 18))
  227.         vs = Val(Mid$(sm$, m * 18 - g * 18 + 1, 18))
  228.         If vs > VB Then
  229.             t$ = Right$(String$(18, "0") + _Trim$(Str$(tenE18 - vs + VB)), 18)
  230.             p = (m - g) * 18
  231.             While p > 0 And Mid$(LG$, p, 1) = "0"
  232.                 Mid$(LG$, p, 1) = "9"
  233.                 p = p - 1
  234.             Wend
  235.             If p > 0 Then Mid$(LG$, p, 1) = _Trim$(Str$(Val(Mid$(LG$, p, 1)) - 1))
  236.         Else
  237.             t$ = Right$(String$(18, "0") + _Trim$(Str$(VB - vs)), 18)
  238.         End If
  239.         result$ = t$ + result$
  240.     Next
  241.     subtr$ = sign$ + result$
  242.  
  243. Function add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no spaces or - signs
  244.     'first thing is to set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
  245.     Dim la As Integer, lb As Integer, m As Integer, g As Integer
  246.     Dim fa$, fb$, t$, new$, result$
  247.     la = Len(a$): lb = Len(b$)
  248.     If la > lb Then m = Int(la / 18) + 1 Else m = Int(lb / 18) + 1
  249.     fa$ = Right$(String$(m * 18, "0") + a$, m * 18)
  250.     fb$ = Right$(String$(m * 18, "0") + b$, m * 18)
  251.  
  252.     'now taking 18 digits at a time Thanks Steve McNeill
  253.     For g = 1 To m
  254.         sa = Val(Mid$(fa$, m * 18 - g * 18 + 1, 18))
  255.         sb = Val(Mid$(fb$, m * 18 - g * 18 + 1, 18))
  256.         t$ = Right$(String$(36, "0") + _Trim$(Str$(sa + sb + co)), 36)
  257.         co = Val(Mid$(t$, 1, 18))
  258.         new$ = Mid$(t$, 19)
  259.         result$ = new$ + result$
  260.     Next
  261.     If co Then result$ = Str$(co) + result$
  262.     add$ = result$
  263.  
  264. ' String Math Helpers -----------------------------------------------
  265.  
  266. 'this function needs TrimLead0$(s$)
  267. Function LTE (a$, b$) ' a$ Less Than or Equal b$  comparison of 2 strings, For Integers Only!
  268.     Dim ca$, cb$, la As Integer, lb As Integer, i As Integer
  269.     ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
  270.     la = Len(ca$): lb = Len(cb$)
  271.     If ca$ = cb$ Then
  272.         LTE = -1
  273.     ElseIf la < lb Then ' a is smaller
  274.         LTE = -1
  275.     ElseIf la > lb Then ' a is bigger
  276.         LTE = 0
  277.     ElseIf la = lb Then ' equal lengths
  278.         For i = 1 To Len(ca$)
  279.             If Val(Mid$(ca$, i, 1)) > Val(Mid$(cb$, i, 1)) Then
  280.                 LTE = 0: Exit Function
  281.             ElseIf Val(Mid$(ca$, i, 1)) < Val(Mid$(cb$, i, 1)) Then
  282.                 LTE = -1: Exit Function
  283.             End If
  284.         Next
  285.     End If
  286.  
  287. ' ------------------------------------- use these for final display
  288.  
  289. Function TrimLead0$ (s$) 'for treating strings as number (pos integers)
  290.     Dim copys$, i As Integer, find As Integer
  291.     copys$ = _Trim$(s$) 'might as well remove spaces too
  292.     i = 1: find = 0
  293.     While i < Len(copys$) And Mid$(copys$, i, 1) = "0"
  294.         i = i + 1: find = 1
  295.     Wend
  296.     If find = 1 Then copys$ = Mid$(copys$, i)
  297.     If copys$ = "" Then TrimLead0$ = "0" Else TrimLead0$ = copys$
  298.  
  299. Function TrimTail0$ (s$)
  300.     Dim copys$, dp As Integer, i As Integer, find As Integer
  301.     copys$ = _Trim$(s$) 'might as well remove spaces too
  302.     TrimTail0$ = copys$
  303.     dp = InStr(copys$, ".")
  304.     If dp > 0 Then
  305.         i = Len(copys$): find = 0
  306.         While i > dp And Mid$(copys$, i, 1) = "0"
  307.             i = i - 1: find = 1
  308.         Wend
  309.         If find = 1 Then
  310.             If i = dp Then
  311.                 TrimTail0$ = Mid$(copys$, 1, dp - 1)
  312.             Else
  313.                 TrimTail0$ = Mid$(copys$, 1, i)
  314.             End If
  315.         End If
  316.     End If
  317.  
  318. Function trim0$ (s$)
  319.     Dim cs$, si$
  320.     cs$ = s$
  321.     si$ = Left$(cs$, 1)
  322.     If si$ = "-" Then cs$ = Mid$(cs$, 2)
  323.     cs$ = TrimLead0$(cs$)
  324.     cs$ = TrimTail0$(cs$)
  325.     If Right$(cs$, 1) = "." Then cs$ = Mid$(cs$, 1, Len(cs$) - 1)
  326.     If si$ = "-" Then trim0$ = si$ + cs$ Else trim0$ = cs$
  327.  
  328. ' for displaying truncated numbers say to 60 digits
  329. Function showDP$ (num$, nDP As Integer)
  330.     Dim cNum$, dp As Integer, d As Integer, i As Integer
  331.     cNum$ = num$ 'since num$ could get changed
  332.     showDP$ = num$
  333.     dp = InStr(num$, ".")
  334.     If dp > 0 Then
  335.         If Len(Mid$(cNum$, dp + 1)) > nDP Then
  336.             d = Val(Mid$(cNum$, dp + nDP + 1, 1))
  337.             If d > 4 Then
  338.                 cNum$ = "0" + cNum$ ' tack on another 0 just in case 9's all the way to left
  339.                 dp = dp + 1
  340.                 i = dp + nDP
  341.                 While Mid$(cNum$, i, 1) = "9" Or Mid$(cNum$, i, 1) = "."
  342.                     If Mid$(cNum$, i, 1) = "9" Then
  343.                         Mid$(cNum$, i, 1) = "0"
  344.                     End If
  345.                     i = i - 1
  346.                 Wend
  347.                 Mid$(cNum$, i, 1) = _Trim$(Str$(Val(Mid$(cNum$, i, 1)) + 1)) 'last non 9 digit
  348.                 cNum$ = Mid$(cNum$, 1, dp + nDP) 'chop it
  349.                 showDP$ = trim0$(cNum$)
  350.             Else
  351.                 showDP$ = Mid$(cNum$, 1, dp + nDP)
  352.             End If
  353.         End If
  354.     End If
  355.  

Going back to oh to replace the Divide$ function there.

Here is a list of compares between Square Root from above code and an Internet Calculator (that rounds it's last digit in decimal place, whereas this code cuts off at that digit # not decimal place.)
Code: [Select]
First show code clipboard result then show Internet Calculator result
from https://www.mathsisfun.com/calculator-precision.html

Square root 2 first from String Math then Internet Result 
1.414213562373095048801688724209698078569671875376948073176679737990732478462107038850387534327641572
1.4142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415727

Square root of 3...
1.732050807568877293527446341505872366942805253810380628055806979451933016908800037081146186757248575
1.7320508075688772935274463415058723669428052538103806280558069794519330169088000370811461867572485757

Square root of 4...
gets stuck in infinite loop 1.999999999999999999999999....   
2 of course!

OK compare guess to last guess and if they match out to 105 digits then 100 digits must be accurate
1.999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
2 of course!

SR 5
2.236067977499789696409173668731276235440618359611525724270897245410520925637804899414414408378782274
2.23606797749978969640917366873127623544061835961152572427089724541052092563780489941441440837878227497

SR 6
2.449489742783178098197284074705891391965947480656670128432692567250960377457315026539859433104640234
2.44948974278317809819728407470589139196594748065667012843269256725096037745731502653985943310464023482

SR 7
2.645751311064590590501615753639260425710259183082450180368334459201068823230283627760392886474543610
2.64575131106459059050161575363926042571025918308245018036833445920106882323028362776039288647454361062

Title: Re: String Math
Post by: George McGinn on June 03, 2021, 03:15:45 pm
I will post it here again, but my square root function gives you the correct answer.

I use the  The Newton-Raphson Iteration method to calculate the square root as well as the n'th roots (see link: https://secure.math.ubc.ca/~anstee/math104/104newtonmethod.pdf (https://secure.math.ubc.ca/~anstee/math104/104newtonmethod.pdf)).

It is more accurate than your "best answer."

Here is the code to execute this method:

Code: QB64: [Select]
  1.  
  2. DECLARE FUNCTION root_2 (A)
  3. DECLARE FUNCTION root_n (A, n)
  4.  
  5. '*** square root function
  6. A = 4503599627370496: r = root_2(A): PRINT "square root of "; A; " equals "; result
  7. A = 125: r = root_2(A): PRINT "square root of "; A; " equals "; result
  8. A = 100: r = root_2(A): PRINT "square root of "; A; " equals "; result
  9.  
  10.  
  11. FUNCTION root_2 (A AS DOUBLE)
  12.     x = A / 2: precision = 0.000001: max_iterations = 30
  13.     FOR i = 1 TO max_iterations
  14.         dx = (x - A / x) / 2: x = x - dx
  15.         IF ABS(dx) < precision THEN i = max_iterations
  16.     NEXT i
  17.     result = STR$(x)




Attempting to extend String Math into Square Roots, I discovered a huge bug with Divide$ Function, it's all commented in code below. I think I have it patched but maybe not. This is tricky without Boolean Compares for String Math.

Main code is testing a 100 digit precision Square Root Code and Divide$ has the "fixed"? String Math patch
Code: QB64: [Select]
  1. _Title "Math regulator mr test sqr estimating" ' b+ start 2021-06-02
  2. Randomize Timer 'now that it's seems to be running silent
  3. Screen _NewImage(1200, 700, 32)
  4. _Delay .25
  5.  
  6. Dim n$, guess$, other$, cont$, sum$, loopCnt, lastGuess$
  7.     'remember everything is strings
  8.     Input "Enter a number to find it's square root "; n$
  9.     If n$ = "" Then End
  10.     guess$ = mr$(n$, "/", "2") ' for n$ = 2
  11.     other$ = n$
  12.     loopCnt = 0
  13.     Do
  14.         loopCnt = loopCnt + 1
  15.         Print loopCnt; " guess: "; guess$
  16.         'Input "Continue press enter, any other to quit "; cont$
  17.         'If cont$ = "" Then
  18.         If (Mid$(guess$, 1, 105) = Mid$(lastGuess$, 1, 105)) Then ' go past 100 matching digits for 100 digit precision
  19.             Print
  20.             Print "100 digits precision goal, not 100 digits past decimal: Square root of "; n$; " is:"
  21.             Print Mid$(guess$, 1, 101) ' one char for decimal
  22.             _Clipboard$ = Mid$(guess$, 1, 101) ' ditto
  23.             Print
  24.             Exit Do
  25.         Else
  26.             lastGuess$ = guess$
  27.             'Print "guess$ "; guess$
  28.             'Print "other$ "; other$
  29.             sum$ = mr$(guess$, "+", other$)
  30.             'Print "sum$ "; sum$
  31.             'Print "Call divide for guess$, sum$ / 2"
  32.             guess$ = mr$(sum$, "/", "2")
  33.             Print "New guess$ "; Mid$(guess$, 1, 105)
  34.             'Print "Call n$ divide  new guess$ for other$, n$ / guess$"
  35.             other$ = mr$(n$, "/", guess$)
  36.             Print "New other$ "; Mid$(other$, 1, 105)
  37.         End If
  38.         'Else
  39.         '    Exit Do
  40.         'End If
  41.     Loop
  42.  
  43. Function mr$ (a$, op$, b$) ' catchy? mr$ for math regulator
  44.     Dim ca$, cb$, aSgn$, bSgn$, postOp$, sgn$
  45.     Dim adp As Integer, bdp As Integer, dp As Integer, lpop As Integer
  46.  
  47.     op$ = _Trim$(op$) 'save fixing each time
  48.     ca$ = _Trim$(a$): cb$ = _Trim$(b$) 'make copies in case we change
  49.     'strip signs and decimals
  50.     If Left$(ca$, 1) = "-" Then
  51.         aSgn$ = "-": ca$ = Mid$(ca$, 2)
  52.     Else
  53.         aSgn$ = "": ca$ = ca$
  54.     End If
  55.     dp = InStr(ca$, ".")
  56.     If dp > 0 Then
  57.         adp = Len(ca$) - dp
  58.         ca$ = Mid$(ca$, 1, dp - 1) + Mid$(ca$, dp + 1)
  59.     Else
  60.         adp = 0
  61.     End If
  62.     If Left$(cb$, 1) = "-" Then
  63.         bSgn$ = "-": cb$ = Mid$(cb$, 2)
  64.     Else
  65.         bSgn$ = "": cb$ = cb$
  66.     End If
  67.     dp = InStr(cb$, ".")
  68.     If dp > 0 Then
  69.         bdp = Len(cb$) - dp
  70.         cb$ = Mid$(cb$, 1, dp - 1) + Mid$(cb$, dp + 1)
  71.     Else
  72.         bdp = 0
  73.     End If
  74.  
  75.     If op$ = "+" Or op$ = "-" Or op$ = "/" Then 'add or subtr  even up strings on right of decimal
  76.         'even up the right sides of decimals if any
  77.         If adp > bdp Then dp = adp Else dp = bdp
  78.         If adp < dp Then ca$ = ca$ + String$(dp - adp, "0")
  79.         If bdp < dp Then cb$ = cb$ + String$(dp - bdp, "0")
  80.     ElseIf op$ = "*" Then
  81.         dp = adp + bdp
  82.     End If
  83.     If op$ = "*" Or op$ = "/" Then
  84.         If aSgn$ = bSgn$ Then sgn$ = "" Else sgn$ = "-"
  85.     End If
  86.     'now according to signs and op$ call add$ or subtr$
  87.     If op$ = "+" Then
  88.         If aSgn$ = bSgn$ Then 'really add
  89.             postOp$ = aSgn$ + add$(ca$, cb$)
  90.         Else 'have a case of subtraction
  91.             If aSgn$ = "-" Then postOp$ = subtr$(cb$, ca$) Else postOp$ = subtr$(ca$, cb$)
  92.         End If
  93.     ElseIf op$ = "-" Then
  94.         If bSgn$ = "-" Then 'really add but switch b sign
  95.             bSgn$ = ""
  96.             If aSgn$ = "-" Then
  97.                 postOp$ = subtr$(cb$, ca$)
  98.             Else 'aSgn = ""
  99.                 postOp$ = add$(ca$, cb$)
  100.             End If
  101.         Else 'bSgn$ =""
  102.             If aSgn$ = "-" Then
  103.                 bSgn$ = "-"
  104.                 postOp$ = aSgn$ + add$(ca$, cb$)
  105.             Else
  106.                 postOp$ = subtr$(ca$, cb$)
  107.             End If
  108.         End If
  109.     ElseIf op$ = "*" Then
  110.         postOp$ = sgn$ + mult$(ca$, cb$)
  111.     ElseIf op$ = "/" Then
  112.         postOp$ = sgn$ + divide$(ca$, cb$)
  113.     End If ' which op
  114.     'put dp back
  115.     If op$ <> "/" Then
  116.         lpop = Len(postOp$) ' put decimal back
  117.         postOp$ = Mid$(postOp$, 1, lpop - dp) + "." + Mid$(postOp$, lpop - dp + 1)
  118.     End If
  119.     mr$ = trim0$(postOp$)
  120.  
  121. Function divide$ (n$, d$) ' goal here is 100 digits precision not 100 digits past decimal
  122.     'Print "divide$ got n$ = "; n$; " and d$ = "; d$
  123.     Dim di$, ndi$, nD As Integer
  124.     If trim0$(n$) = "0" Then divide$ = "0": Exit Function
  125.     If trim0$(d$) = "0" Then divide$ = "div 0": Exit Function
  126.     If trim0$(d$) = "1" Then divide$ = trim0$(n$): Exit Function '8/17 add trim0$
  127.  
  128.     ' aha! found a bug when d$ gets really huge 100 is no where near enough!!!!
  129.     ' 2021-06-03 fix by adding 100 to len(d$), plus have to go a little past 100 for 100 digit precision
  130.     ' need to go past 100 for 100 precise digits (not decimal places)
  131.     di$ = Mid$(nInverse$(d$, Len(d$) + 200), 2) 'chop off decimal point after
  132.     'Print "divide$ inverse of d$ = di$ = "; di$
  133.  
  134.     nD = Len(di$)
  135.     ndi$ = mult$(n$, di$)
  136.     ndi$ = Mid$(ndi$, 1, Len(ndi$) - nD) + "." + Right$(String$(nD, "0") + Right$(ndi$, nD), nD)
  137.     divide$ = trim0$(ndi$)
  138.  
  139. Function nInverse$ (n$, DP As Integer) 'assume decimal at very start of the string of digits returned, no rounding
  140.     Dim m$(1 To 9), si$, r$, outstr$, d$
  141.     Dim i As Integer
  142.     For i = 1 To 9
  143.         si$ = _Trim$(Str$(i))
  144.         m$(i) = mult$(si$, n$)
  145.     Next
  146.     outstr$ = ""
  147.     If n$ = "0" Then nInverse$ = "Div 0": Exit Function
  148.     If n$ = "1" Then nInverse$ = "1": Exit Function
  149.     outstr$ = "." 'everything else n > 1 is decimal 8/17
  150.     r$ = "10"
  151.     Do
  152.         While Left$(subtr$(r$, n$), 1) = "-" '   r - n < 0
  153.             outstr$ = outstr$ + "0" '            add 0 to the  output string
  154.             If Len(outstr$) = DP Then nInverse$ = outstr$: Exit Function 'check if we've reached DP length
  155.             r$ = r$ + "0"
  156.         Wend
  157.         For i = 9 To 1 Step -1
  158.             If LTE(m$(i), r$) Then d$ = _Trim$(Str$(i)): Exit For
  159.         Next
  160.         outstr$ = outstr$ + d$
  161.         If Len(outstr$) = DP Then nInverse$ = outstr$: Exit Function
  162.         r$ = subtr$(r$, mult$(d$, n$)) 'r = r -d*n
  163.         If r$ = "0" Then nInverse$ = outstr$: Exit Function 'found a perfect divisor
  164.         r$ = r$ + "0" 'add another place
  165.     Loop
  166.  
  167. Function mult$ (a$, b$) 'assume both positive integers prechecked as all digits strings
  168.     Dim la As Integer, lb As Integer, m As Integer, g As Integer, dp As Integer
  169.     Dim f18$, f1$, t$, build$, accum$
  170.  
  171.     If trim0$(a$) = "0" Then mult$ = "0": Exit Function
  172.     If trim0$(b$) = "0" Then mult$ = "0": Exit Function
  173.     If trim0$(a$) = "1" Then mult$ = trim0$(b$): Exit Function
  174.     If trim0$(b$) = "1" Then mult$ = trim0$(a$): Exit Function
  175.     'find the longer number and make it a mult of 18 to take 18 digits at a time from it
  176.     la = Len(a$): lb = Len(b$)
  177.     If la > lb Then
  178.         m = Int(la / 18) + 1
  179.         f18$ = Right$(String$(m * 18, "0") + a$, m * 18)
  180.         f1$ = b$
  181.     Else
  182.         m = Int(lb / 18) + 1
  183.         f18$ = Right$(String$(m * 18, "0") + b$, m * 18)
  184.         f1$ = a$
  185.     End If
  186.     For dp = Len(f1$) To 1 Step -1 'dp = digit position of the f1$
  187.         build$ = "" 'line builder
  188.         co = 0
  189.         'now taking 18 digits at a time Thanks Steve McNeill
  190.         For g = 1 To m
  191.             v18 = Val(Mid$(f18$, m * 18 - g * 18 + 1, 18))
  192.             sd = Val(Mid$(f1$, dp, 1))
  193.             t$ = Right$(String$(19, "0") + _Trim$(Str$(v18 * sd + co)), 19)
  194.             co = Val(Mid$(t$, 1, 1))
  195.             build$ = Mid$(t$, 2) + build$
  196.         Next g
  197.         If co Then build$ = _Trim$(Str$(co)) + build$
  198.         If dp = Len(f1$) Then
  199.             accum$ = build$
  200.         Else
  201.             accum$ = add$(accum$, build$ + String$(Len(f1$) - dp, "0"))
  202.         End If
  203.     Next dp
  204.     mult$ = accum$
  205.  
  206. Function subtr$ (sum$, minus$) ' assume both numbers are positive all digits
  207.     Dim m As Integer, g As Integer, p As Integer
  208.     Dim ts$, tm$, sign$, LG$, sm$, t$, result$
  209.  
  210.     ts$ = TrimLead0$(sum$): tm$ = TrimLead0$(minus$)
  211.     If trim0(ts$) = trim0$(tm$) Then subtr$ = "0": Exit Function 'OK proceed with function knowing they are not equal
  212.     tenE18 = 1000000000000000000 'yes!!! no dang E's
  213.     If LTE(ts$, tm$) Then ' which is bigger? minus is bigger
  214.         sign$ = "-"
  215.         m = Int(Len(tm$) / 18) + 1
  216.         LG$ = Right$(String$(m * 18, "0") + tm$, m * 18)
  217.         sm$ = Right$(String$(m * 18, "0") + ts$, m * 18)
  218.     Else 'sum is bigger
  219.         sign$ = ""
  220.         m = Int(Len(ts$) / 18) + 1
  221.         LG$ = Right$(String$(m * 18, "0") + ts$, m * 18)
  222.         sm$ = Right$(String$(m * 18, "0") + tm$, m * 18)
  223.     End If
  224.     'now taking 18 digits at a time From Steve I learned we can do more than 1 digit at a time
  225.     For g = 1 To m
  226.         VB = Val(Mid$(LG$, m * 18 - g * 18 + 1, 18))
  227.         vs = Val(Mid$(sm$, m * 18 - g * 18 + 1, 18))
  228.         If vs > VB Then
  229.             t$ = Right$(String$(18, "0") + _Trim$(Str$(tenE18 - vs + VB)), 18)
  230.             p = (m - g) * 18
  231.             While p > 0 And Mid$(LG$, p, 1) = "0"
  232.                 Mid$(LG$, p, 1) = "9"
  233.                 p = p - 1
  234.             Wend
  235.             If p > 0 Then Mid$(LG$, p, 1) = _Trim$(Str$(Val(Mid$(LG$, p, 1)) - 1))
  236.         Else
  237.             t$ = Right$(String$(18, "0") + _Trim$(Str$(VB - vs)), 18)
  238.         End If
  239.         result$ = t$ + result$
  240.     Next
  241.     subtr$ = sign$ + result$
  242.  
  243. Function add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no spaces or - signs
  244.     'first thing is to set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
  245.     Dim la As Integer, lb As Integer, m As Integer, g As Integer
  246.     Dim fa$, fb$, t$, new$, result$
  247.     la = Len(a$): lb = Len(b$)
  248.     If la > lb Then m = Int(la / 18) + 1 Else m = Int(lb / 18) + 1
  249.     fa$ = Right$(String$(m * 18, "0") + a$, m * 18)
  250.     fb$ = Right$(String$(m * 18, "0") + b$, m * 18)
  251.  
  252.     'now taking 18 digits at a time Thanks Steve McNeill
  253.     For g = 1 To m
  254.         sa = Val(Mid$(fa$, m * 18 - g * 18 + 1, 18))
  255.         sb = Val(Mid$(fb$, m * 18 - g * 18 + 1, 18))
  256.         t$ = Right$(String$(36, "0") + _Trim$(Str$(sa + sb + co)), 36)
  257.         co = Val(Mid$(t$, 1, 18))
  258.         new$ = Mid$(t$, 19)
  259.         result$ = new$ + result$
  260.     Next
  261.     If co Then result$ = Str$(co) + result$
  262.     add$ = result$
  263.  
  264. ' String Math Helpers -----------------------------------------------
  265.  
  266. 'this function needs TrimLead0$(s$)
  267. Function LTE (a$, b$) ' a$ Less Than or Equal b$  comparison of 2 strings, For Integers Only!
  268.     Dim ca$, cb$, la As Integer, lb As Integer, i As Integer
  269.     ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
  270.     la = Len(ca$): lb = Len(cb$)
  271.     If ca$ = cb$ Then
  272.         LTE = -1
  273.     ElseIf la < lb Then ' a is smaller
  274.         LTE = -1
  275.     ElseIf la > lb Then ' a is bigger
  276.         LTE = 0
  277.     ElseIf la = lb Then ' equal lengths
  278.         For i = 1 To Len(ca$)
  279.             If Val(Mid$(ca$, i, 1)) > Val(Mid$(cb$, i, 1)) Then
  280.                 LTE = 0: Exit Function
  281.             ElseIf Val(Mid$(ca$, i, 1)) < Val(Mid$(cb$, i, 1)) Then
  282.                 LTE = -1: Exit Function
  283.             End If
  284.         Next
  285.     End If
  286.  
  287. ' ------------------------------------- use these for final display
  288.  
  289. Function TrimLead0$ (s$) 'for treating strings as number (pos integers)
  290.     Dim copys$, i As Integer, find As Integer
  291.     copys$ = _Trim$(s$) 'might as well remove spaces too
  292.     i = 1: find = 0
  293.     While i < Len(copys$) And Mid$(copys$, i, 1) = "0"
  294.         i = i + 1: find = 1
  295.     Wend
  296.     If find = 1 Then copys$ = Mid$(copys$, i)
  297.     If copys$ = "" Then TrimLead0$ = "0" Else TrimLead0$ = copys$
  298.  
  299. Function TrimTail0$ (s$)
  300.     Dim copys$, dp As Integer, i As Integer, find As Integer
  301.     copys$ = _Trim$(s$) 'might as well remove spaces too
  302.     TrimTail0$ = copys$
  303.     dp = InStr(copys$, ".")
  304.     If dp > 0 Then
  305.         i = Len(copys$): find = 0
  306.         While i > dp And Mid$(copys$, i, 1) = "0"
  307.             i = i - 1: find = 1
  308.         Wend
  309.         If find = 1 Then
  310.             If i = dp Then
  311.                 TrimTail0$ = Mid$(copys$, 1, dp - 1)
  312.             Else
  313.                 TrimTail0$ = Mid$(copys$, 1, i)
  314.             End If
  315.         End If
  316.     End If
  317.  
  318. Function trim0$ (s$)
  319.     Dim cs$, si$
  320.     cs$ = s$
  321.     si$ = Left$(cs$, 1)
  322.     If si$ = "-" Then cs$ = Mid$(cs$, 2)
  323.     cs$ = TrimLead0$(cs$)
  324.     cs$ = TrimTail0$(cs$)
  325.     If Right$(cs$, 1) = "." Then cs$ = Mid$(cs$, 1, Len(cs$) - 1)
  326.     If si$ = "-" Then trim0$ = si$ + cs$ Else trim0$ = cs$
  327.  
  328. ' for displaying truncated numbers say to 60 digits
  329. Function showDP$ (num$, nDP As Integer)
  330.     Dim cNum$, dp As Integer, d As Integer, i As Integer
  331.     cNum$ = num$ 'since num$ could get changed
  332.     showDP$ = num$
  333.     dp = InStr(num$, ".")
  334.     If dp > 0 Then
  335.         If Len(Mid$(cNum$, dp + 1)) > nDP Then
  336.             d = Val(Mid$(cNum$, dp + nDP + 1, 1))
  337.             If d > 4 Then
  338.                 cNum$ = "0" + cNum$ ' tack on another 0 just in case 9's all the way to left
  339.                 dp = dp + 1
  340.                 i = dp + nDP
  341.                 While Mid$(cNum$, i, 1) = "9" Or Mid$(cNum$, i, 1) = "."
  342.                     If Mid$(cNum$, i, 1) = "9" Then
  343.                         Mid$(cNum$, i, 1) = "0"
  344.                     End If
  345.                     i = i - 1
  346.                 Wend
  347.                 Mid$(cNum$, i, 1) = _Trim$(Str$(Val(Mid$(cNum$, i, 1)) + 1)) 'last non 9 digit
  348.                 cNum$ = Mid$(cNum$, 1, dp + nDP) 'chop it
  349.                 showDP$ = trim0$(cNum$)
  350.             Else
  351.                 showDP$ = Mid$(cNum$, 1, dp + nDP)
  352.             End If
  353.         End If
  354.     End If
  355.  

Going back to oh to replace the Divide$ function there.

Here is a list of compares between Square Root from above code and an Internet Calculator (that rounds it's last digit in decimal place, whereas this code cuts off at that digit # not decimal place.)
Code: [Select]
First show code clipboard result then show Internet Calculator result
from https://www.mathsisfun.com/calculator-precision.html

Square root 2 first from String Math then Internet Result 
1.414213562373095048801688724209698078569671875376948073176679737990732478462107038850387534327641572
1.4142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415727

Square root of 3...
1.732050807568877293527446341505872366942805253810380628055806979451933016908800037081146186757248575
1.7320508075688772935274463415058723669428052538103806280558069794519330169088000370811461867572485757

Square root of 4...
gets stuck in infinite loop 1.999999999999999999999999....   
2 of course!

OK compare guess to last guess and if they match out to 105 digits then 100 digits must be accurate
1.999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
2 of course!

SR 5
2.236067977499789696409173668731276235440618359611525724270897245410520925637804899414414408378782274
2.23606797749978969640917366873127623544061835961152572427089724541052092563780489941441440837878227497

SR 6
2.449489742783178098197284074705891391965947480656670128432692567250960377457315026539859433104640234
2.44948974278317809819728407470589139196594748065667012843269256725096037745731502653985943310464023482

SR 7
2.645751311064590590501615753639260425710259183082450180368334459201068823230283627760392886474543610
2.64575131106459059050161575363926042571025918308245018036833445920106882323028362776039288647454361062
Title: Re: String Math
Post by: jack on June 03, 2021, 03:55:53 pm
hi bplus
here's some interesting code that if you manage to translate to QB64 might be useful as a first approximation for the Newton-Raphson Method
this is FreeBbasic code but the code is very short and the algorithm is explained in comments

Code: [Select]
'=======================================================================
' approximate a ^ b
Function approx_power( Byval a As Double, Byval b As Double ) As Double
    Dim As Long Ptr fp = 1 + Cptr( Long Ptr, @a )
    Dim As Long tmp = ( *fp - 1072632447 ) * b
    *fp = tmp + 1072632447
    Return a
End Function

'=======================================================================
' How and Why It Works.
' To accurately compute  y = a^b;  for non-integer b;  with a > 0;
'   take the log(a), multiply it by b, then look up the antilog, which is
'   the Exp() function, to give; y = Exp( b * Log( a ) )
' For a computer using base 2 it is convenient to use; y = 2^( b * Log2(a) )
' The IEEE double precision floating point format has an 11 bit, base two,
'   integer exponent. That will become the integer part of the log2(x) function.
'   It is simple to extract the exponent, multiply it by b, then return it to
'   the exponent, but that would be a clunky approximation because the exponent
'   is an integer count of powers of two with poor resolution.
' So that the multiply will change the result for small changes of a or power b,
'   the leading 20 mantissa bits are included as a false fractional extension to
'   the logarithmic exponent. That is the source of error in this approximation.
'   Note that the implicit 1 msb is missing from the mantissa.
' The exponent offset bias of 1022 must be removed by subtraction before the
'   multiplication. In the same operation an amount can be deducted from the
'   mantissa to significantly reduce the approximation error. The approximation
'   is computed from only the top 32 bits of the 64 bit double precision format.
' &bSeeeeeeeeeeeffffffffffffffffffff  Sign bit, exponent and fraction format
' &b00111111111000000000000000000000    1071644672 = 1022 * 2^20, shifted bias
' &b00000000000011110001001001111111        987775 = fudge amount
' &b00111111111011110001001001111111    1072632447 = sum is the magic number
'=======================================================================
dim as double x, y, z

Print approx_power( 123.456789, .5 ), sqr(123.456789)
here's the QB64 version
Code: QB64: [Select]
  1.     Sub memcpy (ByVal dest As _Offset, Byval source As _Offset, Byval bytes As Long)
  2.  
  3.  
  4. Print approx_power(20000, .5#), Sqr(20000#)
  5.  
  6. Function approx_power# (x#, y#)
  7.     Dim As Double a
  8.     Dim As Long tmp
  9.     a = x#
  10.     memcpy _Offset(tmp), _Offset(a) + 4, 4
  11.     tmp = (tmp - 1072632447) * y#
  12.     tmp = tmp + 1072632447
  13.     memcpy _Offset(a) + 4, _Offset(tmp), 4
  14.     approx_power = a
  15.  
all that's needed is to add the Newton-Raphson code, for reciprocal powers of integers it should give a fast approximation
Title: Re: String Math
Post by: bplus on June 03, 2021, 04:14:07 pm
Oh I see while I was making tiny improvements, @GeorgeMcGinn and @jack have suggested more.
OK I see you both like Newton-Raphson, I've see it like 50 years ago in math class so must be better. I will try and work it that way then.

In meantime I've chosen other$ to return with function because sometimes it nails the answer precisely without a bunch of .99999999's on end, not always but less than guess$. I am also handling negative numbers by tacking on an *i to the end of the number returned, better than error message, maybe.

Code: QB64: [Select]
  1. _Title "Math regulator mr test sqr estimating" ' b+ start 2021-06-02
  2. ' divide$ found fix? 2021-06-03 comment in that function
  3. ' 2021-06-03 update with new sqrRoot$ Function
  4.  
  5. Randomize Timer 'now that it's seems to be running silent
  6. Screen _NewImage(1200, 700, 32)
  7. _Delay .25
  8.  
  9. Dim n$, result$
  10.     'remember everything is strings
  11.     Input "Enter a number to find it's square root "; n$
  12.     If n$ = "" Then End
  13.     result$ = sqrRoot$(n$)
  14.     Print result$
  15.     Print "Length ="; Len(result$)
  16.     Print
  17.  
  18. Function sqrRoot$ (nmbr$)
  19.     Dim n$, guess$, lastGuess$, other$, sum$, imaginary$
  20.     If Left$(nmbr$, 1) = "-" Then 'handle neg numbers
  21.         imaginary$ = "*i": n$ = Mid$(nmbr$, 2)
  22.     Else
  23.         imaginary$ = "": n$ = nmbr$
  24.     End If
  25.     guess$ = mr$(n$, "/", "2")
  26.     other$ = n$
  27.     Do
  28.         If (Mid$(guess$, 1, 105) = Mid$(lastGuess$, 1, 105)) Then ' go past 100 matching digits for 100 digit precision
  29.             sqrRoot$ = Mid$(other$, 1, 101) + imaginary$
  30.             Exit Function
  31.         Else
  32.             lastGuess$ = guess$
  33.             sum$ = mr$(guess$, "+", other$)
  34.             guess$ = mr$(sum$, "/", "2")
  35.             other$ = mr$(n$, "/", guess$)
  36.         End If
  37.     Loop
  38.  
  39. Function mr$ (a$, op$, b$) ' catchy? mr$ for math regulator
  40.     Dim ca$, cb$, aSgn$, bSgn$, postOp$, sgn$
  41.     Dim adp As Integer, bdp As Integer, dp As Integer, lpop As Integer
  42.  
  43.     op$ = _Trim$(op$) 'save fixing each time
  44.     ca$ = _Trim$(a$): cb$ = _Trim$(b$) 'make copies in case we change
  45.     'strip signs and decimals
  46.     If Left$(ca$, 1) = "-" Then
  47.         aSgn$ = "-": ca$ = Mid$(ca$, 2)
  48.     Else
  49.         aSgn$ = "": ca$ = ca$
  50.     End If
  51.     dp = InStr(ca$, ".")
  52.     If dp > 0 Then
  53.         adp = Len(ca$) - dp
  54.         ca$ = Mid$(ca$, 1, dp - 1) + Mid$(ca$, dp + 1)
  55.     Else
  56.         adp = 0
  57.     End If
  58.     If Left$(cb$, 1) = "-" Then
  59.         bSgn$ = "-": cb$ = Mid$(cb$, 2)
  60.     Else
  61.         bSgn$ = "": cb$ = cb$
  62.     End If
  63.     dp = InStr(cb$, ".")
  64.     If dp > 0 Then
  65.         bdp = Len(cb$) - dp
  66.         cb$ = Mid$(cb$, 1, dp - 1) + Mid$(cb$, dp + 1)
  67.     Else
  68.         bdp = 0
  69.     End If
  70.  
  71.     If op$ = "+" Or op$ = "-" Or op$ = "/" Then 'add or subtr  even up strings on right of decimal
  72.         'even up the right sides of decimals if any
  73.         If adp > bdp Then dp = adp Else dp = bdp
  74.         If adp < dp Then ca$ = ca$ + String$(dp - adp, "0")
  75.         If bdp < dp Then cb$ = cb$ + String$(dp - bdp, "0")
  76.     ElseIf op$ = "*" Then
  77.         dp = adp + bdp
  78.     End If
  79.     If op$ = "*" Or op$ = "/" Then
  80.         If aSgn$ = bSgn$ Then sgn$ = "" Else sgn$ = "-"
  81.     End If
  82.     'now according to signs and op$ call add$ or subtr$
  83.     If op$ = "+" Then
  84.         If aSgn$ = bSgn$ Then 'really add
  85.             postOp$ = aSgn$ + add$(ca$, cb$)
  86.         Else 'have a case of subtraction
  87.             If aSgn$ = "-" Then postOp$ = subtr$(cb$, ca$) Else postOp$ = subtr$(ca$, cb$)
  88.         End If
  89.     ElseIf op$ = "-" Then
  90.         If bSgn$ = "-" Then 'really add but switch b sign
  91.             bSgn$ = ""
  92.             If aSgn$ = "-" Then
  93.                 postOp$ = subtr$(cb$, ca$)
  94.             Else 'aSgn = ""
  95.                 postOp$ = add$(ca$, cb$)
  96.             End If
  97.         Else 'bSgn$ =""
  98.             If aSgn$ = "-" Then
  99.                 bSgn$ = "-"
  100.                 postOp$ = aSgn$ + add$(ca$, cb$)
  101.             Else
  102.                 postOp$ = subtr$(ca$, cb$)
  103.             End If
  104.         End If
  105.     ElseIf op$ = "*" Then
  106.         postOp$ = sgn$ + mult$(ca$, cb$)
  107.     ElseIf op$ = "/" Then
  108.         postOp$ = sgn$ + divide$(ca$, cb$)
  109.     End If ' which op
  110.     'put dp back
  111.     If op$ <> "/" Then
  112.         lpop = Len(postOp$) ' put decimal back
  113.         postOp$ = Mid$(postOp$, 1, lpop - dp) + "." + Mid$(postOp$, lpop - dp + 1)
  114.     End If
  115.     mr$ = trim0$(postOp$)
  116.  
  117. Function divide$ (n$, d$) ' goal here is 100 digits precision not 100 digits past decimal
  118.     Dim di$, ndi$, nD As Integer
  119.     If trim0$(n$) = "0" Then divide$ = "0": Exit Function
  120.     If trim0$(d$) = "0" Then divide$ = "div 0": Exit Function
  121.     If trim0$(d$) = "1" Then divide$ = trim0$(n$): Exit Function '8/17 add trim0$
  122.  
  123.     ' aha! found a bug when d$ gets really huge 100 is no where near enough!!!!
  124.     ' 2021-06-03 fix by adding 100 to len(d$), plus have to go a little past 100 for 100 digit precision
  125.     ' need to go past 100 for 100 precise digits (not decimal places)
  126.     di$ = Mid$(nInverse$(d$, Len(d$) + 200), 2) 'chop off decimal point after
  127.     nD = Len(di$)
  128.     ndi$ = mult$(n$, di$)
  129.     ndi$ = Mid$(ndi$, 1, Len(ndi$) - nD) + "." + Right$(String$(nD, "0") + Right$(ndi$, nD), nD)
  130.     divide$ = trim0$(ndi$)
  131.  
  132. Function nInverse$ (n$, DP As Integer) 'assume decimal at very start of the string of digits returned, no rounding
  133.     Dim m$(1 To 9), si$, r$, outstr$, d$
  134.     Dim i As Integer
  135.     For i = 1 To 9
  136.         si$ = _Trim$(Str$(i))
  137.         m$(i) = mult$(si$, n$)
  138.     Next
  139.     outstr$ = ""
  140.     If n$ = "0" Then nInverse$ = "Div 0": Exit Function
  141.     If n$ = "1" Then nInverse$ = "1": Exit Function
  142.     outstr$ = "." 'everything else n > 1 is decimal 8/17
  143.     r$ = "10"
  144.     Do
  145.         While Left$(subtr$(r$, n$), 1) = "-" '   r - n < 0
  146.             outstr$ = outstr$ + "0" '            add 0 to the  output string
  147.             If Len(outstr$) = DP Then nInverse$ = outstr$: Exit Function 'check if we've reached DP length
  148.             r$ = r$ + "0"
  149.         Wend
  150.         For i = 9 To 1 Step -1
  151.             If LTE(m$(i), r$) Then d$ = _Trim$(Str$(i)): Exit For
  152.         Next
  153.         outstr$ = outstr$ + d$
  154.         If Len(outstr$) = DP Then nInverse$ = outstr$: Exit Function
  155.         r$ = subtr$(r$, mult$(d$, n$)) 'r = r -d*n
  156.         If r$ = "0" Then nInverse$ = outstr$: Exit Function 'found a perfect divisor
  157.         r$ = r$ + "0" 'add another place
  158.     Loop
  159.  
  160. Function mult$ (a$, b$) 'assume both positive integers prechecked as all digits strings
  161.     Dim la As Integer, lb As Integer, m As Integer, g As Integer, dp As Integer
  162.     Dim f18$, f1$, t$, build$, accum$
  163.  
  164.     If trim0$(a$) = "0" Then mult$ = "0": Exit Function
  165.     If trim0$(b$) = "0" Then mult$ = "0": Exit Function
  166.     If trim0$(a$) = "1" Then mult$ = trim0$(b$): Exit Function
  167.     If trim0$(b$) = "1" Then mult$ = trim0$(a$): Exit Function
  168.     'find the longer number and make it a mult of 18 to take 18 digits at a time from it
  169.     la = Len(a$): lb = Len(b$)
  170.     If la > lb Then
  171.         m = Int(la / 18) + 1
  172.         f18$ = Right$(String$(m * 18, "0") + a$, m * 18)
  173.         f1$ = b$
  174.     Else
  175.         m = Int(lb / 18) + 1
  176.         f18$ = Right$(String$(m * 18, "0") + b$, m * 18)
  177.         f1$ = a$
  178.     End If
  179.     For dp = Len(f1$) To 1 Step -1 'dp = digit position of the f1$
  180.         build$ = "" 'line builder
  181.         co = 0
  182.         'now taking 18 digits at a time Thanks Steve McNeill
  183.         For g = 1 To m
  184.             v18 = Val(Mid$(f18$, m * 18 - g * 18 + 1, 18))
  185.             sd = Val(Mid$(f1$, dp, 1))
  186.             t$ = Right$(String$(19, "0") + _Trim$(Str$(v18 * sd + co)), 19)
  187.             co = Val(Mid$(t$, 1, 1))
  188.             build$ = Mid$(t$, 2) + build$
  189.         Next g
  190.         If co Then build$ = _Trim$(Str$(co)) + build$
  191.         If dp = Len(f1$) Then
  192.             accum$ = build$
  193.         Else
  194.             accum$ = add$(accum$, build$ + String$(Len(f1$) - dp, "0"))
  195.         End If
  196.     Next dp
  197.     mult$ = accum$
  198.  
  199. Function subtr$ (sum$, minus$) ' assume both numbers are positive all digits
  200.     Dim m As Integer, g As Integer, p As Integer
  201.     Dim ts$, tm$, sign$, LG$, sm$, t$, result$
  202.  
  203.     ts$ = TrimLead0$(sum$): tm$ = TrimLead0$(minus$)
  204.     If trim0(ts$) = trim0$(tm$) Then subtr$ = "0": Exit Function 'OK proceed with function knowing they are not equal
  205.     tenE18 = 1000000000000000000 'yes!!! no dang E's
  206.     If LTE(ts$, tm$) Then ' which is bigger? minus is bigger
  207.         sign$ = "-"
  208.         m = Int(Len(tm$) / 18) + 1
  209.         LG$ = Right$(String$(m * 18, "0") + tm$, m * 18)
  210.         sm$ = Right$(String$(m * 18, "0") + ts$, m * 18)
  211.     Else 'sum is bigger
  212.         sign$ = ""
  213.         m = Int(Len(ts$) / 18) + 1
  214.         LG$ = Right$(String$(m * 18, "0") + ts$, m * 18)
  215.         sm$ = Right$(String$(m * 18, "0") + tm$, m * 18)
  216.     End If
  217.     'now taking 18 digits at a time From Steve I learned we can do more than 1 digit at a time
  218.     For g = 1 To m
  219.         VB = Val(Mid$(LG$, m * 18 - g * 18 + 1, 18))
  220.         vs = Val(Mid$(sm$, m * 18 - g * 18 + 1, 18))
  221.         If vs > VB Then
  222.             t$ = Right$(String$(18, "0") + _Trim$(Str$(tenE18 - vs + VB)), 18)
  223.             p = (m - g) * 18
  224.             While p > 0 And Mid$(LG$, p, 1) = "0"
  225.                 Mid$(LG$, p, 1) = "9"
  226.                 p = p - 1
  227.             Wend
  228.             If p > 0 Then Mid$(LG$, p, 1) = _Trim$(Str$(Val(Mid$(LG$, p, 1)) - 1))
  229.         Else
  230.             t$ = Right$(String$(18, "0") + _Trim$(Str$(VB - vs)), 18)
  231.         End If
  232.         result$ = t$ + result$
  233.     Next
  234.     subtr$ = sign$ + result$
  235.  
  236. Function add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no spaces or - signs
  237.     'first thing is to set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
  238.     Dim la As Integer, lb As Integer, m As Integer, g As Integer
  239.     Dim fa$, fb$, t$, new$, result$
  240.     la = Len(a$): lb = Len(b$)
  241.     If la > lb Then m = Int(la / 18) + 1 Else m = Int(lb / 18) + 1
  242.     fa$ = Right$(String$(m * 18, "0") + a$, m * 18)
  243.     fb$ = Right$(String$(m * 18, "0") + b$, m * 18)
  244.  
  245.     'now taking 18 digits at a time Thanks Steve McNeill
  246.     For g = 1 To m
  247.         sa = Val(Mid$(fa$, m * 18 - g * 18 + 1, 18))
  248.         sb = Val(Mid$(fb$, m * 18 - g * 18 + 1, 18))
  249.         t$ = Right$(String$(36, "0") + _Trim$(Str$(sa + sb + co)), 36)
  250.         co = Val(Mid$(t$, 1, 18))
  251.         new$ = Mid$(t$, 19)
  252.         result$ = new$ + result$
  253.     Next
  254.     If co Then result$ = Str$(co) + result$
  255.     add$ = result$
  256.  
  257. ' String Math Helpers -----------------------------------------------
  258.  
  259. 'this function needs TrimLead0$(s$)
  260. Function LTE (a$, b$) ' a$ Less Than or Equal b$  comparison of 2 strings
  261.     Dim ca$, cb$, la As Integer, lb As Integer, i As Integer
  262.     ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
  263.     la = Len(ca$): lb = Len(cb$)
  264.     If ca$ = cb$ Then
  265.         LTE = -1
  266.     ElseIf la < lb Then ' a is smaller
  267.         LTE = -1
  268.     ElseIf la > lb Then ' a is bigger
  269.         LTE = 0
  270.     ElseIf la = lb Then ' equal lengths
  271.         For i = 1 To Len(ca$)
  272.             If Val(Mid$(ca$, i, 1)) > Val(Mid$(cb$, i, 1)) Then
  273.                 LTE = 0: Exit Function
  274.             ElseIf Val(Mid$(ca$, i, 1)) < Val(Mid$(cb$, i, 1)) Then
  275.                 LTE = -1: Exit Function
  276.             End If
  277.         Next
  278.     End If
  279.  
  280. ' ------------------------------------- use these for final display
  281.  
  282. Function TrimLead0$ (s$) 'for treating strings as number (pos integers)
  283.     Dim copys$, i As Integer, find As Integer
  284.     copys$ = _Trim$(s$) 'might as well remove spaces too
  285.     i = 1: find = 0
  286.     While i < Len(copys$) And Mid$(copys$, i, 1) = "0"
  287.         i = i + 1: find = 1
  288.     Wend
  289.     If find = 1 Then copys$ = Mid$(copys$, i)
  290.     If copys$ = "" Then TrimLead0$ = "0" Else TrimLead0$ = copys$
  291.  
  292. Function TrimTail0$ (s$)
  293.     Dim copys$, dp As Integer, i As Integer, find As Integer
  294.     copys$ = _Trim$(s$) 'might as well remove spaces too
  295.     TrimTail0$ = copys$
  296.     dp = InStr(copys$, ".")
  297.     If dp > 0 Then
  298.         i = Len(copys$): find = 0
  299.         While i > dp And Mid$(copys$, i, 1) = "0"
  300.             i = i - 1: find = 1
  301.         Wend
  302.         If find = 1 Then
  303.             If i = dp Then
  304.                 TrimTail0$ = Mid$(copys$, 1, dp - 1)
  305.             Else
  306.                 TrimTail0$ = Mid$(copys$, 1, i)
  307.             End If
  308.         End If
  309.     End If
  310.  
  311. Function trim0$ (s$)
  312.     Dim cs$, si$
  313.     cs$ = s$
  314.     si$ = Left$(cs$, 1)
  315.     If si$ = "-" Then cs$ = Mid$(cs$, 2)
  316.     cs$ = TrimLead0$(cs$)
  317.     cs$ = TrimTail0$(cs$)
  318.     If Right$(cs$, 1) = "." Then cs$ = Mid$(cs$, 1, Len(cs$) - 1)
  319.     If si$ = "-" Then trim0$ = si$ + cs$ Else trim0$ = cs$
  320.  
  321. ' for displaying truncated numbers say to 60 digits
  322. Function showDP$ (num$, nDP As Integer)
  323.     Dim cNum$, dp As Integer, d As Integer, i As Integer
  324.     cNum$ = num$ 'since num$ could get changed
  325.     showDP$ = num$
  326.     dp = InStr(num$, ".")
  327.     If dp > 0 Then
  328.         If Len(Mid$(cNum$, dp + 1)) > nDP Then
  329.             d = Val(Mid$(cNum$, dp + nDP + 1, 1))
  330.             If d > 4 Then
  331.                 cNum$ = "0" + cNum$ ' tack on another 0 just in case 9's all the way to left
  332.                 dp = dp + 1
  333.                 i = dp + nDP
  334.                 While Mid$(cNum$, i, 1) = "9" Or Mid$(cNum$, i, 1) = "."
  335.                     If Mid$(cNum$, i, 1) = "9" Then
  336.                         Mid$(cNum$, i, 1) = "0"
  337.                     End If
  338.                     i = i - 1
  339.                 Wend
  340.                 Mid$(cNum$, i, 1) = _Trim$(Str$(Val(Mid$(cNum$, i, 1)) + 1)) 'last non 9 digit
  341.                 cNum$ = Mid$(cNum$, 1, dp + nDP) 'chop it
  342.                 showDP$ = trim0$(cNum$)
  343.             Else
  344.                 showDP$ = Mid$(cNum$, 1, dp + nDP)
  345.             End If
  346.         End If
  347.     End If
  348.  



I was all set to make this the new Best Answer but I will investigate adapting Newton-Raphson, a better start from jack and a better method from george.
Title: Re: String Math
Post by: Pete on June 03, 2021, 06:20:31 pm
My advice would be to pick any one of the many methods available, especially one you're comfortable with, and get it coded. I looked into it a while back, but I would extended my project into the time I wanted reserved for other projects. I mean square roots... and then... log, sine, cosign, etc. The list can get pretty long. I'm glad to see more bells and whistles are going into string math, as I do believe it is a valuable project for anyone who wants to work with very large figures, like Jenny Craig.

Pete
Title: Re: String Math
Post by: jack on June 03, 2021, 06:32:09 pm
bplus, maybe this could work
Code: QB64: [Select]
  1.     Sub memcpy (ByVal dest As _Offset, Byval source As _Offset, Byval bytes As Long)
  2.  
  3.  
  4. Print approx_power(20000, 2#), Sqr(20000#)
  5.  
  6. x# = 1D100: y# = 7
  7. Print approx_power(x#, y#), x# ^ (1 / y#)
  8.  
  9. Function ipow# (x As Double, e As Integer)
  10.     'take x to an integer power
  11.     Dim As Double z, y
  12.     Dim As Integer n
  13.     y = x
  14.     n = Abs(e)
  15.     z = 1#
  16.     While n > 0
  17.         While (n Mod 2) = 0
  18.             n = n \ 2
  19.             y = y * y
  20.         Wend
  21.         n = n - 1
  22.         z = y * z
  23.     Wend
  24.     If e < 0 Then
  25.         ipow = 1# / z
  26.     Else
  27.         ipow = z
  28.     End If
  29.  
  30. Function approx_power# (x#, y#)
  31.     Dim As Double a, b
  32.     Dim As Long tmp
  33.     a = x#: b = 1# / y#
  34.     memcpy _Offset(tmp), _Offset(a) + 4, 4
  35.     tmp = (tmp - 1072632447) * b
  36.     tmp = tmp + 1072632447
  37.     memcpy _Offset(a) + 4, _Offset(tmp), 4
  38.     a = a - (ipow(a, y#) - x#) / (y# * ipow(a, y# - 1))
  39.     a = a - (ipow(a, y#) - x#) / (y# * ipow(a, y# - 1))
  40.     a = a - (ipow(a, y#) - x#) / (y# * ipow(a, y# - 1))
  41.     a = a - (ipow(a, y#) - x#) / (y# * ipow(a, y# - 1))
  42.     approx_power = a
  43.  
Title: Re: String Math
Post by: bplus on June 03, 2021, 08:43:39 pm
I have the first sqrRoot$ compared side by side with the Square Root by Newton-Raphson Function (SRbyNR$) the first makes 3 calls to Mr$ Function while SRbyNR$ makes 5 per loop. I think it's noticeable but the real killer is . 20 0's 64, the first method returns a good result the 2nd is lost in infinite loop.

Code: QB64: [Select]
  1. _Title "Math regulator mr test sqr estimating" ' b+ start 2021-06-02
  2. ' divide$ found fix? 2021-06-03 comment in that function
  3. ' 2021-06-03 update with new sqrRoot$ Function
  4.  
  5. Randomize Timer 'now that it's seems to be running silent
  6. Screen _NewImage(1200, 700, 32)
  7. _Delay .25
  8.  
  9. Dim n$, result$
  10.     'remember everything is strings
  11.     Input "Enter a number to find it's square root "; n$
  12.     If n$ = "" Then End
  13.     result$ = sqrRoot$(n$)
  14.     Print result$
  15.     Print "Length ="; Len(result$)
  16.     _Delay 2
  17.     result$ = SRbyNR$(n$)
  18.     Print result$
  19.     Print "Length ="; Len(result$)
  20.     Print
  21.  
  22. Function sqrRoot$ (nmbr$)
  23.     Dim n$, guess$, lastGuess$, other$, sum$, imaginary$, loopcnt
  24.     If Left$(nmbr$, 1) = "-" Then 'handle neg numbers
  25.         imaginary$ = "*i": n$ = Mid$(nmbr$, 2)
  26.     Else
  27.         imaginary$ = "": n$ = nmbr$
  28.     End If
  29.     guess$ = mr$(n$, "/", "2")
  30.     other$ = n$
  31.     Do
  32.         loopcnt = loopcnt + 1
  33.         Print "loop cnt"; loopcnt
  34.  
  35.         If (Mid$(guess$, 1, 105) = Mid$(lastGuess$, 1, 105)) Then ' go past 100 matching digits for 100 digit precision
  36.             sqrRoot$ = Mid$(other$, 1, 101) + imaginary$ ' try other factor for guess$  sometimes it nails answer without all digits
  37.             Exit Function
  38.         Else
  39.             lastGuess$ = guess$
  40.             sum$ = mr$(guess$, "+", other$)
  41.             guess$ = mr$(sum$, "/", "2")
  42.             other$ = mr$(n$, "/", guess$)
  43.         End If
  44.     Loop
  45.  
  46. Function SRbyNR$ (nmbr$) ' square root by Newton - Ralphson method my interpretation of GeorgeMcGinn
  47.  
  48.     Dim n$, guess$, lastGuess$, dx$, imaginary$, other$, loopcnt
  49.     If Left$(nmbr$, 1) = "-" Then 'handle neg numbers
  50.         imaginary$ = "*i": n$ = Mid$(nmbr$, 2)
  51.     Else
  52.         imaginary$ = "": n$ = nmbr$
  53.     End If
  54.  
  55.     guess$ = mr$(n$, "/", "2") ' get this going first and then try better starting guess later
  56.  
  57.     Do
  58.         loopcnt = loopcnt + 1
  59.         Print "loop cnt"; loopcnt
  60.         If (Mid$(guess$, 1, 105) = Mid$(lastGuess$, 1, 105)) Then ' go past 100 matching digits for 100 digit precision
  61.             SRbyNR$ = Mid$(other$, 1, 101) + imaginary$
  62.             Exit Function
  63.         Else
  64.             'dx = (x - A / x) / 2: x = x - dx ' Thanks George
  65.             lastGuess$ = guess$
  66.             dx$ = mr$(mr$(guess$, "-", mr$(n$, "/", guess$)), "/", "2")
  67.             guess$ = mr$(guess$, "-", dx$)
  68.             other$ = mr$(n$, "/", guess$) ' try other factor for guess$  sometimes it nails answer without all digits
  69.         End If
  70.     Loop
  71.  
  72.  
  73. Function mr$ (a$, op$, b$) ' catchy? mr$ for math regulator
  74.     Dim ca$, cb$, aSgn$, bSgn$, postOp$, sgn$
  75.     Dim adp As Integer, bdp As Integer, dp As Integer, lpop As Integer
  76.  
  77.     op$ = _Trim$(op$) 'save fixing each time
  78.     ca$ = _Trim$(a$): cb$ = _Trim$(b$) 'make copies in case we change
  79.     'strip signs and decimals
  80.     If Left$(ca$, 1) = "-" Then
  81.         aSgn$ = "-": ca$ = Mid$(ca$, 2)
  82.     Else
  83.         aSgn$ = "": ca$ = ca$
  84.     End If
  85.     dp = InStr(ca$, ".")
  86.     If dp > 0 Then
  87.         adp = Len(ca$) - dp
  88.         ca$ = Mid$(ca$, 1, dp - 1) + Mid$(ca$, dp + 1)
  89.     Else
  90.         adp = 0
  91.     End If
  92.     If Left$(cb$, 1) = "-" Then
  93.         bSgn$ = "-": cb$ = Mid$(cb$, 2)
  94.     Else
  95.         bSgn$ = "": cb$ = cb$
  96.     End If
  97.     dp = InStr(cb$, ".")
  98.     If dp > 0 Then
  99.         bdp = Len(cb$) - dp
  100.         cb$ = Mid$(cb$, 1, dp - 1) + Mid$(cb$, dp + 1)
  101.     Else
  102.         bdp = 0
  103.     End If
  104.  
  105.     If op$ = "+" Or op$ = "-" Or op$ = "/" Then 'add or subtr  even up strings on right of decimal
  106.         'even up the right sides of decimals if any
  107.         If adp > bdp Then dp = adp Else dp = bdp
  108.         If adp < dp Then ca$ = ca$ + String$(dp - adp, "0")
  109.         If bdp < dp Then cb$ = cb$ + String$(dp - bdp, "0")
  110.     ElseIf op$ = "*" Then
  111.         dp = adp + bdp
  112.     End If
  113.     If op$ = "*" Or op$ = "/" Then
  114.         If aSgn$ = bSgn$ Then sgn$ = "" Else sgn$ = "-"
  115.     End If
  116.     'now according to signs and op$ call add$ or subtr$
  117.     If op$ = "+" Then
  118.         If aSgn$ = bSgn$ Then 'really add
  119.             postOp$ = aSgn$ + add$(ca$, cb$)
  120.         Else 'have a case of subtraction
  121.             If aSgn$ = "-" Then postOp$ = subtr$(cb$, ca$) Else postOp$ = subtr$(ca$, cb$)
  122.         End If
  123.     ElseIf op$ = "-" Then
  124.         If bSgn$ = "-" Then 'really add but switch b sign
  125.             bSgn$ = ""
  126.             If aSgn$ = "-" Then
  127.                 postOp$ = subtr$(cb$, ca$)
  128.             Else 'aSgn = ""
  129.                 postOp$ = add$(ca$, cb$)
  130.             End If
  131.         Else 'bSgn$ =""
  132.             If aSgn$ = "-" Then
  133.                 bSgn$ = "-"
  134.                 postOp$ = aSgn$ + add$(ca$, cb$)
  135.             Else
  136.                 postOp$ = subtr$(ca$, cb$)
  137.             End If
  138.         End If
  139.     ElseIf op$ = "*" Then
  140.         postOp$ = sgn$ + mult$(ca$, cb$)
  141.     ElseIf op$ = "/" Then
  142.         postOp$ = sgn$ + divide$(ca$, cb$)
  143.     End If ' which op
  144.     'put dp back
  145.     If op$ <> "/" Then
  146.         lpop = Len(postOp$) ' put decimal back
  147.         postOp$ = Mid$(postOp$, 1, lpop - dp) + "." + Mid$(postOp$, lpop - dp + 1)
  148.     End If
  149.     mr$ = trim0$(postOp$)
  150.  
  151. Function divide$ (n$, d$) ' goal here is 100 digits precision not 100 digits past decimal
  152.     Dim di$, ndi$, nD As Integer
  153.     If trim0$(n$) = "0" Then divide$ = "0": Exit Function
  154.     If trim0$(d$) = "0" Then divide$ = "div 0": Exit Function
  155.     If trim0$(d$) = "1" Then divide$ = trim0$(n$): Exit Function '8/17 add trim0$
  156.  
  157.     ' aha! found a bug when d$ gets really huge 100 is no where near enough!!!!
  158.     ' 2021-06-03 fix by adding 100 to len(d$), plus have to go a little past 100 for 100 digit precision
  159.     ' need to go past 100 for 100 precise digits (not decimal places)
  160.     di$ = Mid$(nInverse$(d$, Len(d$) + 200), 2) 'chop off decimal point after
  161.     nD = Len(di$)
  162.     ndi$ = mult$(n$, di$)
  163.     ndi$ = Mid$(ndi$, 1, Len(ndi$) - nD) + "." + Right$(String$(nD, "0") + Right$(ndi$, nD), nD)
  164.     divide$ = trim0$(ndi$)
  165.  
  166. Function nInverse$ (n$, DP As Integer) 'assume decimal at very start of the string of digits returned, no rounding
  167.     Dim m$(1 To 9), si$, r$, outstr$, d$
  168.     Dim i As Integer
  169.     For i = 1 To 9
  170.         si$ = _Trim$(Str$(i))
  171.         m$(i) = mult$(si$, n$)
  172.     Next
  173.     outstr$ = ""
  174.     If n$ = "0" Then nInverse$ = "Div 0": Exit Function
  175.     If n$ = "1" Then nInverse$ = "1": Exit Function
  176.     outstr$ = "." 'everything else n > 1 is decimal 8/17
  177.     r$ = "10"
  178.     Do
  179.         While Left$(subtr$(r$, n$), 1) = "-" '   r - n < 0
  180.             outstr$ = outstr$ + "0" '            add 0 to the  output string
  181.             If Len(outstr$) = DP Then nInverse$ = outstr$: Exit Function 'check if we've reached DP length
  182.             r$ = r$ + "0"
  183.         Wend
  184.         For i = 9 To 1 Step -1
  185.             If LTE(m$(i), r$) Then d$ = _Trim$(Str$(i)): Exit For
  186.         Next
  187.         outstr$ = outstr$ + d$
  188.         If Len(outstr$) = DP Then nInverse$ = outstr$: Exit Function
  189.         r$ = subtr$(r$, mult$(d$, n$)) 'r = r -d*n
  190.         If r$ = "0" Then nInverse$ = outstr$: Exit Function 'found a perfect divisor
  191.         r$ = r$ + "0" 'add another place
  192.     Loop
  193.  
  194. Function mult$ (a$, b$) 'assume both positive integers prechecked as all digits strings
  195.     Dim la As Integer, lb As Integer, m As Integer, g As Integer, dp As Integer
  196.     Dim f18$, f1$, t$, build$, accum$
  197.  
  198.     If trim0$(a$) = "0" Then mult$ = "0": Exit Function
  199.     If trim0$(b$) = "0" Then mult$ = "0": Exit Function
  200.     If trim0$(a$) = "1" Then mult$ = trim0$(b$): Exit Function
  201.     If trim0$(b$) = "1" Then mult$ = trim0$(a$): Exit Function
  202.     'find the longer number and make it a mult of 18 to take 18 digits at a time from it
  203.     la = Len(a$): lb = Len(b$)
  204.     If la > lb Then
  205.         m = Int(la / 18) + 1
  206.         f18$ = Right$(String$(m * 18, "0") + a$, m * 18)
  207.         f1$ = b$
  208.     Else
  209.         m = Int(lb / 18) + 1
  210.         f18$ = Right$(String$(m * 18, "0") + b$, m * 18)
  211.         f1$ = a$
  212.     End If
  213.     For dp = Len(f1$) To 1 Step -1 'dp = digit position of the f1$
  214.         build$ = "" 'line builder
  215.         co = 0
  216.         'now taking 18 digits at a time Thanks Steve McNeill
  217.         For g = 1 To m
  218.             v18 = Val(Mid$(f18$, m * 18 - g * 18 + 1, 18))
  219.             sd = Val(Mid$(f1$, dp, 1))
  220.             t$ = Right$(String$(19, "0") + _Trim$(Str$(v18 * sd + co)), 19)
  221.             co = Val(Mid$(t$, 1, 1))
  222.             build$ = Mid$(t$, 2) + build$
  223.         Next g
  224.         If co Then build$ = _Trim$(Str$(co)) + build$
  225.         If dp = Len(f1$) Then
  226.             accum$ = build$
  227.         Else
  228.             accum$ = add$(accum$, build$ + String$(Len(f1$) - dp, "0"))
  229.         End If
  230.     Next dp
  231.     mult$ = accum$
  232.  
  233. Function subtr$ (sum$, minus$) ' assume both numbers are positive all digits
  234.     Dim m As Integer, g As Integer, p As Integer
  235.     Dim ts$, tm$, sign$, LG$, sm$, t$, result$
  236.  
  237.     ts$ = TrimLead0$(sum$): tm$ = TrimLead0$(minus$)
  238.     If trim0(ts$) = trim0$(tm$) Then subtr$ = "0": Exit Function 'OK proceed with function knowing they are not equal
  239.     tenE18 = 1000000000000000000 'yes!!! no dang E's
  240.     If LTE(ts$, tm$) Then ' which is bigger? minus is bigger
  241.         sign$ = "-"
  242.         m = Int(Len(tm$) / 18) + 1
  243.         LG$ = Right$(String$(m * 18, "0") + tm$, m * 18)
  244.         sm$ = Right$(String$(m * 18, "0") + ts$, m * 18)
  245.     Else 'sum is bigger
  246.         sign$ = ""
  247.         m = Int(Len(ts$) / 18) + 1
  248.         LG$ = Right$(String$(m * 18, "0") + ts$, m * 18)
  249.         sm$ = Right$(String$(m * 18, "0") + tm$, m * 18)
  250.     End If
  251.     'now taking 18 digits at a time From Steve I learned we can do more than 1 digit at a time
  252.     For g = 1 To m
  253.         VB = Val(Mid$(LG$, m * 18 - g * 18 + 1, 18))
  254.         vs = Val(Mid$(sm$, m * 18 - g * 18 + 1, 18))
  255.         If vs > VB Then
  256.             t$ = Right$(String$(18, "0") + _Trim$(Str$(tenE18 - vs + VB)), 18)
  257.             p = (m - g) * 18
  258.             While p > 0 And Mid$(LG$, p, 1) = "0"
  259.                 Mid$(LG$, p, 1) = "9"
  260.                 p = p - 1
  261.             Wend
  262.             If p > 0 Then Mid$(LG$, p, 1) = _Trim$(Str$(Val(Mid$(LG$, p, 1)) - 1))
  263.         Else
  264.             t$ = Right$(String$(18, "0") + _Trim$(Str$(VB - vs)), 18)
  265.         End If
  266.         result$ = t$ + result$
  267.     Next
  268.     subtr$ = sign$ + result$
  269.  
  270. Function add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no spaces or - signs
  271.     'first thing is to set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
  272.     Dim la As Integer, lb As Integer, m As Integer, g As Integer
  273.     Dim fa$, fb$, t$, new$, result$
  274.     la = Len(a$): lb = Len(b$)
  275.     If la > lb Then m = Int(la / 18) + 1 Else m = Int(lb / 18) + 1
  276.     fa$ = Right$(String$(m * 18, "0") + a$, m * 18)
  277.     fb$ = Right$(String$(m * 18, "0") + b$, m * 18)
  278.  
  279.     'now taking 18 digits at a time Thanks Steve McNeill
  280.     For g = 1 To m
  281.         sa = Val(Mid$(fa$, m * 18 - g * 18 + 1, 18))
  282.         sb = Val(Mid$(fb$, m * 18 - g * 18 + 1, 18))
  283.         t$ = Right$(String$(36, "0") + _Trim$(Str$(sa + sb + co)), 36)
  284.         co = Val(Mid$(t$, 1, 18))
  285.         new$ = Mid$(t$, 19)
  286.         result$ = new$ + result$
  287.     Next
  288.     If co Then result$ = Str$(co) + result$
  289.     add$ = result$
  290.  
  291. ' String Math Helpers -----------------------------------------------
  292.  
  293. 'this function needs TrimLead0$(s$)
  294. Function LTE (a$, b$) ' a$ Less Than or Equal b$  comparison of 2 strings
  295.     Dim ca$, cb$, la As Integer, lb As Integer, i As Integer
  296.     ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
  297.     la = Len(ca$): lb = Len(cb$)
  298.     If ca$ = cb$ Then
  299.         LTE = -1
  300.     ElseIf la < lb Then ' a is smaller
  301.         LTE = -1
  302.     ElseIf la > lb Then ' a is bigger
  303.         LTE = 0
  304.     ElseIf la = lb Then ' equal lengths
  305.         For i = 1 To Len(ca$)
  306.             If Val(Mid$(ca$, i, 1)) > Val(Mid$(cb$, i, 1)) Then
  307.                 LTE = 0: Exit Function
  308.             ElseIf Val(Mid$(ca$, i, 1)) < Val(Mid$(cb$, i, 1)) Then
  309.                 LTE = -1: Exit Function
  310.             End If
  311.         Next
  312.     End If
  313.  
  314. ' ------------------------------------- use these for final display
  315.  
  316. Function TrimLead0$ (s$) 'for treating strings as number (pos integers)
  317.     Dim copys$, i As Integer, find As Integer
  318.     copys$ = _Trim$(s$) 'might as well remove spaces too
  319.     i = 1: find = 0
  320.     While i < Len(copys$) And Mid$(copys$, i, 1) = "0"
  321.         i = i + 1: find = 1
  322.     Wend
  323.     If find = 1 Then copys$ = Mid$(copys$, i)
  324.     If copys$ = "" Then TrimLead0$ = "0" Else TrimLead0$ = copys$
  325.  
  326. Function TrimTail0$ (s$)
  327.     Dim copys$, dp As Integer, i As Integer, find As Integer
  328.     copys$ = _Trim$(s$) 'might as well remove spaces too
  329.     TrimTail0$ = copys$
  330.     dp = InStr(copys$, ".")
  331.     If dp > 0 Then
  332.         i = Len(copys$): find = 0
  333.         While i > dp And Mid$(copys$, i, 1) = "0"
  334.             i = i - 1: find = 1
  335.         Wend
  336.         If find = 1 Then
  337.             If i = dp Then
  338.                 TrimTail0$ = Mid$(copys$, 1, dp - 1)
  339.             Else
  340.                 TrimTail0$ = Mid$(copys$, 1, i)
  341.             End If
  342.         End If
  343.     End If
  344.  
  345. Function trim0$ (s$)
  346.     Dim cs$, si$
  347.     cs$ = s$
  348.     si$ = Left$(cs$, 1)
  349.     If si$ = "-" Then cs$ = Mid$(cs$, 2)
  350.     cs$ = TrimLead0$(cs$)
  351.     cs$ = TrimTail0$(cs$)
  352.     If Right$(cs$, 1) = "." Then cs$ = Mid$(cs$, 1, Len(cs$) - 1)
  353.     If si$ = "-" Then trim0$ = si$ + cs$ Else trim0$ = cs$
  354.  
  355. ' for displaying truncated numbers say to 60 digits
  356. Function showDP$ (num$, nDP As Integer)
  357.     Dim cNum$, dp As Integer, d As Integer, i As Integer
  358.     cNum$ = num$ 'since num$ could get changed
  359.     showDP$ = num$
  360.     dp = InStr(num$, ".")
  361.     If dp > 0 Then
  362.         If Len(Mid$(cNum$, dp + 1)) > nDP Then
  363.             d = Val(Mid$(cNum$, dp + nDP + 1, 1))
  364.             If d > 4 Then
  365.                 cNum$ = "0" + cNum$ ' tack on another 0 just in case 9's all the way to left
  366.                 dp = dp + 1
  367.                 i = dp + nDP
  368.                 While Mid$(cNum$, i, 1) = "9" Or Mid$(cNum$, i, 1) = "."
  369.                     If Mid$(cNum$, i, 1) = "9" Then
  370.                         Mid$(cNum$, i, 1) = "0"
  371.                     End If
  372.                     i = i - 1
  373.                 Wend
  374.                 Mid$(cNum$, i, 1) = _Trim$(Str$(Val(Mid$(cNum$, i, 1)) + 1)) 'last non 9 digit
  375.                 cNum$ = Mid$(cNum$, 1, dp + nDP) 'chop it
  376.                 showDP$ = trim0$(cNum$)
  377.             Else
  378.                 showDP$ = Mid$(cNum$, 1, dp + nDP)
  379.             End If
  380.         End If
  381.     End If
  382.  
Title: Re: String Math
Post by: jack on June 03, 2021, 09:15:31 pm
hi bplus
here's my version of the Newton-Raphson, I had to use a for loop for the iterations as I found no way to use a do loop but it's much faster especially as the input numbers are larger
Code: QB64: [Select]
  1. _Title "Math regulator mr test sqr estimating" ' b+ start 2021-06-02
  2. ' divide$ found fix? 2021-06-03 comment in that function
  3. ' 2021-06-03 update with new sqrRoot$ Function
  4.  
  5. Randomize Timer 'now that it's seems to be running silent
  6. Screen _NewImage(1200, 700, 32)
  7. _Delay .25
  8.  
  9. Dim n$, result$
  10. Dim t#
  11.     'remember everything is strings
  12.     Input "Enter a number to find it's square root "; n$
  13.     If n$ = "" Then End
  14.     t# = Timer
  15.     result$ = sqrRoot$(n$)
  16.     Print Timer - t#, result$
  17.     t# = Timer
  18.     result$ = sqrt$(n$)
  19.     Print Timer - t#, result$
  20.     Print "Length ="; Len(result$)
  21.     Print
  22.  
  23. Function sqrRoot$ (nmbr$)
  24.     Dim n$, guess$, lastGuess$, other$, sum$, imaginary$
  25.     If Left$(nmbr$, 1) = "-" Then 'handle neg numbers
  26.         imaginary$ = "*i": n$ = Mid$(nmbr$, 2)
  27.     Else
  28.         imaginary$ = "": n$ = nmbr$
  29.     End If
  30.     guess$ = mr$(n$, "/", "2")
  31.     other$ = n$
  32.     Do
  33.         If (Mid$(guess$, 1, 105) = Mid$(lastGuess$, 1, 105)) Then ' go past 100 matching digits for 100 digit precision
  34.             sqrRoot$ = Mid$(other$, 1, 101) + imaginary$
  35.             Exit Function
  36.         Else
  37.             lastGuess$ = guess$
  38.             sum$ = mr$(guess$, "+", other$)
  39.             guess$ = mr$(sum$, "/", "2")
  40.             other$ = mr$(n$, "/", guess$)
  41.         End If
  42.     Loop
  43.  
  44. Function sqrt$ (n$)
  45.     Dim As String r, tmp, r2
  46.     Dim As Integer k
  47.     r = _Trim$(Str$(Sqr(Val(n$))))
  48.     For k = 1 To 3
  49.         tmp = mr$(r, "*", r)
  50.         tmp = mr$(tmp, "-", n$)
  51.         r2 = mr$(r, "+", r)
  52.         tmp = mr$(tmp, "/", r2)
  53.         r = mr$(r, "-", tmp)
  54.         If Len(r) > 101 Then r = Left$(r, 101)
  55.     Next k 'Loop Until Abs(Val(tmp)) < 1D-100
  56.     sqrt$ = r
  57.  
  58. Function mr$ (a$, op$, b$) ' catchy? mr$ for math regulator
  59.     Dim ca$, cb$, aSgn$, bSgn$, postOp$, sgn$
  60.     Dim adp As Integer, bdp As Integer, dp As Integer, lpop As Integer
  61.  
  62.     op$ = _Trim$(op$) 'save fixing each time
  63.     ca$ = _Trim$(a$): cb$ = _Trim$(b$) 'make copies in case we change
  64.     'strip signs and decimals
  65.     If Left$(ca$, 1) = "-" Then
  66.         aSgn$ = "-": ca$ = Mid$(ca$, 2)
  67.     Else
  68.         aSgn$ = "": ca$ = ca$
  69.     End If
  70.     dp = InStr(ca$, ".")
  71.     If dp > 0 Then
  72.         adp = Len(ca$) - dp
  73.         ca$ = Mid$(ca$, 1, dp - 1) + Mid$(ca$, dp + 1)
  74.     Else
  75.         adp = 0
  76.     End If
  77.     If Left$(cb$, 1) = "-" Then
  78.         bSgn$ = "-": cb$ = Mid$(cb$, 2)
  79.     Else
  80.         bSgn$ = "": cb$ = cb$
  81.     End If
  82.     dp = InStr(cb$, ".")
  83.     If dp > 0 Then
  84.         bdp = Len(cb$) - dp
  85.         cb$ = Mid$(cb$, 1, dp - 1) + Mid$(cb$, dp + 1)
  86.     Else
  87.         bdp = 0
  88.     End If
  89.  
  90.     If op$ = "+" Or op$ = "-" Or op$ = "/" Then 'add or subtr  even up strings on right of decimal
  91.         'even up the right sides of decimals if any
  92.         If adp > bdp Then dp = adp Else dp = bdp
  93.         If adp < dp Then ca$ = ca$ + String$(dp - adp, "0")
  94.         If bdp < dp Then cb$ = cb$ + String$(dp - bdp, "0")
  95.     ElseIf op$ = "*" Then
  96.         dp = adp + bdp
  97.     End If
  98.     If op$ = "*" Or op$ = "/" Then
  99.         If aSgn$ = bSgn$ Then sgn$ = "" Else sgn$ = "-"
  100.     End If
  101.     'now according to signs and op$ call add$ or subtr$
  102.     If op$ = "+" Then
  103.         If aSgn$ = bSgn$ Then 'really add
  104.             postOp$ = aSgn$ + add$(ca$, cb$)
  105.         Else 'have a case of subtraction
  106.             If aSgn$ = "-" Then postOp$ = subtr$(cb$, ca$) Else postOp$ = subtr$(ca$, cb$)
  107.         End If
  108.     ElseIf op$ = "-" Then
  109.         If bSgn$ = "-" Then 'really add but switch b sign
  110.             bSgn$ = ""
  111.             If aSgn$ = "-" Then
  112.                 postOp$ = subtr$(cb$, ca$)
  113.             Else 'aSgn = ""
  114.                 postOp$ = add$(ca$, cb$)
  115.             End If
  116.         Else 'bSgn$ =""
  117.             If aSgn$ = "-" Then
  118.                 bSgn$ = "-"
  119.                 postOp$ = aSgn$ + add$(ca$, cb$)
  120.             Else
  121.                 postOp$ = subtr$(ca$, cb$)
  122.             End If
  123.         End If
  124.     ElseIf op$ = "*" Then
  125.         postOp$ = sgn$ + mult$(ca$, cb$)
  126.     ElseIf op$ = "/" Then
  127.         postOp$ = sgn$ + divide$(ca$, cb$)
  128.     End If ' which op
  129.     'put dp back
  130.     If op$ <> "/" Then
  131.         lpop = Len(postOp$) ' put decimal back
  132.         postOp$ = Mid$(postOp$, 1, lpop - dp) + "." + Mid$(postOp$, lpop - dp + 1)
  133.     End If
  134.     mr$ = trim0$(postOp$)
  135.  
  136. Function divide$ (n$, d$) ' goal here is 100 digits precision not 100 digits past decimal
  137.     Dim di$, ndi$, nD As Integer
  138.     If trim0$(n$) = "0" Then divide$ = "0": Exit Function
  139.     If trim0$(d$) = "0" Then divide$ = "div 0": Exit Function
  140.     If trim0$(d$) = "1" Then divide$ = trim0$(n$): Exit Function '8/17 add trim0$
  141.  
  142.     ' aha! found a bug when d$ gets really huge 100 is no where near enough!!!!
  143.     ' 2021-06-03 fix by adding 100 to len(d$), plus have to go a little past 100 for 100 digit precision
  144.     ' need to go past 100 for 100 precise digits (not decimal places)
  145.     di$ = Mid$(nInverse$(d$, Len(d$) + 200), 2) 'chop off decimal point after
  146.     nD = Len(di$)
  147.     ndi$ = mult$(n$, di$)
  148.     ndi$ = Mid$(ndi$, 1, Len(ndi$) - nD) + "." + Right$(String$(nD, "0") + Right$(ndi$, nD), nD)
  149.     divide$ = trim0$(ndi$)
  150.  
  151. Function nInverse$ (n$, DP As Integer) 'assume decimal at very start of the string of digits returned, no rounding
  152.     Dim m$(1 To 9), si$, r$, outstr$, d$
  153.     Dim i As Integer
  154.     For i = 1 To 9
  155.         si$ = _Trim$(Str$(i))
  156.         m$(i) = mult$(si$, n$)
  157.     Next
  158.     outstr$ = ""
  159.     If n$ = "0" Then nInverse$ = "Div 0": Exit Function
  160.     If n$ = "1" Then nInverse$ = "1": Exit Function
  161.     outstr$ = "." 'everything else n > 1 is decimal 8/17
  162.     r$ = "10"
  163.     Do
  164.         While Left$(subtr$(r$, n$), 1) = "-" '   r - n < 0
  165.             outstr$ = outstr$ + "0" '            add 0 to the  output string
  166.             If Len(outstr$) = DP Then nInverse$ = outstr$: Exit Function 'check if we've reached DP length
  167.             r$ = r$ + "0"
  168.         Wend
  169.         For i = 9 To 1 Step -1
  170.             If LTE(m$(i), r$) Then d$ = _Trim$(Str$(i)): Exit For
  171.         Next
  172.         outstr$ = outstr$ + d$
  173.         If Len(outstr$) = DP Then nInverse$ = outstr$: Exit Function
  174.         r$ = subtr$(r$, mult$(d$, n$)) 'r = r -d*n
  175.         If r$ = "0" Then nInverse$ = outstr$: Exit Function 'found a perfect divisor
  176.         r$ = r$ + "0" 'add another place
  177.     Loop
  178.  
  179. Function mult$ (a$, b$) 'assume both positive integers prechecked as all digits strings
  180.     Dim la As Integer, lb As Integer, m As Integer, g As Integer, dp As Integer
  181.     Dim f18$, f1$, t$, build$, accum$
  182.  
  183.     If trim0$(a$) = "0" Then mult$ = "0": Exit Function
  184.     If trim0$(b$) = "0" Then mult$ = "0": Exit Function
  185.     If trim0$(a$) = "1" Then mult$ = trim0$(b$): Exit Function
  186.     If trim0$(b$) = "1" Then mult$ = trim0$(a$): Exit Function
  187.     'find the longer number and make it a mult of 18 to take 18 digits at a time from it
  188.     la = Len(a$): lb = Len(b$)
  189.     If la > lb Then
  190.         m = Int(la / 18) + 1
  191.         f18$ = Right$(String$(m * 18, "0") + a$, m * 18)
  192.         f1$ = b$
  193.     Else
  194.         m = Int(lb / 18) + 1
  195.         f18$ = Right$(String$(m * 18, "0") + b$, m * 18)
  196.         f1$ = a$
  197.     End If
  198.     For dp = Len(f1$) To 1 Step -1 'dp = digit position of the f1$
  199.         build$ = "" 'line builder
  200.         co = 0
  201.         'now taking 18 digits at a time Thanks Steve McNeill
  202.         For g = 1 To m
  203.             v18 = Val(Mid$(f18$, m * 18 - g * 18 + 1, 18))
  204.             sd = Val(Mid$(f1$, dp, 1))
  205.             t$ = Right$(String$(19, "0") + _Trim$(Str$(v18 * sd + co)), 19)
  206.             co = Val(Mid$(t$, 1, 1))
  207.             build$ = Mid$(t$, 2) + build$
  208.         Next g
  209.         If co Then build$ = _Trim$(Str$(co)) + build$
  210.         If dp = Len(f1$) Then
  211.             accum$ = build$
  212.         Else
  213.             accum$ = add$(accum$, build$ + String$(Len(f1$) - dp, "0"))
  214.         End If
  215.     Next dp
  216.     mult$ = accum$
  217.  
  218. Function subtr$ (sum$, minus$) ' assume both numbers are positive all digits
  219.     Dim m As Integer, g As Integer, p As Integer
  220.     Dim ts$, tm$, sign$, LG$, sm$, t$, result$
  221.  
  222.     ts$ = TrimLead0$(sum$): tm$ = TrimLead0$(minus$)
  223.     If trim0(ts$) = trim0$(tm$) Then subtr$ = "0": Exit Function 'OK proceed with function knowing they are not equal
  224.     tenE18 = 1000000000000000000 'yes!!! no dang E's
  225.     If LTE(ts$, tm$) Then ' which is bigger? minus is bigger
  226.         sign$ = "-"
  227.         m = Int(Len(tm$) / 18) + 1
  228.         LG$ = Right$(String$(m * 18, "0") + tm$, m * 18)
  229.         sm$ = Right$(String$(m * 18, "0") + ts$, m * 18)
  230.     Else 'sum is bigger
  231.         sign$ = ""
  232.         m = Int(Len(ts$) / 18) + 1
  233.         LG$ = Right$(String$(m * 18, "0") + ts$, m * 18)
  234.         sm$ = Right$(String$(m * 18, "0") + tm$, m * 18)
  235.     End If
  236.     'now taking 18 digits at a time From Steve I learned we can do more than 1 digit at a time
  237.     For g = 1 To m
  238.         VB = Val(Mid$(LG$, m * 18 - g * 18 + 1, 18))
  239.         vs = Val(Mid$(sm$, m * 18 - g * 18 + 1, 18))
  240.         If vs > VB Then
  241.             t$ = Right$(String$(18, "0") + _Trim$(Str$(tenE18 - vs + VB)), 18)
  242.             p = (m - g) * 18
  243.             While p > 0 And Mid$(LG$, p, 1) = "0"
  244.                 Mid$(LG$, p, 1) = "9"
  245.                 p = p - 1
  246.             Wend
  247.             If p > 0 Then Mid$(LG$, p, 1) = _Trim$(Str$(Val(Mid$(LG$, p, 1)) - 1))
  248.         Else
  249.             t$ = Right$(String$(18, "0") + _Trim$(Str$(VB - vs)), 18)
  250.         End If
  251.         result$ = t$ + result$
  252.     Next
  253.     subtr$ = sign$ + result$
  254.  
  255. Function add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no spaces or - signs
  256.     'first thing is to set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
  257.     Dim la As Integer, lb As Integer, m As Integer, g As Integer
  258.     Dim fa$, fb$, t$, new$, result$
  259.     la = Len(a$): lb = Len(b$)
  260.     If la > lb Then m = Int(la / 18) + 1 Else m = Int(lb / 18) + 1
  261.     fa$ = Right$(String$(m * 18, "0") + a$, m * 18)
  262.     fb$ = Right$(String$(m * 18, "0") + b$, m * 18)
  263.  
  264.     'now taking 18 digits at a time Thanks Steve McNeill
  265.     For g = 1 To m
  266.         sa = Val(Mid$(fa$, m * 18 - g * 18 + 1, 18))
  267.         sb = Val(Mid$(fb$, m * 18 - g * 18 + 1, 18))
  268.         t$ = Right$(String$(36, "0") + _Trim$(Str$(sa + sb + co)), 36)
  269.         co = Val(Mid$(t$, 1, 18))
  270.         new$ = Mid$(t$, 19)
  271.         result$ = new$ + result$
  272.     Next
  273.     If co Then result$ = Str$(co) + result$
  274.     add$ = result$
  275.  
  276. ' String Math Helpers -----------------------------------------------
  277.  
  278. 'this function needs TrimLead0$(s$)
  279. Function LTE (a$, b$) ' a$ Less Than or Equal b$  comparison of 2 strings
  280.     Dim ca$, cb$, la As Integer, lb As Integer, i As Integer
  281.     ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
  282.     la = Len(ca$): lb = Len(cb$)
  283.     If ca$ = cb$ Then
  284.         LTE = -1
  285.     ElseIf la < lb Then ' a is smaller
  286.         LTE = -1
  287.     ElseIf la > lb Then ' a is bigger
  288.         LTE = 0
  289.     ElseIf la = lb Then ' equal lengths
  290.         For i = 1 To Len(ca$)
  291.             If Val(Mid$(ca$, i, 1)) > Val(Mid$(cb$, i, 1)) Then
  292.                 LTE = 0: Exit Function
  293.             ElseIf Val(Mid$(ca$, i, 1)) < Val(Mid$(cb$, i, 1)) Then
  294.                 LTE = -1: Exit Function
  295.             End If
  296.         Next
  297.     End If
  298.  
  299. ' ------------------------------------- use these for final display
  300.  
  301. Function TrimLead0$ (s$) 'for treating strings as number (pos integers)
  302.     Dim copys$, i As Integer, find As Integer
  303.     copys$ = _Trim$(s$) 'might as well remove spaces too
  304.     i = 1: find = 0
  305.     While i < Len(copys$) And Mid$(copys$, i, 1) = "0"
  306.         i = i + 1: find = 1
  307.     Wend
  308.     If find = 1 Then copys$ = Mid$(copys$, i)
  309.     If copys$ = "" Then TrimLead0$ = "0" Else TrimLead0$ = copys$
  310.  
  311. Function TrimTail0$ (s$)
  312.     Dim copys$, dp As Integer, i As Integer, find As Integer
  313.     copys$ = _Trim$(s$) 'might as well remove spaces too
  314.     TrimTail0$ = copys$
  315.     dp = InStr(copys$, ".")
  316.     If dp > 0 Then
  317.         i = Len(copys$): find = 0
  318.         While i > dp And Mid$(copys$, i, 1) = "0"
  319.             i = i - 1: find = 1
  320.         Wend
  321.         If find = 1 Then
  322.             If i = dp Then
  323.                 TrimTail0$ = Mid$(copys$, 1, dp - 1)
  324.             Else
  325.                 TrimTail0$ = Mid$(copys$, 1, i)
  326.             End If
  327.         End If
  328.     End If
  329.  
  330. Function trim0$ (s$)
  331.     Dim cs$, si$
  332.     cs$ = s$
  333.     si$ = Left$(cs$, 1)
  334.     If si$ = "-" Then cs$ = Mid$(cs$, 2)
  335.     cs$ = TrimLead0$(cs$)
  336.     cs$ = TrimTail0$(cs$)
  337.     If Right$(cs$, 1) = "." Then cs$ = Mid$(cs$, 1, Len(cs$) - 1)
  338.     If si$ = "-" Then trim0$ = si$ + cs$ Else trim0$ = cs$
  339.  
  340. ' for displaying truncated numbers say to 60 digits
  341. Function showDP$ (num$, nDP As Integer)
  342.     Dim cNum$, dp As Integer, d As Integer, i As Integer
  343.     cNum$ = num$ 'since num$ could get changed
  344.     showDP$ = num$
  345.     dp = InStr(num$, ".")
  346.     If dp > 0 Then
  347.         If Len(Mid$(cNum$, dp + 1)) > nDP Then
  348.             d = Val(Mid$(cNum$, dp + nDP + 1, 1))
  349.             If d > 4 Then
  350.                 cNum$ = "0" + cNum$ ' tack on another 0 just in case 9's all the way to left
  351.                 dp = dp + 1
  352.                 i = dp + nDP
  353.                 While Mid$(cNum$, i, 1) = "9" Or Mid$(cNum$, i, 1) = "."
  354.                     If Mid$(cNum$, i, 1) = "9" Then
  355.                         Mid$(cNum$, i, 1) = "0"
  356.                     End If
  357.                     i = i - 1
  358.                 Wend
  359.                 Mid$(cNum$, i, 1) = _Trim$(Str$(Val(Mid$(cNum$, i, 1)) + 1)) 'last non 9 digit
  360.                 cNum$ = Mid$(cNum$, 1, dp + nDP) 'chop it
  361.                 showDP$ = trim0$(cNum$)
  362.             Else
  363.                 showDP$ = Mid$(cNum$, 1, dp + nDP)
  364.             End If
  365.         End If
  366.     End If
  367.  
Title: Re: String Math
Post by: George McGinn on June 03, 2021, 09:25:56 pm
@bplus - Here is my version of the Square Root using strings.

I have integrated it into your MR program. However, as you will see from the result screen, it seems like it does not clear out what is in the decimal places.

I am using your add, subtract and divide routines, and I'm not yet sure if it is an error with your routines not clearing out the decimal places, or that they are not designed to be nested in a formula (the ones my friend and I wrote for the iPad/iPhone does work properly), or I just don't understand how your program fully works.

I created an OP code ">" to represent Square Root, and the second number is set to "" (can be any value really, as it is never referenced in my routine.

Because the iterations do not stop when dx$ = "10" I get an erroneous result. As you will see I have both the original formula expressed properly in your mr$ format, as well as it broken out in its components based on the order of operations. I get the same results in both, that is I get a carryover of the decimals from the prior iteration.

Code: QB64: [Select]
  1. PRINT "Square Root of 100 equals "; mr$("100", ">", "")    

Code: QB64: [Select]
  1. '*** Add in Square Root Function
  2.     ELSEIF op$ = ">" THEN
  3.         postOp$ = root_2$(ca$)
  4.  
  5.  

Code: QB64: [Select]
  1. FUNCTION root_2$ (A$)
  2.     DIM x$, dx$
  3.     DIM precision, max_iterations
  4.     DIM i AS INTEGER
  5.  
  6.     x$ = mr$(A$, "/", "2"): precision = 0.001: max_iterations = 30
  7.     FOR i = 1 TO max_iterations
  8. '*** Formula: dx = (x - A / x) / 2
  9. '*** Reduce x by dx:  x = x - dx
  10.  
  11.          dx$ = mr$(mr$(x$, "-", mr$(A$, "/", x$)), "/", "2")
  12.  
  13. '         dx$ = mr$(A$, "/", x$)
  14. '         dx$ = mr$(x$, "-", dx$)
  15. '         dx$ = mr$(dx$, "/", "2")
  16.          x$ = mr$(x$, "-", dx$)
  17.          PRINT "DX$ = "; dx$
  18.          PRINT "X$  = "; x$: PRINT
  19.          IF ABS(VAL(dx$)) < precision THEN i = max_iterations
  20.     NEXT i
  21.     root_2$ = x$


Here is my run of the function above. You will see that dx$ is right, but x$ should be just 10. If you want, I can post the entire MR program.
 

Title: Re: String Math
Post by: jack on June 03, 2021, 09:39:39 pm
@bplus
your string math does not understand numbers in exponential format so when the input number get big and the Sqr of the number is expressed in exponential format then my version fails, I suppose there may be a way to fix that
Title: Re: String Math
Post by: jack on June 03, 2021, 09:44:25 pm
the Newton-Raphson formula for the square root is
r = r - (r^2-x)/(2*r)
in general the inverse function approximation is found by
Xi = Xi - ( f(Xi) -x)/ f ' (Xi)
Title: Re: String Math
Post by: bplus on June 03, 2021, 09:50:21 pm
@bplus
your string math does not understand numbers in exponential format so when the input number get big and the Sqr of the number is expressed in exponential format then my version fails, I suppose there may be a way to fix that

Right! String Math means no exponents, it's straight digits and though it takes longer it's accurate with the biggest and smallest of numbers
@jack your code here:
Code: QB64: [Select]
  1. Function sqrt$ (n$)
  2.     Dim As String r, tmp, r2
  3.     Dim As Integer k
  4.     r = _Trim$(Str$(Sqr(Val(n$)))) ' <<<<<<<<<<<<
  5.     For k = 1 To 3
  6.         tmp = mr$(r, "*", r)
  7.         tmp = mr$(tmp, "-", n$)
  8.         r2 = mr$(r, "+", r)
  9.         tmp = mr$(tmp, "/", r2)
  10.         r = mr$(r, "-", tmp)
  11.         If Len(r) > 101 Then r = Left$(r, 101)
  12.     Next k 'Loop Until Abs(Val(tmp)) < 1D-100
  13.     sqrt$ = r
  14.  
Uses 2 functions not available to String Math,
 
VAL() because there are only string type variables
EDIT: well maybe VAL() will work on smaller strings but I don't see it handling a string a 100 digits long?

SQR() heck! that's exactly what we are looking for, of course it's faster but only works for regular type numbers, integers, longs, single, double... not a string of digits.
EDIT: Unless you are somehow dividing the string into workable sections and taking SQR() of those and string together a giant construction like we do with the add$, mult$, subtract$ then I am misunderstanding, apologies.
In fact I have something like that from way back doing SQR, like we learned in school.

 
Title: Re: String Math
Post by: bplus on June 03, 2021, 09:53:23 pm
the Newton-Raphson formula for the square root is
r = r - (r^2-x)/(2*r)
in general the inverse function approximation is found by
Xi = Xi - ( f(Xi) -x)/ f ' (Xi)

I was going by George McGinn example which did seem to be working for big numbers (but not dot 20-0's64 or .0000000000000000000064).
Title: Re: String Math
Post by: bplus on June 03, 2021, 10:32:37 pm
@bplus - Here is my version of the Square Root using strings.

I have integrated it into your MR program. However, as you will see from the result screen, it seems like it does not clear out what is in the decimal places.

I am using your add, subtract and divide routines, and I'm not yet sure if it is an error with your routines not clearing out the decimal places, or that they are not designed to be nested in a formula (the ones my friend and I wrote for the iPad/iPhone does work properly), or I just don't understand how your program fully works.

I created an OP code ">" to represent Square Root, and the second number is set to "" (can be any value really, as it is never referenced in my routine.

Because the iterations do not stop when dx$ = "10" I get an erroneous result. As you will see I have both the original formula expressed properly in your mr$ format, as well as it broken out in its components based on the order of operations. I get the same results in both, that is I get a carryover of the decimals from the prior iteration.

Code: QB64: [Select]
  1. PRINT "Square Root of 100 equals "; mr$("100", ">", "")    

Code: QB64: [Select]
  1. '*** Add in Square Root Function
  2.     ELSEIF op$ = ">" THEN
  3.         postOp$ = root_2$(ca$)
  4.  
  5.  

Code: QB64: [Select]
  1. FUNCTION root_2$ (A$)
  2.     DIM x$, dx$
  3.     DIM precision, max_iterations
  4.     DIM i AS INTEGER
  5.  
  6.     x$ = mr$(A$, "/", "2"): precision = 0.001: max_iterations = 30
  7.     FOR i = 1 TO max_iterations
  8. '*** Formula: dx = (x - A / x) / 2
  9. '*** Reduce x by dx:  x = x - dx
  10.  
  11.          dx$ = mr$(mr$(x$, "-", mr$(A$, "/", x$)), "/", "2")
  12.  
  13. '         dx$ = mr$(A$, "/", x$)
  14. '         dx$ = mr$(x$, "-", dx$)
  15. '         dx$ = mr$(dx$, "/", "2")
  16.          x$ = mr$(x$, "-", dx$)
  17.          PRINT "DX$ = "; dx$
  18.          PRINT "X$  = "; x$: PRINT
  19.          IF ABS(VAL(dx$)) < precision THEN i = max_iterations
  20.     NEXT i
  21.     root_2$ = x$


Here is my run of the function above. You will see that dx$ is right, but x$ should be just 10. If you want, I can post the entire MR program.
 


@George McGinn are we so different?
Code: QB64: [Select]
  1. Function SRbyNR$ (nmbr$) ' square root by Newton - Ralphson method my interpretation of GeorgeMcGinn
  2.  
  3.     Dim n$, guess$, lastGuess$, dx$, imaginary$, other$, loopcnt
  4.     If Left$(nmbr$, 1) = "-" Then 'handle neg numbers
  5.         imaginary$ = "*i": n$ = Mid$(nmbr$, 2)
  6.     Else
  7.         imaginary$ = "": n$ = nmbr$
  8.     End If
  9.  
  10.     guess$ = mr$(n$, "/", "2") ' get this going first and then try better starting guess later
  11.  
  12.     Do
  13.         loopcnt = loopcnt + 1
  14.         Print "loop cnt"; loopcnt
  15.         If (Mid$(guess$, 1, 105) = Mid$(lastGuess$, 1, 105)) Then ' go past 100 matching digits for 100 digit precision
  16.             SRbyNR$ = Mid$(other$, 1, 101) + imaginary$
  17.             Exit Function
  18.         Else
  19.             'dx = (x - A / x) / 2: x = x - dx ' Thanks George
  20.             lastGuess$ = guess$
  21.             dx$ = mr$(mr$(guess$, "-", mr$(n$, "/", guess$)), "/", "2")
  22.             guess$ = mr$(guess$, "-", dx$)
  23.             other$ = mr$(n$, "/", guess$) ' try other factor for guess$  sometimes it nails answer without all digits
  24.         End If
  25.     Loop

dx$ = mr$(mr$(guess$, "-", mr$(n$, "/", guess$)), "/", "2") ' yes you can team functions up like this that is fine!
dx$ = mr$(mr$(x$, "-", mr$(A$, "/", x$)), "/", "2")

We just use different variables with the main formula.

This is where we differ, Finished conditional-
yours:
Code: QB64: [Select]
  1. IF ABS(VAL(dx$)) < precision THEN i = max_iterations
ABS() needs a number type and I don't trust VAL() to handle 100 plus digit strings.
Hence I don't trust < to work as expected.

mine:
Code: QB64: [Select]
  1. If (Mid$(guess$, 1, 105) = Mid$(lastGuess$, 1, 105)) Then
So I went the route of matching strings down until the first 100 digits of last guess match first 100 digits of current guess. Oddly our method would rather go 100-9's, 1.9999999...'s, than go to 2.0 for the square root of 4!
If guess is always growing larger towards 2, the other factor is gaining 0's after decimal such that it looks exactly right when we cut off after 100 digits of 0's (for 2.0000000000...) have been accumulated.
Title: Re: String Math
Post by: bplus on June 03, 2021, 10:47:31 pm
@jack no exponents, is that a paradigm shift? do you feel the freedom or do you feel like you lost your best friend? like you are dealing with stone age folks!

Obviously it's slow as hell but the accuracy!
Title: Re: String Math
Post by: George McGinn on June 04, 2021, 12:01:48 am
@bplus - DUH, you're right. I saw the word 'guess" which threw me off.

I put your routine in with mine to test it out.

Both produces 2 as the square root of 4, but when I do SRQ(100) I get 5. ... from mine (which is the issue I outlined) but yours does not produce a result.

Are we having the same issue?
Title: Re: String Math
Post by: George McGinn on June 04, 2021, 12:06:23 am
This was the reason why my friend and I decided to add a precision check.

As for the question about VAL working on a string of 100 digits/decimal places, it works fine from my testing on both mobile devices (from the original code) to what I ported over to QB64.


I was going by George McGinn example which did seem to be working for big numbers (but not dot 20-0's64 or .0000000000000000000064).
Title: Re: String Math
Post by: jack on June 04, 2021, 07:20:19 am
@bplus it seems that you didn't bother to run the code I posted, instead you jump to conclusions.
but for completeness here my sqrt$ function that takes care of any exponential notation and is many times faster
Code: QB64: [Select]
  1. Function sqrt$ (n$)
  2.     Dim As String r, tmp, r2
  3.     Dim As Integer d, e, k, l
  4.     r = _Trim$(Str$(Sqr(Val(n$))))
  5.     d = InStr(r, "D")
  6.     If d > 0 Then
  7.         e = Val(Mid$(r, d + 1))
  8.         r = Left$(r, 1) + Mid$(Left$(r, d - 1), 3)
  9.         l = Len(r)
  10.         If e > 0 Then
  11.             r = r + String$(e - l + 1, "0")
  12.         ElseIf e < 0 Then
  13.             r = "0." + String$(Abs(e) - 1, "0") + r
  14.         End If
  15.     End If
  16.     For k = 1 To 3
  17.         tmp = mr$(r, "*", r)
  18.         tmp = mr$(tmp, "-", n$)
  19.         r2 = mr$(r, "+", r)
  20.         tmp = mr$(tmp, "/", r2)
  21.         r = mr$(r, "-", tmp)
  22.         If Len(r) > 101 Then r = Left$(r, 101)
  23.     Next k 'Loop Until Abs(Val(tmp)) < 1D-100
  24.     sqrt$ = r
  25.  
Quote
Enter a number to find it's square root ?  20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
 26.375  141421356237309504880168872420969807856967187537694807317667.9737990732478462107038850387534327641572
 .0546875          141421356237309504880168872420969807856967187537694807317667.9737990732478462107038850387534327641572
about 482 times faster
Title: Re: String Math
Post by: jack on June 04, 2021, 08:21:09 am
bplus, there's a bug in your subtract routine
Code: QB64: [Select]
  1. result$ = mr$(".00000000000000000000000000000000000000000000200000000000000054307978001764", "-", ".000000000000000000000000000000000000000000002")
  2. Print result$
  3.  
the output
Quote
.000000000000000000000054307978001764
it should be
Quote
.00000000000000000000000000000000000000000000000000000000000054307978001764
Title: Re: String Math
Post by: George McGinn on June 04, 2021, 09:46:15 am
@jack

I ran it and it does give me the correct answer for SQR(100) where mine is having an issue where the decimals are being carried over (might be part of a subtraction problem in the Math Regulator, but not sure yet, and I did see your post about an issue with the subtraction function).

I am working on converting my n'th root function to string, unless you have one as well. I would test it out along side mine, as the issue I am having with my square root function I believe I will also have with my n'th root function.

@bplus it seems that you didn't bother to run the code I posted, instead you jump to conclusions.
but for completeness here my sqrt$ function that takes care of any exponential notation and is many times faster
Code: QB64: [Select]
  1. Function sqrt$ (n$)
  2.     Dim As String r, tmp, r2
  3.     Dim As Integer d, e, k, l
  4.     r = _Trim$(Str$(Sqr(Val(n$))))
  5.     d = InStr(r, "D")
  6.     If d > 0 Then
  7.         e = Val(Mid$(r, d + 1))
  8.         r = Left$(r, 1) + Mid$(Left$(r, d - 1), 3)
  9.         l = Len(r)
  10.         If e > 0 Then
  11.             r = r + String$(e - l + 1, "0")
  12.         ElseIf e < 0 Then
  13.             r = "0." + String$(Abs(e) - 1, "0") + r
  14.         End If
  15.     End If
  16.     For k = 1 To 3
  17.         tmp = mr$(r, "*", r)
  18.         tmp = mr$(tmp, "-", n$)
  19.         r2 = mr$(r, "+", r)
  20.         tmp = mr$(tmp, "/", r2)
  21.         r = mr$(r, "-", tmp)
  22.         If Len(r) > 101 Then r = Left$(r, 101)
  23.     Next k 'Loop Until Abs(Val(tmp)) < 1D-100
  24.     sqrt$ = r
  25.  
about 482 times faster
Title: Re: String Math
Post by: bplus on June 04, 2021, 10:42:04 am
@bplus it seems that you didn't bother to run the code I posted, instead you jump to conclusions.
...

@jack

I did run the code you provided in reply #33
https://www.qb64.org/forum/index.php?topic=2921.msg133047#msg133047

I suspected it would not work with numbers outside the range of doubles and floats so first I tried 100 with an even amount of zeros added on, I didn't count then but I double checked with 100 + 30 zeros a second time, then I ran a small number .0000000000000000000064 (that's suppose to be 20 zeroes between decimal and 64.

I posted my first results in reply #37
https://www.qb64.org/forum/index.php?topic=2921.msg133053#msg133053

Just to confirm what I saw yesterday I ran the code from reply #33 again this for sure 100 then 30 zeros and . 20-0's and 64
Again my functions takes a very long time but result looks correct and your function doesn't take a long time but results are do not look close to correct:
 


I have not run the code that corrects for exponents (reply #43 posted this AM) because I am just getting to it now your sqr(2) times 10 to some hefty amount of even number (I assume by the answers) looks very impressive specially with time.

Please forget my comments in #37 I don't know what the heck you're doing in 3 iterations, let the screen shot of my test of your code speak for itself.
Title: Re: String Math
Post by: bplus on June 04, 2021, 10:48:57 am
bplus, there's a bug in your subtract routine
Code: QB64: [Select]
  1. result$ = mr$(".00000000000000000000000000000000000000000000200000000000000054307978001764", "-", ".000000000000000000000000000000000000000000002")
  2. Print result$
  3.  
the outputit should be

Confirmed! thanks for heads up.

Apparently the Forum editor can only Quote one Quote box at a time.
Title: Re: String Math
Post by: jack on June 04, 2021, 11:48:44 am
here's your MR with nth root, but I there seem to problems with very big numbers, not sure it's due to a MR bug
there's a loss of precision with roots higher than 50
Code: QB64: [Select]
  1. _Title "Math regulator mr test sqr estimating" ' b+ start 2021-06-02
  2. ' divide$ found fix? 2021-06-03 comment in that function
  3. ' 2021-06-03 update with new sqrRoot$ Function
  4.  
  5. Randomize Timer 'now that it's seems to be running silent
  6. 'Screen _NewImage(1200, 700, 32)
  7. '_Delay .25
  8. '_ScreenMove _Middle
  9.     Sub memcpy (ByVal dest As _Offset, Byval source As _Offset, Byval bytes As Long)
  10.  
  11.  
  12. Dim n$, result$
  13. Dim t#
  14. 'Do
  15. '    'remember everything is strings
  16. '    Input "Enter a number to find it's square root "; n$
  17. '    If n$ = "" Then End
  18. '    t# = Timer
  19. '    result$ = sqrRoot$(n$)
  20. '    Print Timer - t#, result$
  21. '    t# = Timer
  22. '    result$ = sqrt$(n$)
  23. '    Print Timer - t#, result$
  24. '    Print "Length ="; Len(result$)
  25. '    Print
  26. 'Loop
  27. 'Print approx_power(x#, y#), x# ^ (1 / y#)
  28.  
  29. n$ = "2"
  30. t# = Timer
  31. result$ = approx_powers$(n$, 2)
  32. Print Timer - t#, result$
  33.  
  34. Function sqrRoot$ (nmbr$)
  35.     Dim n$, guess$, lastGuess$, other$, sum$, imaginary$
  36.     If Left$(nmbr$, 1) = "-" Then 'handle neg numbers
  37.         imaginary$ = "*i": n$ = Mid$(nmbr$, 2)
  38.     Else
  39.         imaginary$ = "": n$ = nmbr$
  40.     End If
  41.     guess$ = mr$(n$, "/", "2")
  42.     other$ = n$
  43.     Do
  44.         If (Mid$(guess$, 1, 105) = Mid$(lastGuess$, 1, 105)) Then ' go past 100 matching digits for 100 digit precision
  45.             sqrRoot$ = Mid$(other$, 1, 101) + imaginary$
  46.             Exit Function
  47.         Else
  48.             lastGuess$ = guess$
  49.             sum$ = mr$(guess$, "+", other$)
  50.             guess$ = mr$(sum$, "/", "2")
  51.             other$ = mr$(n$, "/", guess$)
  52.         End If
  53.     Loop
  54.  
  55. Function ipower# (x As Double, e As Integer)
  56.     'take x to an integer power
  57.     Dim As Double z, y
  58.     Dim As Integer n
  59.     y = x
  60.     n = Abs(e)
  61.     z = 1#
  62.     While n > 0
  63.         While (n Mod 2) = 0
  64.             n = n \ 2
  65.             y = y * y
  66.         Wend
  67.         n = n - 1
  68.         z = y * z
  69.     Wend
  70.     If e < 0 Then
  71.         ipower = 1# / z
  72.     Else
  73.         ipower = z
  74.     End If
  75.  
  76. Function approx_power# (x#, y#)
  77.     Dim As Double a, b
  78.     Dim As Long tmp
  79.     a = x#: b = 1# / y#
  80.     memcpy _Offset(tmp), _Offset(a) + 4, 4
  81.     tmp = (tmp - 1072632447) * b
  82.     tmp = tmp + 1072632447
  83.     memcpy _Offset(a) + 4, _Offset(tmp), 4
  84.     a = a - (ipower(a, y#) - x#) / (y# * ipower(a, y# - 1))
  85.     a = a - (ipower(a, y#) - x#) / (y# * ipower(a, y# - 1))
  86.     a = a - (ipower(a, y#) - x#) / (y# * ipower(a, y# - 1))
  87.     a = a - (ipower(a, y#) - x#) / (y# * ipower(a, y# - 1))
  88.     approx_power = a
  89.  
  90. Function ipow$ (x As String, e As Integer)
  91.     'take x to an integer power
  92.     Dim As String z, y
  93.     Dim As Integer n
  94.     y = x
  95.     n = Abs(e)
  96.     z = "1"
  97.     While n > 0
  98.         While (n Mod 2) = 0
  99.             n = n \ 2
  100.             y = mr$(y, "*", y)
  101.             If Len(y) > 101 Then y = Left$(y, 101)
  102.         Wend
  103.         n = n - 1
  104.         z = mr$(y, "*", z)
  105.         If Len(z) > 101 Then z = Left$(z, 101)
  106.     Wend
  107.     If e < 0 Then
  108.         ipow = mr$("1", "/", z)
  109.     Else
  110.         ipow = z
  111.     End If
  112.  
  113. Function approx_powers$ (x$, y%)
  114.     Dim As Double a, b
  115.     Dim As Integer d, e, k, l
  116.     Dim As String r, r2, temp, ys
  117.     a = Val(x$): b = y%
  118.     a = approx_power#(a, b)
  119.     r = _Trim$(Str$(a))
  120.     d = InStr(r, "D")
  121.     If d > 0 Then
  122.         e = Val(Mid$(r, d + 1))
  123.         r = Left$(r, 1) + Mid$(Left$(r, d - 1), 3)
  124.         l = Len(r)
  125.         If e > 0 Then
  126.             r = r + String$(e - l + 1, "0")
  127.         ElseIf e < 0 Then
  128.             r = "0." + String$(Abs(e) - 1, "0") + r
  129.         End If
  130.     End If
  131.     ys = _Trim$(Str$(y%))
  132.     For k = 1 To 3
  133.         temp = ipow$(r, y%)
  134.         temp = mr$(temp, "-", x$)
  135.         r2 = ipow$(r, y% - 1)
  136.         r2 = mr$(ys, "*", r2)
  137.         r2 = mr$(temp, "/", r2)
  138.         r = mr$(r, "-", r2)
  139.     Next k
  140.     If Len(r) > 101 Then r = Left$(r, 101)
  141.     approx_powers = r
  142.  
  143. Function sqrt$ (n$)
  144.     Dim As String r, tmp, r2
  145.     Dim As Integer d, e, k, l
  146.     r = _Trim$(Str$(Sqr(Val(n$))))
  147.     d = InStr(r, "D")
  148.     If d > 0 Then
  149.         e = Val(Mid$(r, d + 1))
  150.         r = Left$(r, 1) + Mid$(Left$(r, d - 1), 3)
  151.         l = Len(r)
  152.         If e > 0 Then
  153.             r = r + String$(e - l + 1, "0")
  154.         ElseIf e < 0 Then
  155.             r = "0." + String$(Abs(e) - 1, "0") + r
  156.         End If
  157.     End If
  158.     For k = 1 To 3
  159.         tmp = mr$(r, "*", r)
  160.         tmp = mr$(tmp, "-", n$)
  161.         r2 = mr$(r, "+", r)
  162.         tmp = mr$(tmp, "/", r2)
  163.         r = mr$(r, "-", tmp)
  164.         If Len(r) > 101 Then r = Left$(r, 101)
  165.     Next k 'Loop Until Abs(Val(tmp)) < 1D-100
  166.     sqrt$ = r
  167.  
  168. Function mr$ (a$, op$, b$) ' catchy? mr$ for math regulator
  169.     Dim ca$, cb$, aSgn$, bSgn$, postOp$, sgn$
  170.     Dim adp As Integer, bdp As Integer, dp As Integer, lpop As Integer
  171.  
  172.     op$ = _Trim$(op$) 'save fixing each time
  173.     ca$ = _Trim$(a$): cb$ = _Trim$(b$) 'make copies in case we change
  174.     'strip signs and decimals
  175.     If Left$(ca$, 1) = "-" Then
  176.         aSgn$ = "-": ca$ = Mid$(ca$, 2)
  177.     Else
  178.         aSgn$ = "": ca$ = ca$
  179.     End If
  180.     dp = InStr(ca$, ".")
  181.     If dp > 0 Then
  182.         adp = Len(ca$) - dp
  183.         ca$ = Mid$(ca$, 1, dp - 1) + Mid$(ca$, dp + 1)
  184.     Else
  185.         adp = 0
  186.     End If
  187.     If Left$(cb$, 1) = "-" Then
  188.         bSgn$ = "-": cb$ = Mid$(cb$, 2)
  189.     Else
  190.         bSgn$ = "": cb$ = cb$
  191.     End If
  192.     dp = InStr(cb$, ".")
  193.     If dp > 0 Then
  194.         bdp = Len(cb$) - dp
  195.         cb$ = Mid$(cb$, 1, dp - 1) + Mid$(cb$, dp + 1)
  196.     Else
  197.         bdp = 0
  198.     End If
  199.  
  200.     If op$ = "+" Or op$ = "-" Or op$ = "/" Then 'add or subtr  even up strings on right of decimal
  201.         'even up the right sides of decimals if any
  202.         If adp > bdp Then dp = adp Else dp = bdp
  203.         If adp < dp Then ca$ = ca$ + String$(dp - adp, "0")
  204.         If bdp < dp Then cb$ = cb$ + String$(dp - bdp, "0")
  205.     ElseIf op$ = "*" Then
  206.         dp = adp + bdp
  207.     End If
  208.     If op$ = "*" Or op$ = "/" Then
  209.         If aSgn$ = bSgn$ Then sgn$ = "" Else sgn$ = "-"
  210.     End If
  211.     'now according to signs and op$ call add$ or subtr$
  212.     If op$ = "+" Then
  213.         If aSgn$ = bSgn$ Then 'really add
  214.             postOp$ = aSgn$ + add$(ca$, cb$)
  215.         Else 'have a case of subtraction
  216.             If aSgn$ = "-" Then postOp$ = subtr$(cb$, ca$) Else postOp$ = subtr$(ca$, cb$)
  217.         End If
  218.     ElseIf op$ = "-" Then
  219.         If bSgn$ = "-" Then 'really add but switch b sign
  220.             bSgn$ = ""
  221.             If aSgn$ = "-" Then
  222.                 postOp$ = subtr$(cb$, ca$)
  223.             Else 'aSgn = ""
  224.                 postOp$ = add$(ca$, cb$)
  225.             End If
  226.         Else 'bSgn$ =""
  227.             If aSgn$ = "-" Then
  228.                 bSgn$ = "-"
  229.                 postOp$ = aSgn$ + add$(ca$, cb$)
  230.             Else
  231.                 postOp$ = subtr$(ca$, cb$)
  232.             End If
  233.         End If
  234.     ElseIf op$ = "*" Then
  235.         postOp$ = sgn$ + mult$(ca$, cb$)
  236.     ElseIf op$ = "/" Then
  237.         postOp$ = sgn$ + divide$(ca$, cb$)
  238.     End If ' which op
  239.     'put dp back
  240.     If op$ <> "/" Then
  241.         lpop = Len(postOp$) ' put decimal back
  242.         postOp$ = Mid$(postOp$, 1, lpop - dp) + "." + Mid$(postOp$, lpop - dp + 1)
  243.     End If
  244.     mr$ = trim0$(postOp$)
  245.  
  246. Function divide$ (n$, d$) ' goal here is 100 digits precision not 100 digits past decimal
  247.     Dim di$, ndi$, nD As Integer
  248.     If trim0$(n$) = "0" Then divide$ = "0": Exit Function
  249.     If trim0$(d$) = "0" Then divide$ = "div 0": Exit Function
  250.     If trim0$(d$) = "1" Then divide$ = trim0$(n$): Exit Function '8/17 add trim0$
  251.  
  252.     ' aha! found a bug when d$ gets really huge 100 is no where near enough!!!!
  253.     ' 2021-06-03 fix by adding 100 to len(d$), plus have to go a little past 100 for 100 digit precision
  254.     ' need to go past 100 for 100 precise digits (not decimal places)
  255.     di$ = Mid$(nInverse$(d$, Len(d$) + 200), 2) 'chop off decimal point after
  256.     nD = Len(di$)
  257.     ndi$ = mult$(n$, di$)
  258.     ndi$ = Mid$(ndi$, 1, Len(ndi$) - nD) + "." + Right$(String$(nD, "0") + Right$(ndi$, nD), nD)
  259.     divide$ = trim0$(ndi$)
  260.  
  261. Function nInverse$ (n$, DP As Integer) 'assume decimal at very start of the string of digits returned, no rounding
  262.     Dim m$(1 To 9), si$, r$, outstr$, d$
  263.     Dim i As Integer
  264.     For i = 1 To 9
  265.         si$ = _Trim$(Str$(i))
  266.         m$(i) = mult$(si$, n$)
  267.     Next
  268.     outstr$ = ""
  269.     If n$ = "0" Then nInverse$ = "Div 0": Exit Function
  270.     If n$ = "1" Then nInverse$ = "1": Exit Function
  271.     outstr$ = "." 'everything else n > 1 is decimal 8/17
  272.     r$ = "10"
  273.     Do
  274.         While Left$(subtr$(r$, n$), 1) = "-" '   r - n < 0
  275.             outstr$ = outstr$ + "0" '            add 0 to the  output string
  276.             If Len(outstr$) = DP Then nInverse$ = outstr$: Exit Function 'check if we've reached DP length
  277.             r$ = r$ + "0"
  278.         Wend
  279.         For i = 9 To 1 Step -1
  280.             If LTE(m$(i), r$) Then d$ = _Trim$(Str$(i)): Exit For
  281.         Next
  282.         outstr$ = outstr$ + d$
  283.         If Len(outstr$) = DP Then nInverse$ = outstr$: Exit Function
  284.         r$ = subtr$(r$, mult$(d$, n$)) 'r = r -d*n
  285.         If r$ = "0" Then nInverse$ = outstr$: Exit Function 'found a perfect divisor
  286.         r$ = r$ + "0" 'add another place
  287.     Loop
  288.  
  289. Function mult$ (a$, b$) 'assume both positive integers prechecked as all digits strings
  290.     Dim la As Integer, lb As Integer, m As Integer, g As Integer, dp As Integer
  291.     Dim f18$, f1$, t$, build$, accum$
  292.  
  293.     If trim0$(a$) = "0" Then mult$ = "0": Exit Function
  294.     If trim0$(b$) = "0" Then mult$ = "0": Exit Function
  295.     If trim0$(a$) = "1" Then mult$ = trim0$(b$): Exit Function
  296.     If trim0$(b$) = "1" Then mult$ = trim0$(a$): Exit Function
  297.     'find the longer number and make it a mult of 18 to take 18 digits at a time from it
  298.     la = Len(a$): lb = Len(b$)
  299.     If la > lb Then
  300.         m = Int(la / 18) + 1
  301.         f18$ = Right$(String$(m * 18, "0") + a$, m * 18)
  302.         f1$ = b$
  303.     Else
  304.         m = Int(lb / 18) + 1
  305.         f18$ = Right$(String$(m * 18, "0") + b$, m * 18)
  306.         f1$ = a$
  307.     End If
  308.     For dp = Len(f1$) To 1 Step -1 'dp = digit position of the f1$
  309.         build$ = "" 'line builder
  310.         co = 0
  311.         'now taking 18 digits at a time Thanks Steve McNeill
  312.         For g = 1 To m
  313.             v18 = Val(Mid$(f18$, m * 18 - g * 18 + 1, 18))
  314.             sd = Val(Mid$(f1$, dp, 1))
  315.             t$ = Right$(String$(19, "0") + _Trim$(Str$(v18 * sd + co)), 19)
  316.             co = Val(Mid$(t$, 1, 1))
  317.             build$ = Mid$(t$, 2) + build$
  318.         Next g
  319.         If co Then build$ = _Trim$(Str$(co)) + build$
  320.         If dp = Len(f1$) Then
  321.             accum$ = build$
  322.         Else
  323.             accum$ = add$(accum$, build$ + String$(Len(f1$) - dp, "0"))
  324.         End If
  325.     Next dp
  326.     mult$ = accum$
  327.  
  328. Function subtr$ (sum$, minus$) ' assume both numbers are positive all digits
  329.     Dim m As Integer, g As Integer, p As Integer
  330.     Dim ts$, tm$, sign$, LG$, sm$, t$, result$
  331.  
  332.     ts$ = TrimLead0$(sum$): tm$ = TrimLead0$(minus$)
  333.     If trim0(ts$) = trim0$(tm$) Then subtr$ = "0": Exit Function 'OK proceed with function knowing they are not equal
  334.     tenE18 = 1000000000000000000 'yes!!! no dang E's
  335.     If LTE(ts$, tm$) Then ' which is bigger? minus is bigger
  336.         sign$ = "-"
  337.         m = Int(Len(tm$) / 18) + 1
  338.         LG$ = Right$(String$(m * 18, "0") + tm$, m * 18)
  339.         sm$ = Right$(String$(m * 18, "0") + ts$, m * 18)
  340.     Else 'sum is bigger
  341.         sign$ = ""
  342.         m = Int(Len(ts$) / 18) + 1
  343.         LG$ = Right$(String$(m * 18, "0") + ts$, m * 18)
  344.         sm$ = Right$(String$(m * 18, "0") + tm$, m * 18)
  345.     End If
  346.     'now taking 18 digits at a time From Steve I learned we can do more than 1 digit at a time
  347.     For g = 1 To m
  348.         VB = Val(Mid$(LG$, m * 18 - g * 18 + 1, 18))
  349.         vs = Val(Mid$(sm$, m * 18 - g * 18 + 1, 18))
  350.         If vs > VB Then
  351.             t$ = Right$(String$(18, "0") + _Trim$(Str$(tenE18 - vs + VB)), 18)
  352.             p = (m - g) * 18
  353.             While p > 0 And Mid$(LG$, p, 1) = "0"
  354.                 Mid$(LG$, p, 1) = "9"
  355.                 p = p - 1
  356.             Wend
  357.             If p > 0 Then Mid$(LG$, p, 1) = _Trim$(Str$(Val(Mid$(LG$, p, 1)) - 1))
  358.         Else
  359.             t$ = Right$(String$(18, "0") + _Trim$(Str$(VB - vs)), 18)
  360.         End If
  361.         result$ = t$ + result$
  362.     Next
  363.     subtr$ = sign$ + result$
  364.  
  365. Function add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no spaces or - signs
  366.     'first thing is to set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
  367.     Dim la As Integer, lb As Integer, m As Integer, g As Integer
  368.     Dim fa$, fb$, t$, new$, result$
  369.     la = Len(a$): lb = Len(b$)
  370.     If la > lb Then m = Int(la / 18) + 1 Else m = Int(lb / 18) + 1
  371.     fa$ = Right$(String$(m * 18, "0") + a$, m * 18)
  372.     fb$ = Right$(String$(m * 18, "0") + b$, m * 18)
  373.  
  374.     'now taking 18 digits at a time Thanks Steve McNeill
  375.     For g = 1 To m
  376.         sa = Val(Mid$(fa$, m * 18 - g * 18 + 1, 18))
  377.         sb = Val(Mid$(fb$, m * 18 - g * 18 + 1, 18))
  378.         t$ = Right$(String$(36, "0") + _Trim$(Str$(sa + sb + co)), 36)
  379.         co = Val(Mid$(t$, 1, 18))
  380.         new$ = Mid$(t$, 19)
  381.         result$ = new$ + result$
  382.     Next
  383.     If co Then result$ = Str$(co) + result$
  384.     add$ = result$
  385.  
  386. ' String Math Helpers -----------------------------------------------
  387.  
  388. 'this function needs TrimLead0$(s$)
  389. Function LTE (a$, b$) ' a$ Less Than or Equal b$  comparison of 2 strings
  390.     Dim ca$, cb$, la As Integer, lb As Integer, i As Integer
  391.     ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
  392.     la = Len(ca$): lb = Len(cb$)
  393.     If ca$ = cb$ Then
  394.         LTE = -1
  395.     ElseIf la < lb Then ' a is smaller
  396.         LTE = -1
  397.     ElseIf la > lb Then ' a is bigger
  398.         LTE = 0
  399.     ElseIf la = lb Then ' equal lengths
  400.         For i = 1 To Len(ca$)
  401.             If Val(Mid$(ca$, i, 1)) > Val(Mid$(cb$, i, 1)) Then
  402.                 LTE = 0: Exit Function
  403.             ElseIf Val(Mid$(ca$, i, 1)) < Val(Mid$(cb$, i, 1)) Then
  404.                 LTE = -1: Exit Function
  405.             End If
  406.         Next
  407.     End If
  408.  
  409. ' ------------------------------------- use these for final display
  410.  
  411. Function TrimLead0$ (s$) 'for treating strings as number (pos integers)
  412.     Dim copys$, i As Integer, find As Integer
  413.     copys$ = _Trim$(s$) 'might as well remove spaces too
  414.     i = 1: find = 0
  415.     While i < Len(copys$) And Mid$(copys$, i, 1) = "0"
  416.         i = i + 1: find = 1
  417.     Wend
  418.     If find = 1 Then copys$ = Mid$(copys$, i)
  419.     If copys$ = "" Then TrimLead0$ = "0" Else TrimLead0$ = copys$
  420.  
  421. Function TrimTail0$ (s$)
  422.     Dim copys$, dp As Integer, i As Integer, find As Integer
  423.     copys$ = _Trim$(s$) 'might as well remove spaces too
  424.     TrimTail0$ = copys$
  425.     dp = InStr(copys$, ".")
  426.     If dp > 0 Then
  427.         i = Len(copys$): find = 0
  428.         While i > dp And Mid$(copys$, i, 1) = "0"
  429.             i = i - 1: find = 1
  430.         Wend
  431.         If find = 1 Then
  432.             If i = dp Then
  433.                 TrimTail0$ = Mid$(copys$, 1, dp - 1)
  434.             Else
  435.                 TrimTail0$ = Mid$(copys$, 1, i)
  436.             End If
  437.         End If
  438.     End If
  439.  
  440. Function trim0$ (s$)
  441.     Dim cs$, si$
  442.     cs$ = s$
  443.     si$ = Left$(cs$, 1)
  444.     If si$ = "-" Then cs$ = Mid$(cs$, 2)
  445.     cs$ = TrimLead0$(cs$)
  446.     cs$ = TrimTail0$(cs$)
  447.     If Right$(cs$, 1) = "." Then cs$ = Mid$(cs$, 1, Len(cs$) - 1)
  448.     If si$ = "-" Then trim0$ = si$ + cs$ Else trim0$ = cs$
  449.  
  450. ' for displaying truncated numbers say to 60 digits
  451. Function showDP$ (num$, nDP As Integer)
  452.     Dim cNum$, dp As Integer, d As Integer, i As Integer
  453.     cNum$ = num$ 'since num$ could get changed
  454.     showDP$ = num$
  455.     dp = InStr(num$, ".")
  456.     If dp > 0 Then
  457.         If Len(Mid$(cNum$, dp + 1)) > nDP Then
  458.             d = Val(Mid$(cNum$, dp + nDP + 1, 1))
  459.             If d > 4 Then
  460.                 cNum$ = "0" + cNum$ ' tack on another 0 just in case 9's all the way to left
  461.                 dp = dp + 1
  462.                 i = dp + nDP
  463.                 While Mid$(cNum$, i, 1) = "9" Or Mid$(cNum$, i, 1) = "."
  464.                     If Mid$(cNum$, i, 1) = "9" Then
  465.                         Mid$(cNum$, i, 1) = "0"
  466.                     End If
  467.                     i = i - 1
  468.                 Wend
  469.                 Mid$(cNum$, i, 1) = _Trim$(Str$(Val(Mid$(cNum$, i, 1)) + 1)) 'last non 9 digit
  470.                 cNum$ = Mid$(cNum$, 1, dp + nDP) 'chop it
  471.                 showDP$ = trim0$(cNum$)
  472.             Else
  473.                 showDP$ = Mid$(cNum$, 1, dp + nDP)
  474.             End If
  475.         End If
  476.     End If
  477.  
Title: Re: String Math
Post by: bplus on June 04, 2021, 06:46:13 pm
The subtr$ Function is terrible!

I should have checked way larger and smaller numbers with it. I think I just checked it with QB64 Doubles or Floats for errors but the bug doesn't show until numbers are longer than 18 digits.

Apologies to all who wasted time working that code.

Still, our goal is a good one I think.

PS This explains why the attempts with Newton-Raphson were not working with Mr$() Function calls and why I was getting good sqrRoots because I was using the Average Method that did not use subtraction.
Title: Re: String Math
Post by: George McGinn on June 05, 2021, 01:16:38 am
The sqrt$ also performs subtr$ function. The root_n$ I have now (non-string version) and your power## seems to work fine.

Also, I noticed that for the power## function, when I do 1.2^5 [mr$("1.2", "^", "5")] I get the right digits, but the decimal place isn't put back into the final result (see screen print).

  [ This attachment cannot be displayed inline in 'Print Page' view ]  
Title: Re: String Math
Post by: bplus on June 05, 2021, 11:07:29 pm
Well it took all day to find a simple little change in the subtr$() Function that fixes problems. That was the good news.

The bad news, it is now taking the inverse of STx number to see 115 terms of Fibonacci series (or is it sequence)  about 30 secs to calculate, before it was up in a sneeze. It looks different also at the end but the same 115 terms of Fibonacci can still be found.

OK so the first line in subtr$() fixes the bug that jack pointed out that I confirmed and it also fixes my version of George's version of Newton-Raphson estimates of SQR() but it is definitely slowed down. We now get same sqr roots for 100 + 30-0's and .20-0's 64 for both versions.

Anyway here is the code checks and tests:
Code: QB64: [Select]
  1. _Title "Math regulator mr test sqr estimating" ' b+ start 2021-06-02
  2. ' divide$ found fix? 2021-06-03 comment in that function
  3. ' 2021-06-03 update with new sqrRoot$ Function
  4.  
  5. Randomize Timer 'now that it's seems to be running silent
  6. Screen _NewImage(1200, 700, 32)
  7. _Delay .25
  8.  
  9. ' jack error reported 2021-06-04 confirmed!   fixed
  10. Print mr$(".00000000000000000000000000000000000000000000200000000000000054307978001764", "-", ".000000000000000000000000000000000000000000002")
  11. Print ".00000000000000000000000000000000000000000000000000000000000054307978001764"
  12.  
  13. Dim r$, ruler$
  14. ruler$ = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" + Chr$(10)
  15. ruler$ = ruler$ + "0         1         2         3         4         5         6         7         8         9        10        11        12"
  16. ' debug tests
  17. Print mr$("-5", "+", "-2100"), " OK if -2105"
  18. Print mr$("." + String$(20, "0") + "1", "+", "1" + String$(40, "0") + "1") ' first real test of add
  19. Print ruler$
  20. Print Val("." + String$(20, "0") + "1") + Val("1" + String$(40, "0") + "1") ' test print of big and small val
  21. Print mr$("-.00071", "+", ".00036" + String$(35, "0") + "9")
  22. '-.00071
  23. ' .00036000000000000000000000000000000000009
  24. Print "-.00034999999999999999999999999999999999991"
  25. Print ruler$
  26. 'testing a different subtract sub
  27. Print mr$("10", "-", "5"), " 5 OK"
  28. Print mr$("-10", "+", "5"), " -5 OK"
  29. Print mr$("-10", "-", "-5"), " -5 OK"
  30. Print mr$("-10", "-", "5"), " -15  OK  added"
  31. Print mr$("10", "-", "-5"), " 15  OK  added"
  32. Print mr$("-.010", "-", "-5"), "4.99 OK"
  33. Print mr$("-.010", "-", "5"), "-5.01  OK just added"
  34. Print mr$(".010", "-", "5"), " -4.99 OK"
  35. ' jack error reported 2021-06-04 confirmed!  variation below
  36. r$ = mr$(".00000000200000000000000054307978001764", "-", ".0000000020000000000000001") '16 0 wrong  8 wrong
  37. Print "   mr$ rtnd:"; r$
  38. Print "    compare:.00000000000000000000000044307978001764" ' 2021-06-05 finally!
  39. '                  .0000000020000000000000001
  40. '                  .00000000200000000000000054307978001764
  41.  
  42. r$ = mr$(".00000000000000000100000000000000000011", "-", ".000000000000000001000000000000000001") ' bad too
  43. Print "   mr$ rtnd:"; r$
  44. '         ".000000000000000001000000000000000001"
  45. Print "    compare:-.00000000000000000000000000000000000089"
  46. r$ = mr$(".00000000000000000100000000000000000111", "-", ".000000000000000002000000000000000001")
  47. '       ".000000000000000002000000000000000001"
  48. '       ".00000000000000000100000000000000000111"
  49. '       ".00000000000000000099999999999999999989"
  50. Print "   mr$ rtnd:"; r$
  51. Print "    compare:-.00000000000000000099999999999999999989"
  52.  
  53. r$ = mr$(".00000000000000000000000000999", "-", "1")
  54. '-1.00000000000000000000000000000000000000000
  55. '  .00000000000000000000000000999
  56. ' -.99999999999999999999999999001
  57. Print "   mr$ rtnd:"; r$
  58. Print "    compare:-.99999999999999999999999999001"
  59.  
  60. r$ = mr$("1", "+", "-1000000000000000000000000000000000000000")
  61. Print "   mr$ rtnd:"; r$
  62. '1000000000000000000000000000000000000000
  63. '-999999999999999999999999999999999999999
  64. Print "    compare:-999999999999999999999999999999999999999"
  65.  
  66. ' check jack problems with FB translation  2021-06-03  errors must be in FB trans from QB64
  67. Print Mid$(mr$(" .1", "/", "3"), 1, 100) ' too long?
  68. Print Mid$(mr$("1.1", "/", "9"), 1, 100)
  69. Print Mid$(mr$("1.38", "/", "1.2"), 1, 100)
  70. Print ruler$
  71.  
  72. Print "checking inverse of 50 = .02 "; nInverse$("50", 20) 'OK  .020000... length 20
  73. Print ruler$
  74. Print "zzz... see inverse of STx number now takes close to 30 secs with fixed subtr$() sub,"
  75. Print "Use to come up in a sneeze! It also looks different way more space on end but still"
  76. Print "can find 115 Fibonacci terms in it."
  77. Print "    The only difference is I am not trimming leading 0's in subst$() function!"
  78. Print nInverse$("999999999999999999999998999999999999999999999999", 4000) ' wow big delay!
  79.  
  80.  
  81.  
  82. Dim n$, result$
  83.     'remember everything is strings
  84.     Input "Enter a number to find it's square root "; n$
  85.     If n$ = "" Then End
  86.     result$ = sqrRoot$(n$)
  87.     Print result$
  88.     Print "Length ="; Len(result$)
  89.     _Delay 2
  90.     result$ = SRbyNR$(n$)
  91.     Print result$
  92.     Print "Length ="; Len(result$)
  93.     Print
  94.  
  95. Function sqrRoot$ (nmbr$)
  96.     Dim n$, guess$, lastGuess$, other$, sum$, imaginary$, loopcnt
  97.     If Left$(nmbr$, 1) = "-" Then 'handle neg numbers
  98.         imaginary$ = "*i": n$ = Mid$(nmbr$, 2)
  99.     Else
  100.         imaginary$ = "": n$ = nmbr$
  101.     End If
  102.     guess$ = mr$(n$, "/", "2")
  103.     other$ = n$
  104.     Do
  105.         loopcnt = loopcnt + 1
  106.         Print "loop cnt"; loopcnt
  107.  
  108.         If (Mid$(guess$, 1, 105) = Mid$(lastGuess$, 1, 105)) Then ' go past 100 matching digits for 100 digit precision
  109.             sqrRoot$ = Mid$(other$, 1, 101) + imaginary$ ' try other factor for guess$  sometimes it nails answer without all digits
  110.             Exit Function
  111.         Else
  112.             lastGuess$ = guess$
  113.             sum$ = mr$(guess$, "+", other$)
  114.             guess$ = mr$(sum$, "/", "2")
  115.             other$ = mr$(n$, "/", guess$)
  116.         End If
  117.     Loop
  118.  
  119. Function SRbyNR$ (nmbr$) ' square root by Newton - Ralphson method my interpretation of GeorgeMcGinn
  120.  
  121.     Dim n$, guess$, lastGuess$, dx$, imaginary$, other$, loopcnt
  122.     If Left$(nmbr$, 1) = "-" Then 'handle neg numbers
  123.         imaginary$ = "*i": n$ = Mid$(nmbr$, 2)
  124.     Else
  125.         imaginary$ = "": n$ = nmbr$
  126.     End If
  127.  
  128.     guess$ = mr$(n$, "/", "2") ' get this going first and then try better starting guess later
  129.  
  130.     Do
  131.         loopcnt = loopcnt + 1
  132.         Print "loop cnt"; loopcnt
  133.         If (Mid$(guess$, 1, 105) = Mid$(lastGuess$, 1, 105)) Then ' go past 100 matching digits for 100 digit precision
  134.             SRbyNR$ = Mid$(other$, 1, 101) + imaginary$
  135.             Exit Function
  136.         Else
  137.             'dx = (x - A / x) / 2: x = x - dx ' Thanks George
  138.             lastGuess$ = guess$
  139.             dx$ = mr$(mr$(guess$, "-", mr$(n$, "/", guess$)), "/", "2")
  140.             guess$ = mr$(guess$, "-", dx$)
  141.             other$ = mr$(n$, "/", guess$) ' try other factor for guess$  sometimes it nails answer without all digits
  142.         End If
  143.     Loop
  144.  
  145.  
  146. Function mr$ (a$, op$, b$) ' catchy? mr$ for math regulator
  147.     Dim ca$, cb$, aSgn$, bSgn$, postOp$, sgn$
  148.     Dim adp As Integer, bdp As Integer, dp As Integer, lpop As Integer
  149.  
  150.     op$ = _Trim$(op$) 'save fixing each time
  151.     ca$ = _Trim$(a$): cb$ = _Trim$(b$) 'make copies in case we change
  152.     'strip signs and decimals
  153.     If Left$(ca$, 1) = "-" Then
  154.         aSgn$ = "-": ca$ = Mid$(ca$, 2)
  155.     Else
  156.         aSgn$ = "": ca$ = ca$
  157.     End If
  158.     dp = InStr(ca$, ".")
  159.     If dp > 0 Then
  160.         adp = Len(ca$) - dp
  161.         ca$ = Mid$(ca$, 1, dp - 1) + Mid$(ca$, dp + 1)
  162.     Else
  163.         adp = 0
  164.     End If
  165.     If Left$(cb$, 1) = "-" Then
  166.         bSgn$ = "-": cb$ = Mid$(cb$, 2)
  167.     Else
  168.         bSgn$ = "": cb$ = cb$
  169.     End If
  170.     dp = InStr(cb$, ".")
  171.     If dp > 0 Then
  172.         bdp = Len(cb$) - dp
  173.         cb$ = Mid$(cb$, 1, dp - 1) + Mid$(cb$, dp + 1)
  174.     Else
  175.         bdp = 0
  176.     End If
  177.  
  178.     If op$ = "+" Or op$ = "-" Or op$ = "/" Then 'add or subtr  even up strings on right of decimal
  179.         'even up the right sides of decimals if any
  180.         If adp > bdp Then dp = adp Else dp = bdp
  181.         If adp < dp Then ca$ = ca$ + String$(dp - adp, "0")
  182.         If bdp < dp Then cb$ = cb$ + String$(dp - bdp, "0")
  183.     ElseIf op$ = "*" Then
  184.         dp = adp + bdp
  185.     End If
  186.     If op$ = "*" Or op$ = "/" Then
  187.         If aSgn$ = bSgn$ Then sgn$ = "" Else sgn$ = "-"
  188.     End If
  189.     'now according to signs and op$ call add$ or subtr$
  190.     If op$ = "+" Then
  191.         If aSgn$ = bSgn$ Then 'really add
  192.             postOp$ = aSgn$ + add$(ca$, cb$)
  193.         Else 'have a case of subtraction
  194.             If aSgn$ = "-" Then postOp$ = subtr$(cb$, ca$) Else postOp$ = subtr$(ca$, cb$)
  195.         End If
  196.     ElseIf op$ = "-" Then
  197.         If bSgn$ = "-" Then 'really add but switch b sign
  198.             bSgn$ = ""
  199.             If aSgn$ = "-" Then
  200.                 postOp$ = subtr$(cb$, ca$)
  201.             Else 'aSgn = ""
  202.                 postOp$ = add$(ca$, cb$)
  203.             End If
  204.         Else 'bSgn$ =""
  205.             If aSgn$ = "-" Then
  206.                 bSgn$ = "-"
  207.                 postOp$ = aSgn$ + add$(ca$, cb$)
  208.             Else
  209.                 postOp$ = subtr$(ca$, cb$)
  210.             End If
  211.         End If
  212.     ElseIf op$ = "*" Then
  213.         postOp$ = sgn$ + mult$(ca$, cb$)
  214.     ElseIf op$ = "/" Then
  215.         postOp$ = sgn$ + divide$(ca$, cb$)
  216.     End If ' which op
  217.     'put dp back
  218.     If op$ <> "/" Then
  219.         lpop = Len(postOp$) ' put decimal back
  220.         postOp$ = Mid$(postOp$, 1, lpop - dp) + "." + Mid$(postOp$, lpop - dp + 1)
  221.     End If
  222.     mr$ = trim0$(postOp$)
  223.  
  224. Function divide$ (n$, d$) ' goal here is 100 digits precision not 100 digits past decimal
  225.     Dim di$, ndi$, nD As Integer
  226.     If trim0$(n$) = "0" Then divide$ = "0": Exit Function
  227.     If trim0$(d$) = "0" Then divide$ = "div 0": Exit Function
  228.     If trim0$(d$) = "1" Then divide$ = trim0$(n$): Exit Function '8/17 add trim0$
  229.  
  230.     ' aha! found a bug when d$ gets really huge 100 is no where near enough!!!!
  231.     ' 2021-06-03 fix by adding 100 to len(d$), plus have to go a little past 100 for 100 digit precision
  232.     ' need to go past 100 for 100 precise digits (not decimal places)
  233.     di$ = Mid$(nInverse$(d$, Len(d$) + 200), 2) 'chop off decimal point after
  234.     nD = Len(di$)
  235.     ndi$ = mult$(n$, di$)
  236.     ndi$ = Mid$(ndi$, 1, Len(ndi$) - nD) + "." + Right$(String$(nD, "0") + Right$(ndi$, nD), nD)
  237.     divide$ = trim0$(ndi$)
  238.  
  239. Function nInverse$ (n$, DP As Integer) 'assume decimal at very start of the string of digits returned, no rounding
  240.     Dim m$(1 To 9), si$, r$, outstr$, d$
  241.     Dim i As Integer
  242.     For i = 1 To 9
  243.         si$ = _Trim$(Str$(i))
  244.         m$(i) = mult$(si$, n$)
  245.     Next
  246.     outstr$ = ""
  247.     If n$ = "0" Then nInverse$ = "Div 0": Exit Function
  248.     If n$ = "1" Then nInverse$ = "1": Exit Function
  249.     outstr$ = "." 'everything else n > 1 is decimal 8/17
  250.     r$ = "10"
  251.     Do
  252.         While Left$(subtr$(r$, n$), 1) = "-" '   r - n < 0
  253.             outstr$ = outstr$ + "0" '            add 0 to the  output string
  254.             If Len(outstr$) = DP Then nInverse$ = outstr$: Exit Function 'check if we've reached DP length
  255.             r$ = r$ + "0"
  256.         Wend
  257.         For i = 9 To 1 Step -1
  258.             If LTE(m$(i), r$) Then d$ = _Trim$(Str$(i)): Exit For
  259.         Next
  260.         outstr$ = outstr$ + d$
  261.         If Len(outstr$) = DP Then nInverse$ = outstr$: Exit Function
  262.         r$ = subtr$(r$, mult$(d$, n$)) 'r = r -d*n
  263.         If r$ = "0" Then nInverse$ = outstr$: Exit Function 'found a perfect divisor
  264.         r$ = r$ + "0" 'add another place
  265.     Loop
  266.  
  267. Function mult$ (a$, b$) 'assume both positive integers prechecked as all digits strings
  268.     Dim la As Integer, lb As Integer, m As Integer, g As Integer, dp As Integer
  269.     Dim f18$, f1$, t$, build$, accum$
  270.  
  271.     If trim0$(a$) = "0" Then mult$ = "0": Exit Function
  272.     If trim0$(b$) = "0" Then mult$ = "0": Exit Function
  273.     If trim0$(a$) = "1" Then mult$ = trim0$(b$): Exit Function
  274.     If trim0$(b$) = "1" Then mult$ = trim0$(a$): Exit Function
  275.     'find the longer number and make it a mult of 18 to take 18 digits at a time from it
  276.     la = Len(a$): lb = Len(b$)
  277.     If la > lb Then
  278.         m = Int(la / 18) + 1
  279.         f18$ = Right$(String$(m * 18, "0") + a$, m * 18)
  280.         f1$ = b$
  281.     Else
  282.         m = Int(lb / 18) + 1
  283.         f18$ = Right$(String$(m * 18, "0") + b$, m * 18)
  284.         f1$ = a$
  285.     End If
  286.     For dp = Len(f1$) To 1 Step -1 'dp = digit position of the f1$
  287.         build$ = "" 'line builder
  288.         co = 0
  289.         'now taking 18 digits at a time Thanks Steve McNeill
  290.         For g = 1 To m
  291.             v18 = Val(Mid$(f18$, m * 18 - g * 18 + 1, 18))
  292.             sd = Val(Mid$(f1$, dp, 1))
  293.             t$ = Right$(String$(19, "0") + _Trim$(Str$(v18 * sd + co)), 19)
  294.             co = Val(Mid$(t$, 1, 1))
  295.             build$ = Mid$(t$, 2) + build$
  296.         Next g
  297.         If co Then build$ = _Trim$(Str$(co)) + build$
  298.         If dp = Len(f1$) Then
  299.             accum$ = build$
  300.         Else
  301.             accum$ = add$(accum$, build$ + String$(Len(f1$) - dp, "0"))
  302.         End If
  303.     Next dp
  304.     mult$ = accum$
  305.  
  306. Function subtr$ (sum$, minus$) ' assume both numbers are positive all digits
  307.     Dim m As Integer, g As Integer, p As Integer
  308.     Dim ts$, tm$, sign$, LG$, sm$, t$, result$
  309.  
  310.     'ts$ = TrimLead0$(sum$): tm$ = TrimLead0$(minus$)   'orig with decent square root speed
  311.     ts$ = _Trim$(sum$): tm$ = _Trim$(minus$) ' fixed subtr$ 2021-06-05
  312.     If trim0(ts$) = trim0$(tm$) Then subtr$ = "0": Exit Function 'OK proceed with function knowing they are not equal
  313.     tenE18 = 1000000000000000000 'yes!!! no dang E's
  314.     If LTE(ts$, tm$) Then ' which is bigger? minus is bigger
  315.         sign$ = "-"
  316.         m = Int(Len(tm$) / 18) + 1
  317.         LG$ = Right$(String$(m * 18, "0") + tm$, m * 18)
  318.         sm$ = Right$(String$(m * 18, "0") + ts$, m * 18)
  319.     Else 'sum is bigger
  320.         sign$ = ""
  321.         m = Int(Len(ts$) / 18) + 1
  322.         LG$ = Right$(String$(m * 18, "0") + ts$, m * 18)
  323.         sm$ = Right$(String$(m * 18, "0") + tm$, m * 18)
  324.     End If
  325.     'now taking 18 digits at a time From Steve I learned we can do more than 1 digit at a time
  326.     For g = 1 To m
  327.         VB = Val(Mid$(LG$, m * 18 - g * 18 + 1, 18))
  328.         vs = Val(Mid$(sm$, m * 18 - g * 18 + 1, 18))
  329.         If vs > VB Then
  330.             t$ = Right$(String$(18, "0") + _Trim$(Str$(tenE18 - vs + VB)), 18)
  331.             p = (m - g) * 18
  332.             While p > 0 And Mid$(LG$, p, 1) = "0"
  333.                 Mid$(LG$, p, 1) = "9"
  334.                 p = p - 1
  335.             Wend
  336.             If p > 0 Then Mid$(LG$, p, 1) = _Trim$(Str$(Val(Mid$(LG$, p, 1)) - 1))
  337.         Else
  338.             t$ = Right$(String$(18, "0") + _Trim$(Str$(VB - vs)), 18)
  339.         End If
  340.         result$ = t$ + result$
  341.     Next
  342.     subtr$ = sign$ + result$
  343.  
  344. Function add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no spaces or - signs
  345.     'first thing is to set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
  346.     Dim la As Integer, lb As Integer, m As Integer, g As Integer
  347.     Dim fa$, fb$, t$, new$, result$
  348.     la = Len(a$): lb = Len(b$)
  349.     If la > lb Then m = Int(la / 18) + 1 Else m = Int(lb / 18) + 1
  350.     fa$ = Right$(String$(m * 18, "0") + a$, m * 18)
  351.     fb$ = Right$(String$(m * 18, "0") + b$, m * 18)
  352.  
  353.     'now taking 18 digits at a time Thanks Steve McNeill
  354.     For g = 1 To m
  355.         sa = Val(Mid$(fa$, m * 18 - g * 18 + 1, 18))
  356.         sb = Val(Mid$(fb$, m * 18 - g * 18 + 1, 18))
  357.         t$ = Right$(String$(36, "0") + _Trim$(Str$(sa + sb + co)), 36)
  358.         co = Val(Mid$(t$, 1, 18))
  359.         new$ = Mid$(t$, 19)
  360.         result$ = new$ + result$
  361.     Next
  362.     If co Then result$ = Str$(co) + result$
  363.     add$ = result$
  364.  
  365. ' String Math Helpers -----------------------------------------------
  366.  
  367. 'this function needs TrimLead0$(s$)
  368. Function LTE (a$, b$) ' a$ Less Than or Equal b$  comparison of 2 strings
  369.     Dim ca$, cb$, la As Integer, lb As Integer, i As Integer
  370.     ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
  371.     la = Len(ca$): lb = Len(cb$)
  372.     If ca$ = cb$ Then
  373.         LTE = -1
  374.     ElseIf la < lb Then ' a is smaller
  375.         LTE = -1
  376.     ElseIf la > lb Then ' a is bigger
  377.         LTE = 0
  378.     ElseIf la = lb Then ' equal lengths
  379.         For i = 1 To Len(ca$)
  380.             If Val(Mid$(ca$, i, 1)) > Val(Mid$(cb$, i, 1)) Then
  381.                 LTE = 0: Exit Function
  382.             ElseIf Val(Mid$(ca$, i, 1)) < Val(Mid$(cb$, i, 1)) Then
  383.                 LTE = -1: Exit Function
  384.             End If
  385.         Next
  386.     End If
  387.  
  388. ' ------------------------------------- use these for final display
  389.  
  390. Function TrimLead0$ (s$) 'for treating strings as number (pos integers)
  391.     Dim copys$, i As Integer, find As Integer
  392.     copys$ = _Trim$(s$) 'might as well remove spaces too
  393.     i = 1: find = 0
  394.     While i < Len(copys$) And Mid$(copys$, i, 1) = "0"
  395.         i = i + 1: find = 1
  396.     Wend
  397.     If find = 1 Then copys$ = Mid$(copys$, i)
  398.     If copys$ = "" Then TrimLead0$ = "0" Else TrimLead0$ = copys$
  399.  
  400. Function TrimTail0$ (s$)
  401.     Dim copys$, dp As Integer, i As Integer, find As Integer
  402.     copys$ = _Trim$(s$) 'might as well remove spaces too
  403.     TrimTail0$ = copys$
  404.     dp = InStr(copys$, ".")
  405.     If dp > 0 Then
  406.         i = Len(copys$): find = 0
  407.         While i > dp And Mid$(copys$, i, 1) = "0"
  408.             i = i - 1: find = 1
  409.         Wend
  410.         If find = 1 Then
  411.             If i = dp Then
  412.                 TrimTail0$ = Mid$(copys$, 1, dp - 1)
  413.             Else
  414.                 TrimTail0$ = Mid$(copys$, 1, i)
  415.             End If
  416.         End If
  417.     End If
  418.  
  419. Function trim0$ (s$)
  420.     Dim cs$, si$
  421.     cs$ = s$
  422.     si$ = Left$(cs$, 1)
  423.     If si$ = "-" Then cs$ = Mid$(cs$, 2)
  424.     cs$ = TrimLead0$(cs$)
  425.     cs$ = TrimTail0$(cs$)
  426.     If Right$(cs$, 1) = "." Then cs$ = Mid$(cs$, 1, Len(cs$) - 1)
  427.     If si$ = "-" Then trim0$ = si$ + cs$ Else trim0$ = cs$
  428.  
  429. ' for displaying truncated numbers say to 60 digits
  430. Function showDP$ (num$, nDP As Integer)
  431.     Dim cNum$, dp As Integer, d As Integer, i As Integer
  432.     cNum$ = num$ 'since num$ could get changed
  433.     showDP$ = num$
  434.     dp = InStr(num$, ".")
  435.     If dp > 0 Then
  436.         If Len(Mid$(cNum$, dp + 1)) > nDP Then
  437.             d = Val(Mid$(cNum$, dp + nDP + 1, 1))
  438.             If d > 4 Then
  439.                 cNum$ = "0" + cNum$ ' tack on another 0 just in case 9's all the way to left
  440.                 dp = dp + 1
  441.                 i = dp + nDP
  442.                 While Mid$(cNum$, i, 1) = "9" Or Mid$(cNum$, i, 1) = "."
  443.                     If Mid$(cNum$, i, 1) = "9" Then
  444.                         Mid$(cNum$, i, 1) = "0"
  445.                     End If
  446.                     i = i - 1
  447.                 Wend
  448.                 Mid$(cNum$, i, 1) = _Trim$(Str$(Val(Mid$(cNum$, i, 1)) + 1)) 'last non 9 digit
  449.                 cNum$ = Mid$(cNum$, 1, dp + nDP) 'chop it
  450.                 showDP$ = trim0$(cNum$)
  451.             Else
  452.                 showDP$ = Mid$(cNum$, 1, dp + nDP)
  453.             End If
  454.         End If
  455.     End If
  456.  
  457.  
Title: Re: String Math
Post by: jack on June 06, 2021, 02:11:38 am
hi bplus
I don't understand your nInverse function, here's how I would implement the reciprocal
pseudo code
dim x as double
x=1#/val(n$) ' approximation
r$=_trim$(str$(x))  '<< you need to sift-out exponential notation
' Newton-Raphson iteration to improve the reciprocal - it doubles the accuracy per iteration
do
r$=r$*(2-n$*r$)
loop until satisfied '<< (2-n$*r$) approaches 1
or
do
r$=r$+r$*(1-r$*n$)
loop until satisfied '<<r$*(1-r$*n$) approaches 0

if you do r$=r$+r$*(1-r$*n$) then you could check r$*(1-r$*n$) until it's small enough
3 iterations would give over 120 digits accuracy
Title: Re: String Math
Post by: jack on June 06, 2021, 04:52:01 am
your multiply don't work
Code: QB64: [Select]
  1. Print mr$("1.000000000000000000000001000000000000000000000001", "*", "0.000000000000000000000000000000000000000000000001")
  2.  
output
.1000000000000000000000001000000000000000000000001
was trying to test my nInverse but it fails due to the multiply bug
Code: QB64: [Select]
  1. Function nInverse2$ (n$, DP As Integer) 'assume decimal at very start of the string of digits returned, no rounding
  2.     Dim x As Double
  3.     Dim As Integer d, e, k, l
  4.     Dim As String r, r2
  5.     If n$ = "0" Then nInverse2$ = "Div 0": Exit Function
  6.     If n$ = "1" Then nInverse2$ = "1": Exit Function
  7.     x = 1# / Val(n$)
  8.     r = _Trim$(Str$(x))
  9.     d = InStr(r, "D")
  10.     If d > 0 Then
  11.         e = Val(Mid$(r, d + 1))
  12.         r = Left$(r, 1) + Mid$(Left$(r, d - 1), 3)
  13.         l = Len(r)
  14.         If e > 0 Then
  15.             r = r + String$(e - l + 1, "0")
  16.         ElseIf e < 0 Then
  17.             r = "0." + String$(Abs(e) - 1, "0") + r
  18.         End If
  19.     End If
  20.  
  21.     For k = 1 To 7
  22.         r2 = mr$(n$, "*", r)
  23.         r2 = mr$("2", "-", r2)
  24.         r = mr$(r, "*", r2)
  25.     Next k
  26.     nInverse2$ = r
  27.  
Title: Re: String Math
Post by: bplus on June 06, 2021, 01:02:49 pm
Simple fix on multiply again problem with trimming leading 0's.

@jack I like your inverse function and started playing with in attempts to control when it should quit.
It works well finding inverse of STx number and the 115 terms of Fibonacci in that sequence.

I am not satisfied with significant digits because a difference between 816 and 817 was just one more iteration in your code loop yet it went from finding 64 terms in 4.22 secs to all 115 terms in around 13 secs. My inverse with fixed subtr$() was taking over 19 secs and needing about 2785 digits.

So your inverse better. I see you are working on reciprical, interesting!

Code: QB64: [Select]
  1. _Title "Math regulator mr test sqr estimating" ' b+ start 2021-06-02
  2. ' divide$ found fix? 2021-06-03 comment in that function
  3. ' 2021-06-03 update with new sqrRoot$ Function
  4. ' 2021-06-06 fixed divide, subtr and mult, all were trimming lead 0's and messing up decimal settings.
  5. ' 2021-06-06 test jack's nInverse2$() on STx # to find 115 terms of Fibannci.
  6.  
  7. Randomize Timer 'now that it's seems to be running silent
  8. Screen _NewImage(1200, 700, 32)
  9. _Delay .25
  10.  
  11. ' jack error reported 2021-06-04 confirmed!   fixed
  12. 'Print mr$(".00000000000000000000000000000000000000000000200000000000000054307978001764", "-", ".000000000000000000000000000000000000000000002")
  13. 'Print ".00000000000000000000000000000000000000000000000000000000000054307978001764"
  14.  
  15. Dim r$, ruler$
  16. ruler$ = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" + Chr$(10)
  17. ruler$ = ruler$ + "0         1         2         3         4         5         6         7         8         9        10        11        12"
  18. ' debug tests
  19. 'Print mr$("-5", "+", "-2100"), " OK if -2105"
  20. 'Print mr$("." + String$(20, "0") + "1", "+", "1" + String$(40, "0") + "1") ' first real test of add
  21. 'Print ruler$
  22. 'Print Val("." + String$(20, "0") + "1") + Val("1" + String$(40, "0") + "1") ' test print of big and small val
  23. 'Print mr$("-.00071", "+", ".00036" + String$(35, "0") + "9")
  24. '-.00071
  25. ' .00036000000000000000000000000000000000009
  26. 'Print "-.00034999999999999999999999999999999999991"
  27. 'Print ruler$
  28. 'testing a different subtract sub
  29. 'Print mr$("10", "-", "5"), " 5 OK"
  30. 'Print mr$("-10", "+", "5"), " -5 OK"
  31. 'Print mr$("-10", "-", "-5"), " -5 OK"
  32. 'Print mr$("-10", "-", "5"), " -15  OK  added"
  33. 'Print mr$("10", "-", "-5"), " 15  OK  added"
  34. 'Print mr$("-.010", "-", "-5"), "4.99 OK"
  35. 'Print mr$("-.010", "-", "5"), "-5.01  OK just added"
  36. 'Print mr$(".010", "-", "5"), " -4.99 OK"
  37. ' jack error reported 2021-06-04 confirmed!  variation below
  38. 'r$ = mr$(".00000000200000000000000054307978001764", "-", ".0000000020000000000000001") '16 0 wrong  8 wrong
  39. 'Print "   mr$ rtnd:"; r$
  40. 'Print "    compare:.00000000000000000000000044307978001764" ' 2021-06-05 finally!
  41. '                  .0000000020000000000000001
  42. '                  .00000000200000000000000054307978001764
  43.  
  44. 'r$ = mr$(".00000000000000000100000000000000000011", "-", ".000000000000000001000000000000000001") ' bad too
  45. 'Print "   mr$ rtnd:"; r$
  46. '         ".000000000000000001000000000000000001"
  47. 'Print "    compare:-.00000000000000000000000000000000000089"
  48. 'r$ = mr$(".00000000000000000100000000000000000111", "-", ".000000000000000002000000000000000001")
  49. '       ".000000000000000002000000000000000001"
  50. '       ".00000000000000000100000000000000000111"
  51. '       ".00000000000000000099999999999999999989"
  52. 'Print "   mr$ rtnd:"; r$
  53. 'Print "    compare:-.00000000000000000099999999999999999989"
  54.  
  55. 'r$ = mr$(".00000000000000000000000000999", "-", "1")
  56. '-1.00000000000000000000000000000000000000000
  57. '  .00000000000000000000000000999
  58. ' -.99999999999999999999999999001
  59. 'Print "   mr$ rtnd:"; r$
  60. 'Print "    compare:-.99999999999999999999999999001"
  61.  
  62. 'r$ = mr$("1", "+", "-1000000000000000000000000000000000000000")
  63. 'Print "   mr$ rtnd:"; r$
  64. '1000000000000000000000000000000000000000
  65. '-999999999999999999999999999999999999999
  66. 'Print "    compare:-999999999999999999999999999999999999999"
  67.  
  68. ' check jack problems with FB translation  2021-06-03  errors must be in FB trans from QB64
  69. 'Print Mid$(mr$(" .1", "/", "3"), 1, 100) ' too long?
  70. 'Print Mid$(mr$("1.1", "/", "9"), 1, 100)
  71. 'Print Mid$(mr$("1.38", "/", "1.2"), 1, 100)
  72. 'Print ruler$
  73.  
  74. ' another error reported by jack 2021-06-06 fixed (same problem as subtr$)
  75. Print mr$("1.000000000000000000000001000000000000000000000001", "*", ".000000000000000000000000000000000000000000000001")
  76. Print ".000000000000000000000000000000000000000000000001"
  77. Print "                                                1000000000000000000000001000000000000000000000001"
  78. Print ruler$
  79. 'Print "checking inverse of 50 = .02 "; nInverse$("50", 20) 'OK  .020000... length 20
  80. 'Print ruler$
  81. 'Print
  82. 'Print "zzz... see inverse of STx number now takes close to 30 secs with fixed subtr$() sub,"
  83. 'Print "Use to come up in a sneeze! It also looks different way more space on end but still"
  84. 'Print "can find 115 Fibonacci terms in it."
  85. 'Print "    The only difference is I am not trimming leading 0's in subst$() function!"
  86. 'Sleep
  87. 'Cls
  88. Dim inverseSTx$, start, done
  89. start = Timer(.001)
  90. 'inverseSTx$ = nInverse$("999999999999999999999998999999999999999999999999", 2785) ' wow big delay! 19.52 secs +-
  91. inverseSTx$ = nInverse2$("999999999999999999999998999999999999999999999999", 817)
  92. ' 816 in 4.22 secs only 64 terms   817 sigDigits matching in 12.93 secs gets all 115 Fibonacci Terms
  93. done = Timer(.001) - start
  94. Print Mid$(inverseSTx$, 1, 3000)
  95. Dim As Long startSearch, termN, find
  96. Dim f1$, f2$, searchFor$
  97. f1$ = "1"
  98. f2$ = "1"
  99. startSearch = 1
  100. termN = 2
  101.     searchFor$ = mr$(f1$, "+", f2$)
  102.     find = InStr(startSearch, inverseSTx$, searchFor$)
  103.     If find Then
  104.         termN = termN + 1
  105.         Print "Term Number"; termN; " = "; searchFor$; " found at"; find
  106.         f1$ = f2$
  107.         f2$ = searchFor$
  108.         startSearch = find + Len(searchFor$)
  109.     Else
  110.         Print searchFor$; " not found."
  111.         Exit Do
  112.     End If
  113. Print "Inverse time:"; done
  114.  
  115.  
  116. 'Dim n$, result$
  117. 'Do
  118. '    'remember everything is strings
  119. '    Input "Enter a number to find it's square root "; n$
  120. '    If n$ = "" Then End
  121. '    result$ = sqrRoot$(n$)
  122. '    Print result$
  123. '    Print "Length ="; Len(result$)
  124. '    _Delay 2
  125. '    result$ = SRbyNR$(n$)
  126. '    Print result$
  127. '    Print "Length ="; Len(result$)
  128. '    Print
  129. 'Loop
  130.  
  131. Function sqrRoot$ (nmbr$)
  132.     Dim n$, guess$, lastGuess$, other$, sum$, imaginary$, loopcnt
  133.     If Left$(nmbr$, 1) = "-" Then 'handle neg numbers
  134.         imaginary$ = "*i": n$ = Mid$(nmbr$, 2)
  135.     Else
  136.         imaginary$ = "": n$ = nmbr$
  137.     End If
  138.     guess$ = mr$(n$, "/", "2")
  139.     other$ = n$
  140.     Do
  141.         loopcnt = loopcnt + 1
  142.         Print "loop cnt"; loopcnt
  143.  
  144.         If (Mid$(guess$, 1, 105) = Mid$(lastGuess$, 1, 105)) Then ' go past 100 matching digits for 100 digit precision
  145.             sqrRoot$ = Mid$(other$, 1, 101) + imaginary$ ' try other factor for guess$  sometimes it nails answer without all digits
  146.             Exit Function
  147.         Else
  148.             lastGuess$ = guess$
  149.             sum$ = mr$(guess$, "+", other$)
  150.             guess$ = mr$(sum$, "/", "2")
  151.             other$ = mr$(n$, "/", guess$)
  152.         End If
  153.     Loop
  154.  
  155. Function SRbyNR$ (nmbr$) ' square root by Newton - Ralphson method my interpretation of GeorgeMcGinn
  156.  
  157.     Dim n$, guess$, lastGuess$, dx$, imaginary$, other$, loopcnt
  158.     If Left$(nmbr$, 1) = "-" Then 'handle neg numbers
  159.         imaginary$ = "*i": n$ = Mid$(nmbr$, 2)
  160.     Else
  161.         imaginary$ = "": n$ = nmbr$
  162.     End If
  163.  
  164.     guess$ = mr$(n$, "/", "2") ' get this going first and then try better starting guess later
  165.  
  166.     Do
  167.         loopcnt = loopcnt + 1
  168.         Print "loop cnt"; loopcnt
  169.         If (Mid$(guess$, 1, 105) = Mid$(lastGuess$, 1, 105)) Then ' go past 100 matching digits for 100 digit precision
  170.             SRbyNR$ = Mid$(other$, 1, 101) + imaginary$
  171.             Exit Function
  172.         Else
  173.             'dx = (x - A / x) / 2: x = x - dx ' Thanks George
  174.             lastGuess$ = guess$
  175.             dx$ = mr$(mr$(guess$, "-", mr$(n$, "/", guess$)), "/", "2")
  176.             guess$ = mr$(guess$, "-", dx$)
  177.             other$ = mr$(n$, "/", guess$) ' try other factor for guess$  sometimes it nails answer without all digits
  178.         End If
  179.     Loop
  180.  
  181. 'jacks nInverse2$ modified from using 7 interations to ending when a certain amount of significant digits match between r's
  182. Function nInverse2$ (n$, SigDigits As Long) 'assume decimal at very start of the string of digits returned, no rounding
  183.     Dim x As Double
  184.     Dim As Integer d, e, k, l
  185.     Dim As String r, r2, lastR
  186.     If n$ = "0" Then nInverse2$ = "Div 0": Exit Function
  187.     If n$ = "1" Then nInverse2$ = "1": Exit Function
  188.     x = 1# / Val(n$)
  189.     r = _Trim$(Str$(x))
  190.     d = InStr(r, "D")
  191.     If d > 0 Then
  192.         e = Val(Mid$(r, d + 1))
  193.         r = Left$(r, 1) + Mid$(Left$(r, d - 1), 3)
  194.         l = Len(r)
  195.         If e > 0 Then
  196.             r = r + String$(e - l + 1, "0")
  197.         ElseIf e < 0 Then
  198.             r = "0." + String$(Abs(e) - 1, "0") + r
  199.         End If
  200.     End If
  201.     While Mid$(lastR, 1, SigDigits) <> Mid$(r, 1, SigDigits) ' use to be 7 interations
  202.         lastR = r
  203.         r2 = mr$(n$, "*", r)
  204.         r2 = mr$("2", "-", r2)
  205.         r = mr$(r, "*", r2)
  206.     Wend
  207.     nInverse2$ = r
  208.  
  209. Function mr$ (a$, op$, b$) ' catchy? mr$ for math regulator
  210.     Dim ca$, cb$, aSgn$, bSgn$, postOp$, sgn$
  211.     Dim adp As Integer, bdp As Integer, dp As Integer, lpop As Integer
  212.  
  213.     op$ = _Trim$(op$) 'save fixing each time
  214.     ca$ = _Trim$(a$): cb$ = _Trim$(b$) 'make copies in case we change
  215.     'strip signs and decimals
  216.     If Left$(ca$, 1) = "-" Then
  217.         aSgn$ = "-": ca$ = Mid$(ca$, 2)
  218.     Else
  219.         aSgn$ = "": ca$ = ca$
  220.     End If
  221.     dp = InStr(ca$, ".")
  222.     If dp > 0 Then
  223.         adp = Len(ca$) - dp
  224.         ca$ = Mid$(ca$, 1, dp - 1) + Mid$(ca$, dp + 1)
  225.     Else
  226.         adp = 0
  227.     End If
  228.     If Left$(cb$, 1) = "-" Then
  229.         bSgn$ = "-": cb$ = Mid$(cb$, 2)
  230.     Else
  231.         bSgn$ = "": cb$ = cb$
  232.     End If
  233.     dp = InStr(cb$, ".")
  234.     If dp > 0 Then
  235.         bdp = Len(cb$) - dp
  236.         cb$ = Mid$(cb$, 1, dp - 1) + Mid$(cb$, dp + 1)
  237.     Else
  238.         bdp = 0
  239.     End If
  240.  
  241.     If op$ = "+" Or op$ = "-" Or op$ = "/" Then 'add or subtr  even up strings on right of decimal
  242.         'even up the right sides of decimals if any
  243.         If adp > bdp Then dp = adp Else dp = bdp
  244.         If adp < dp Then ca$ = ca$ + String$(dp - adp, "0")
  245.         If bdp < dp Then cb$ = cb$ + String$(dp - bdp, "0")
  246.     ElseIf op$ = "*" Then
  247.         dp = adp + bdp
  248.     End If
  249.     If op$ = "*" Or op$ = "/" Then
  250.         If aSgn$ = bSgn$ Then sgn$ = "" Else sgn$ = "-"
  251.     End If
  252.     'now according to signs and op$ call add$ or subtr$
  253.     If op$ = "+" Then
  254.         If aSgn$ = bSgn$ Then 'really add
  255.             postOp$ = aSgn$ + add$(ca$, cb$)
  256.         Else 'have a case of subtraction
  257.             If aSgn$ = "-" Then postOp$ = subtr$(cb$, ca$) Else postOp$ = subtr$(ca$, cb$)
  258.         End If
  259.     ElseIf op$ = "-" Then
  260.         If bSgn$ = "-" Then 'really add but switch b sign
  261.             bSgn$ = ""
  262.             If aSgn$ = "-" Then
  263.                 postOp$ = subtr$(cb$, ca$)
  264.             Else 'aSgn = ""
  265.                 postOp$ = add$(ca$, cb$)
  266.             End If
  267.         Else 'bSgn$ =""
  268.             If aSgn$ = "-" Then
  269.                 bSgn$ = "-"
  270.                 postOp$ = aSgn$ + add$(ca$, cb$)
  271.             Else
  272.                 postOp$ = subtr$(ca$, cb$)
  273.             End If
  274.         End If
  275.     ElseIf op$ = "*" Then
  276.         postOp$ = sgn$ + mult$(ca$, cb$)
  277.     ElseIf op$ = "/" Then
  278.         postOp$ = sgn$ + divide$(ca$, cb$)
  279.     End If ' which op
  280.     'put dp back
  281.     If op$ <> "/" Then
  282.         lpop = Len(postOp$) ' put decimal back
  283.         postOp$ = Mid$(postOp$, 1, lpop - dp) + "." + Mid$(postOp$, lpop - dp + 1)
  284.     End If
  285.     mr$ = trim0$(postOp$)
  286.  
  287. Function divide$ (n$, d$) ' goal here is 100 digits precision not 100 digits past decimal
  288.     Dim di$, ndi$, nD As Integer
  289.     If _Trim$(n$) = "0" Then divide$ = "0": Exit Function
  290.     If _Trim$(d$) = "0" Then divide$ = "div 0": Exit Function
  291.     If _Trim$(d$) = "1" Then divide$ = _Trim$(n$): Exit Function
  292.  
  293.     ' aha! found a bug when d$ gets really huge 100 is no where near enough!!!!
  294.     ' 2021-06-03 fix by adding 100 to len(d$), plus have to go a little past 100 for 100 digit precision
  295.     ' need to go past 100 for 100 precise digits (not decimal places)
  296.     di$ = Mid$(nInverse$(d$, Len(d$) + 200), 2) 'chop off decimal point after
  297.     nD = Len(di$)
  298.     ndi$ = mult$(n$, di$)
  299.     ndi$ = Mid$(ndi$, 1, Len(ndi$) - nD) + "." + Right$(String$(nD, "0") + Right$(ndi$, nD), nD)
  300.     divide$ = trim0$(ndi$)
  301.  
  302. Function nInverse$ (n$, DP As Integer) 'assume decimal at very start of the string of digits returned, no rounding
  303.     Dim m$(1 To 9), si$, r$, outstr$, d$
  304.     Dim i As Integer
  305.     For i = 1 To 9
  306.         si$ = _Trim$(Str$(i))
  307.         m$(i) = mult$(si$, n$)
  308.     Next
  309.     outstr$ = ""
  310.     If n$ = "0" Then nInverse$ = "Div 0": Exit Function
  311.     If n$ = "1" Then nInverse$ = "1": Exit Function
  312.     outstr$ = "." 'everything else n > 1 is decimal 8/17
  313.     r$ = "10"
  314.     Do
  315.         While Left$(subtr$(r$, n$), 1) = "-" '   r - n < 0
  316.             outstr$ = outstr$ + "0" '            add 0 to the  output string
  317.             If Len(outstr$) = DP Then nInverse$ = outstr$: Exit Function 'check if we've reached DP length
  318.             r$ = r$ + "0"
  319.         Wend
  320.         For i = 9 To 1 Step -1
  321.             If LTE(m$(i), r$) Then d$ = _Trim$(Str$(i)): Exit For
  322.         Next
  323.         outstr$ = outstr$ + d$
  324.         If Len(outstr$) = DP Then nInverse$ = outstr$: Exit Function
  325.         r$ = subtr$(r$, mult$(d$, n$)) 'r = r -d*n
  326.         If r$ = "0" Then nInverse$ = outstr$: Exit Function 'found a perfect divisor
  327.         r$ = r$ + "0" 'add another place
  328.     Loop
  329.  
  330. Function mult$ (a$, b$) 'assume both positive integers prechecked as all digits strings
  331.     Dim la As Integer, lb As Integer, m As Integer, g As Integer, dp As Integer
  332.     Dim f18$, f1$, t$, build$, accum$
  333.     ' fixed mult$ don't trim lead 0's 2021-06-06
  334.     If _Trim$(a$) = "0" Then mult$ = "0": Exit Function
  335.     If _Trim$(b$) = "0" Then mult$ = "0": Exit Function
  336.     If _Trim$(a$) = "1" Then mult$ = _Trim$(b$): Exit Function
  337.     If _Trim$(b$) = "1" Then mult$ = _Trim$(a$): Exit Function
  338.     'find the longer number and make it a mult of 18 to take 18 digits at a time from it
  339.     la = Len(a$): lb = Len(b$)
  340.     If la > lb Then
  341.         m = Int(la / 18) + 1
  342.         f18$ = Right$(String$(m * 18, "0") + a$, m * 18)
  343.         f1$ = b$
  344.     Else
  345.         m = Int(lb / 18) + 1
  346.         f18$ = Right$(String$(m * 18, "0") + b$, m * 18)
  347.         f1$ = a$
  348.     End If
  349.     For dp = Len(f1$) To 1 Step -1 'dp = digit position of the f1$
  350.         build$ = "" 'line builder
  351.         co = 0
  352.         'now taking 18 digits at a time Thanks Steve McNeill
  353.         For g = 1 To m
  354.             v18 = Val(Mid$(f18$, m * 18 - g * 18 + 1, 18))
  355.             sd = Val(Mid$(f1$, dp, 1))
  356.             t$ = Right$(String$(19, "0") + _Trim$(Str$(v18 * sd + co)), 19)
  357.             co = Val(Mid$(t$, 1, 1))
  358.             build$ = Mid$(t$, 2) + build$
  359.         Next g
  360.         If co Then build$ = _Trim$(Str$(co)) + build$
  361.         If dp = Len(f1$) Then
  362.             accum$ = build$
  363.         Else
  364.             accum$ = add$(accum$, build$ + String$(Len(f1$) - dp, "0"))
  365.         End If
  366.     Next dp
  367.     mult$ = accum$
  368.  
  369. Function subtr$ (sum$, minus$) ' assume both numbers are positive all digits
  370.     Dim m As Integer, g As Integer, p As Integer
  371.     Dim ts$, tm$, sign$, LG$, sm$, t$, result$
  372.  
  373.     'ts$ = TrimLead0$(sum$): tm$ = TrimLead0$(minus$)   'orig with decent square root speed
  374.     ts$ = _Trim$(sum$): tm$ = _Trim$(minus$) ' fixed subtr$ 2021-06-05
  375.     If trim0(ts$) = trim0$(tm$) Then subtr$ = "0": Exit Function 'OK proceed with function knowing they are not equal
  376.     tenE18 = 1000000000000000000 'yes!!! no dang E's
  377.     If LTE(ts$, tm$) Then ' which is bigger? minus is bigger
  378.         sign$ = "-"
  379.         m = Int(Len(tm$) / 18) + 1
  380.         LG$ = Right$(String$(m * 18, "0") + tm$, m * 18)
  381.         sm$ = Right$(String$(m * 18, "0") + ts$, m * 18)
  382.     Else 'sum is bigger
  383.         sign$ = ""
  384.         m = Int(Len(ts$) / 18) + 1
  385.         LG$ = Right$(String$(m * 18, "0") + ts$, m * 18)
  386.         sm$ = Right$(String$(m * 18, "0") + tm$, m * 18)
  387.     End If
  388.     'now taking 18 digits at a time From Steve I learned we can do more than 1 digit at a time
  389.     For g = 1 To m
  390.         VB = Val(Mid$(LG$, m * 18 - g * 18 + 1, 18))
  391.         vs = Val(Mid$(sm$, m * 18 - g * 18 + 1, 18))
  392.         If vs > VB Then
  393.             t$ = Right$(String$(18, "0") + _Trim$(Str$(tenE18 - vs + VB)), 18)
  394.             p = (m - g) * 18
  395.             While p > 0 And Mid$(LG$, p, 1) = "0"
  396.                 Mid$(LG$, p, 1) = "9"
  397.                 p = p - 1
  398.             Wend
  399.             If p > 0 Then Mid$(LG$, p, 1) = _Trim$(Str$(Val(Mid$(LG$, p, 1)) - 1))
  400.         Else
  401.             t$ = Right$(String$(18, "0") + _Trim$(Str$(VB - vs)), 18)
  402.         End If
  403.         result$ = t$ + result$
  404.     Next
  405.     subtr$ = sign$ + result$
  406.  
  407. Function add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no spaces or - signs
  408.     'first thing is to set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
  409.     Dim la As Integer, lb As Integer, m As Integer, g As Integer
  410.     Dim fa$, fb$, t$, new$, result$
  411.     la = Len(a$): lb = Len(b$)
  412.     If la > lb Then m = Int(la / 18) + 1 Else m = Int(lb / 18) + 1
  413.     fa$ = Right$(String$(m * 18, "0") + a$, m * 18)
  414.     fb$ = Right$(String$(m * 18, "0") + b$, m * 18)
  415.  
  416.     'now taking 18 digits at a time Thanks Steve McNeill
  417.     For g = 1 To m
  418.         sa = Val(Mid$(fa$, m * 18 - g * 18 + 1, 18))
  419.         sb = Val(Mid$(fb$, m * 18 - g * 18 + 1, 18))
  420.         t$ = Right$(String$(36, "0") + _Trim$(Str$(sa + sb + co)), 36)
  421.         co = Val(Mid$(t$, 1, 18))
  422.         new$ = Mid$(t$, 19)
  423.         result$ = new$ + result$
  424.     Next
  425.     If co Then result$ = Str$(co) + result$
  426.     add$ = result$
  427.  
  428. ' String Math Helpers -----------------------------------------------
  429.  
  430. 'this function needs TrimLead0$(s$) a and b have decimal removed and a and b lengths aligned on right
  431. Function LTE (a$, b$) ' a$ Less Than or Equal b$  comparison of 2 strings
  432.     Dim ca$, cb$, la As Integer, lb As Integer, i As Integer
  433.     ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
  434.     la = Len(ca$): lb = Len(cb$)
  435.     If ca$ = cb$ Then
  436.         LTE = -1
  437.     ElseIf la < lb Then ' a is smaller
  438.         LTE = -1
  439.     ElseIf la > lb Then ' a is bigger
  440.         LTE = 0
  441.     ElseIf la = lb Then ' equal lengths
  442.         For i = 1 To Len(ca$)
  443.             If Val(Mid$(ca$, i, 1)) > Val(Mid$(cb$, i, 1)) Then
  444.                 LTE = 0: Exit Function
  445.             ElseIf Val(Mid$(ca$, i, 1)) < Val(Mid$(cb$, i, 1)) Then
  446.                 LTE = -1: Exit Function
  447.             End If
  448.         Next
  449.     End If
  450.  
  451. ' ------------------------------------- use these for final display
  452.  
  453. Function TrimLead0$ (s$) 'for treating strings as number (pos integers)
  454.     Dim copys$, i As Integer, find As Integer
  455.     copys$ = _Trim$(s$) 'might as well remove spaces too
  456.     i = 1: find = 0
  457.     While i < Len(copys$) And Mid$(copys$, i, 1) = "0"
  458.         i = i + 1: find = 1
  459.     Wend
  460.     If find = 1 Then copys$ = Mid$(copys$, i)
  461.     If copys$ = "" Then TrimLead0$ = "0" Else TrimLead0$ = copys$
  462.  
  463. Function TrimTail0$ (s$)
  464.     Dim copys$, dp As Integer, i As Integer, find As Integer
  465.     copys$ = _Trim$(s$) 'might as well remove spaces too
  466.     TrimTail0$ = copys$
  467.     dp = InStr(copys$, ".")
  468.     If dp > 0 Then
  469.         i = Len(copys$): find = 0
  470.         While i > dp And Mid$(copys$, i, 1) = "0"
  471.             i = i - 1: find = 1
  472.         Wend
  473.         If find = 1 Then
  474.             If i = dp Then
  475.                 TrimTail0$ = Mid$(copys$, 1, dp - 1)
  476.             Else
  477.                 TrimTail0$ = Mid$(copys$, 1, i)
  478.             End If
  479.         End If
  480.     End If
  481.  
  482. Function trim0$ (s$)
  483.     Dim cs$, si$
  484.     cs$ = s$
  485.     si$ = Left$(cs$, 1)
  486.     If si$ = "-" Then cs$ = Mid$(cs$, 2)
  487.     cs$ = TrimLead0$(cs$)
  488.     cs$ = TrimTail0$(cs$)
  489.     If Right$(cs$, 1) = "." Then cs$ = Mid$(cs$, 1, Len(cs$) - 1)
  490.     If si$ = "-" Then trim0$ = si$ + cs$ Else trim0$ = cs$
  491.  
  492. ' for displaying truncated numbers say to 60 digits
  493. Function showDP$ (num$, nDP As Integer)
  494.     Dim cNum$, dp As Integer, d As Integer, i As Integer
  495.     cNum$ = num$ 'since num$ could get changed
  496.     showDP$ = num$
  497.     dp = InStr(num$, ".")
  498.     If dp > 0 Then
  499.         If Len(Mid$(cNum$, dp + 1)) > nDP Then
  500.             d = Val(Mid$(cNum$, dp + nDP + 1, 1))
  501.             If d > 4 Then
  502.                 cNum$ = "0" + cNum$ ' tack on another 0 just in case 9's all the way to left
  503.                 dp = dp + 1
  504.                 i = dp + nDP
  505.                 While Mid$(cNum$, i, 1) = "9" Or Mid$(cNum$, i, 1) = "."
  506.                     If Mid$(cNum$, i, 1) = "9" Then
  507.                         Mid$(cNum$, i, 1) = "0"
  508.                     End If
  509.                     i = i - 1
  510.                 Wend
  511.                 Mid$(cNum$, i, 1) = _Trim$(Str$(Val(Mid$(cNum$, i, 1)) + 1)) 'last non 9 digit
  512.                 cNum$ = Mid$(cNum$, 1, dp + nDP) 'chop it
  513.                 showDP$ = trim0$(cNum$)
  514.             Else
  515.                 showDP$ = Mid$(cNum$, 1, dp + nDP)
  516.             End If
  517.         End If
  518.     End If
  519.  
  520.  
  521.  
Title: Re: String Math
Post by: jack on June 06, 2021, 01:45:34 pm
bplus
if you would set all your routines to a limit of say 128 digits - integer + fractional, then your operations would be relatively fast, nInverse would require only 3 iterations to give 128 digits of accuracy - you could dispense the loop and loop-test
as it is, your strings grow huge and the routines become very slow, that's why in my sqrt$ routine I would keep trimming the resulting strings
< edit > or you could go to 256 digits, nInverse would only require 4 iterations
Title: Re: String Math
Post by: jack on June 06, 2021, 05:37:13 pm
here's a different way to print the Fibonacci numbers
Code: QB64: [Select]
  1. _Title "Math regulator mr test sqr estimating" ' b+ start 2021-06-02
  2. ' divide$ found fix? 2021-06-03 comment in that function
  3. ' 2021-06-03 update with new sqrRoot$ Function
  4. ' 2021-06-06 fixed divide, subtr and mult, all were trimming lead 0's and messing up decimal settings.
  5. ' 2021-06-06 test jack's nInverse2$() on STx # to find 115 terms of Fibannci.
  6.  
  7. Randomize Timer 'now that it's seems to be running silent
  8. Screen _NewImage(1200, 1000, 32)
  9. _Delay .25
  10.  
  11. ' jack error reported 2021-06-04 confirmed!   fixed
  12. 'Print mr$(".00000000000000000000000000000000000000000000200000000000000054307978001764", "-", ".000000000000000000000000000000000000000000002")
  13. 'Print ".00000000000000000000000000000000000000000000000000000000000054307978001764"
  14.  
  15. Dim r$, ruler$
  16. ruler$ = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" + Chr$(10)
  17. ruler$ = ruler$ + "0         1         2         3         4         5         6         7         8         9        10        11        12"
  18. ' debug tests
  19. 'Print mr$("-5", "+", "-2100"), " OK if -2105"
  20. 'Print mr$("." + String$(20, "0") + "1", "+", "1" + String$(40, "0") + "1") ' first real test of add
  21. 'Print ruler$
  22. 'Print Val("." + String$(20, "0") + "1") + Val("1" + String$(40, "0") + "1") ' test print of big and small val
  23. 'Print mr$("-.00071", "+", ".00036" + String$(35, "0") + "9")
  24. '-.00071
  25. ' .00036000000000000000000000000000000000009
  26. 'Print "-.00034999999999999999999999999999999999991"
  27. 'Print ruler$
  28. 'testing a different subtract sub
  29. 'Print mr$("10", "-", "5"), " 5 OK"
  30. 'Print mr$("-10", "+", "5"), " -5 OK"
  31. 'Print mr$("-10", "-", "-5"), " -5 OK"
  32. 'Print mr$("-10", "-", "5"), " -15  OK  added"
  33. 'Print mr$("10", "-", "-5"), " 15  OK  added"
  34. 'Print mr$("-.010", "-", "-5"), "4.99 OK"
  35. 'Print mr$("-.010", "-", "5"), "-5.01  OK just added"
  36. 'Print mr$(".010", "-", "5"), " -4.99 OK"
  37. ' jack error reported 2021-06-04 confirmed!  variation below
  38. 'r$ = mr$(".00000000200000000000000054307978001764", "-", ".0000000020000000000000001") '16 0 wrong  8 wrong
  39. 'Print "   mr$ rtnd:"; r$
  40. 'Print "    compare:.00000000000000000000000044307978001764" ' 2021-06-05 finally!
  41. '                  .0000000020000000000000001
  42. '                  .00000000200000000000000054307978001764
  43.  
  44. 'r$ = mr$(".00000000000000000100000000000000000011", "-", ".000000000000000001000000000000000001") ' bad too
  45. 'Print "   mr$ rtnd:"; r$
  46. '         ".000000000000000001000000000000000001"
  47. 'Print "    compare:-.00000000000000000000000000000000000089"
  48. 'r$ = mr$(".00000000000000000100000000000000000111", "-", ".000000000000000002000000000000000001")
  49. '       ".000000000000000002000000000000000001"
  50. '       ".00000000000000000100000000000000000111"
  51. '       ".00000000000000000099999999999999999989"
  52. 'Print "   mr$ rtnd:"; r$
  53. 'Print "    compare:-.00000000000000000099999999999999999989"
  54.  
  55. 'r$ = mr$(".00000000000000000000000000999", "-", "1")
  56. '-1.00000000000000000000000000000000000000000
  57. '  .00000000000000000000000000999
  58. ' -.99999999999999999999999999001
  59. 'Print "   mr$ rtnd:"; r$
  60. 'Print "    compare:-.99999999999999999999999999001"
  61.  
  62. 'r$ = mr$("1", "+", "-1000000000000000000000000000000000000000")
  63. 'Print "   mr$ rtnd:"; r$
  64. '1000000000000000000000000000000000000000
  65. '-999999999999999999999999999999999999999
  66. 'Print "    compare:-999999999999999999999999999999999999999"
  67.  
  68. ' check jack problems with FB translation  2021-06-03  errors must be in FB trans from QB64
  69. 'Print Mid$(mr$(" .1", "/", "3"), 1, 100) ' too long?
  70. 'Print Mid$(mr$("1.1", "/", "9"), 1, 100)
  71. 'Print Mid$(mr$("1.38", "/", "1.2"), 1, 100)
  72. 'Print ruler$
  73.  
  74. ' another error reported by jack 2021-06-06 fixed (same problem as subtr$)
  75. Print mr$("1.000000000000000000000001000000000000000000000001", "*", ".000000000000000000000000000000000000000000000001")
  76. Print ".000000000000000000000000000000000000000000000001"
  77. Print "                                                1000000000000000000000001000000000000000000000001"
  78. Print ruler$
  79. 'Print "checking inverse of 50 = .02 "; nInverse$("50", 20) 'OK  .020000... length 20
  80. 'Print ruler$
  81. 'Print
  82. 'Print "zzz... see inverse of STx number now takes close to 30 secs with fixed subtr$() sub,"
  83. 'Print "Use to come up in a sneeze! It also looks different way more space on end but still"
  84. 'Print "can find 115 Fibonacci terms in it."
  85. 'Print "    The only difference is I am not trimming leading 0's in subst$() function!"
  86. 'Sleep
  87. 'Cls
  88. Dim inverseSTx$, start, done
  89. start = Timer(.001)
  90. 'inverseSTx$ = nInverse$("999999999999999999999998999999999999999999999999", 2785) ' wow big delay! 19.52 secs +-
  91. inverseSTx$ = nInverse2$("999999999999999999999998999999999999999999999999", 817)
  92. ' 816 in 4.22 secs only 64 terms   817 sigDigits matching in 12.93 secs gets all 115 Fibonacci Terms
  93. done = Timer(.001) - start
  94.  
  95. Print Mid$(inverseSTx$, 1, 3000)
  96. Dim As Long startSearch, termN, find
  97. Dim f1$, f2$, searchFor$
  98. Dim As Long i, j
  99. Print "inverseSTx$ length = "; Len(inverseSTx$)
  100. j = InStr(inverseSTx$, ".")
  101. inverseSTx$ = Mid$(inverseSTx$, j + 1)
  102. j = 1
  103. For i = 1 To 58
  104.     Print Mid$(inverseSTx$, j, 24), Mid$(inverseSTx$, j + 24, 24)
  105.     j = j + 48
  106.  
  107. 'f1$ = "1"
  108. 'f2$ = "1"
  109. 'startSearch = 1
  110. 'termN = 2
  111. 'Do
  112. '    searchFor$ = mr$(f1$, "+", f2$)
  113. '    find = InStr(startSearch, inverseSTx$, searchFor$)
  114. '    If find Then
  115. '        termN = termN + 1
  116. '        Print "Term Number"; termN; " = "; searchFor$; " found at"; find
  117. '        f1$ = f2$
  118. '        f2$ = searchFor$
  119. '        startSearch = find + Len(searchFor$)
  120. '    Else
  121. '        Print searchFor$; " not found."
  122. '        Exit Do
  123. '    End If
  124. 'Loop
  125. 'Print "Inverse time:"; done
  126.  
  127.  
  128. 'Dim n$, result$
  129. 'Do
  130. '    'remember everything is strings
  131. '    Input "Enter a number to find it's square root "; n$
  132. '    If n$ = "" Then End
  133. '    result$ = sqrRoot$(n$)
  134. '    Print result$
  135. '    Print "Length ="; Len(result$)
  136. '    _Delay 2
  137. '    result$ = SRbyNR$(n$)
  138. '    Print result$
  139. '    Print "Length ="; Len(result$)
  140. '    Print
  141. 'Loop
  142.  
  143. Function sqrRoot$ (nmbr$)
  144.     Dim n$, guess$, lastGuess$, other$, sum$, imaginary$, loopcnt
  145.     If Left$(nmbr$, 1) = "-" Then 'handle neg numbers
  146.         imaginary$ = "*i": n$ = Mid$(nmbr$, 2)
  147.     Else
  148.         imaginary$ = "": n$ = nmbr$
  149.     End If
  150.     guess$ = mr$(n$, "/", "2")
  151.     other$ = n$
  152.     Do
  153.         loopcnt = loopcnt + 1
  154.         Print "loop cnt"; loopcnt
  155.  
  156.         If (Mid$(guess$, 1, 105) = Mid$(lastGuess$, 1, 105)) Then ' go past 100 matching digits for 100 digit precision
  157.             sqrRoot$ = Mid$(other$, 1, 101) + imaginary$ ' try other factor for guess$  sometimes it nails answer without all digits
  158.             Exit Function
  159.         Else
  160.             lastGuess$ = guess$
  161.             sum$ = mr$(guess$, "+", other$)
  162.             guess$ = mr$(sum$, "/", "2")
  163.             other$ = mr$(n$, "/", guess$)
  164.         End If
  165.     Loop
  166.  
  167. Function SRbyNR$ (nmbr$) ' square root by Newton - Ralphson method my interpretation of GeorgeMcGinn
  168.  
  169.     Dim n$, guess$, lastGuess$, dx$, imaginary$, other$, loopcnt
  170.     If Left$(nmbr$, 1) = "-" Then 'handle neg numbers
  171.         imaginary$ = "*i": n$ = Mid$(nmbr$, 2)
  172.     Else
  173.         imaginary$ = "": n$ = nmbr$
  174.     End If
  175.  
  176.     guess$ = mr$(n$, "/", "2") ' get this going first and then try better starting guess later
  177.  
  178.     Do
  179.         loopcnt = loopcnt + 1
  180.         Print "loop cnt"; loopcnt
  181.         If (Mid$(guess$, 1, 105) = Mid$(lastGuess$, 1, 105)) Then ' go past 100 matching digits for 100 digit precision
  182.             SRbyNR$ = Mid$(other$, 1, 101) + imaginary$
  183.             Exit Function
  184.         Else
  185.             'dx = (x - A / x) / 2: x = x - dx ' Thanks George
  186.             lastGuess$ = guess$
  187.             dx$ = mr$(mr$(guess$, "-", mr$(n$, "/", guess$)), "/", "2")
  188.             guess$ = mr$(guess$, "-", dx$)
  189.             other$ = mr$(n$, "/", guess$) ' try other factor for guess$  sometimes it nails answer without all digits
  190.         End If
  191.     Loop
  192.  
  193. 'jacks nInverse2$ modified from using 7 interations to ending when a certain amount of significant digits match between r's
  194. Function nInverse2$ (n$, SigDigits As Long) 'assume decimal at very start of the string of digits returned, no rounding
  195.     Dim x As Double
  196.     Dim As Integer d, e, k, l
  197.     Dim As String r, r2, lastR
  198.     If n$ = "0" Then nInverse2$ = "Div 0": Exit Function
  199.     If n$ = "1" Then nInverse2$ = "1": Exit Function
  200.     x = 1# / Val(n$)
  201.     r = _Trim$(Str$(x))
  202.     d = InStr(r, "D")
  203.     If d > 0 Then
  204.         e = Val(Mid$(r, d + 1))
  205.         r = Left$(r, 1) + Mid$(Left$(r, d - 1), 3)
  206.         l = Len(r)
  207.         If e > 0 Then
  208.             r = r + String$(e - l + 1, "0")
  209.         ElseIf e < 0 Then
  210.             r = "0." + String$(Abs(e) - 1, "0") + r
  211.         End If
  212.     End If
  213.     While Mid$(lastR, 1, SigDigits) <> Mid$(r, 1, SigDigits) ' use to be 7 interations
  214.         lastR = r
  215.         r2 = mr$(n$, "*", r)
  216.         r2 = mr$("2", "-", r2)
  217.         r = mr$(r, "*", r2)
  218.     Wend
  219.     nInverse2$ = r
  220.  
  221. Function mr$ (a$, op$, b$) ' catchy? mr$ for math regulator
  222.     Dim ca$, cb$, aSgn$, bSgn$, postOp$, sgn$
  223.     Dim adp As Integer, bdp As Integer, dp As Integer, lpop As Integer
  224.  
  225.     op$ = _Trim$(op$) 'save fixing each time
  226.     ca$ = _Trim$(a$): cb$ = _Trim$(b$) 'make copies in case we change
  227.     'strip signs and decimals
  228.     If Left$(ca$, 1) = "-" Then
  229.         aSgn$ = "-": ca$ = Mid$(ca$, 2)
  230.     Else
  231.         aSgn$ = "": ca$ = ca$
  232.     End If
  233.     dp = InStr(ca$, ".")
  234.     If dp > 0 Then
  235.         adp = Len(ca$) - dp
  236.         ca$ = Mid$(ca$, 1, dp - 1) + Mid$(ca$, dp + 1)
  237.     Else
  238.         adp = 0
  239.     End If
  240.     If Left$(cb$, 1) = "-" Then
  241.         bSgn$ = "-": cb$ = Mid$(cb$, 2)
  242.     Else
  243.         bSgn$ = "": cb$ = cb$
  244.     End If
  245.     dp = InStr(cb$, ".")
  246.     If dp > 0 Then
  247.         bdp = Len(cb$) - dp
  248.         cb$ = Mid$(cb$, 1, dp - 1) + Mid$(cb$, dp + 1)
  249.     Else
  250.         bdp = 0
  251.     End If
  252.  
  253.     If op$ = "+" Or op$ = "-" Or op$ = "/" Then 'add or subtr  even up strings on right of decimal
  254.         'even up the right sides of decimals if any
  255.         If adp > bdp Then dp = adp Else dp = bdp
  256.         If adp < dp Then ca$ = ca$ + String$(dp - adp, "0")
  257.         If bdp < dp Then cb$ = cb$ + String$(dp - bdp, "0")
  258.     ElseIf op$ = "*" Then
  259.         dp = adp + bdp
  260.     End If
  261.     If op$ = "*" Or op$ = "/" Then
  262.         If aSgn$ = bSgn$ Then sgn$ = "" Else sgn$ = "-"
  263.     End If
  264.     'now according to signs and op$ call add$ or subtr$
  265.     If op$ = "+" Then
  266.         If aSgn$ = bSgn$ Then 'really add
  267.             postOp$ = aSgn$ + add$(ca$, cb$)
  268.         Else 'have a case of subtraction
  269.             If aSgn$ = "-" Then postOp$ = subtr$(cb$, ca$) Else postOp$ = subtr$(ca$, cb$)
  270.         End If
  271.     ElseIf op$ = "-" Then
  272.         If bSgn$ = "-" Then 'really add but switch b sign
  273.             bSgn$ = ""
  274.             If aSgn$ = "-" Then
  275.                 postOp$ = subtr$(cb$, ca$)
  276.             Else 'aSgn = ""
  277.                 postOp$ = add$(ca$, cb$)
  278.             End If
  279.         Else 'bSgn$ =""
  280.             If aSgn$ = "-" Then
  281.                 bSgn$ = "-"
  282.                 postOp$ = aSgn$ + add$(ca$, cb$)
  283.             Else
  284.                 postOp$ = subtr$(ca$, cb$)
  285.             End If
  286.         End If
  287.     ElseIf op$ = "*" Then
  288.         postOp$ = sgn$ + mult$(ca$, cb$)
  289.     ElseIf op$ = "/" Then
  290.         postOp$ = sgn$ + divide$(ca$, cb$)
  291.     End If ' which op
  292.     'put dp back
  293.     If op$ <> "/" Then
  294.         lpop = Len(postOp$) ' put decimal back
  295.         postOp$ = Mid$(postOp$, 1, lpop - dp) + "." + Mid$(postOp$, lpop - dp + 1)
  296.     End If
  297.     mr$ = trim0$(postOp$)
  298.  
  299. Function divide$ (n$, d$) ' goal here is 100 digits precision not 100 digits past decimal
  300.     Dim di$, ndi$, nD As Integer
  301.     If _Trim$(n$) = "0" Then divide$ = "0": Exit Function
  302.     If _Trim$(d$) = "0" Then divide$ = "div 0": Exit Function
  303.     If _Trim$(d$) = "1" Then divide$ = _Trim$(n$): Exit Function
  304.  
  305.     ' aha! found a bug when d$ gets really huge 100 is no where near enough!!!!
  306.     ' 2021-06-03 fix by adding 100 to len(d$), plus have to go a little past 100 for 100 digit precision
  307.     ' need to go past 100 for 100 precise digits (not decimal places)
  308.     di$ = Mid$(nInverse$(d$, Len(d$) + 200), 2) 'chop off decimal point after
  309.     nD = Len(di$)
  310.     ndi$ = mult$(n$, di$)
  311.     ndi$ = Mid$(ndi$, 1, Len(ndi$) - nD) + "." + Right$(String$(nD, "0") + Right$(ndi$, nD), nD)
  312.     divide$ = trim0$(ndi$)
  313.  
  314. Function nInverse$ (n$, DP As Integer) 'assume decimal at very start of the string of digits returned, no rounding
  315.     Dim m$(1 To 9), si$, r$, outstr$, d$
  316.     Dim i As Integer
  317.     For i = 1 To 9
  318.         si$ = _Trim$(Str$(i))
  319.         m$(i) = mult$(si$, n$)
  320.     Next
  321.     outstr$ = ""
  322.     If n$ = "0" Then nInverse$ = "Div 0": Exit Function
  323.     If n$ = "1" Then nInverse$ = "1": Exit Function
  324.     outstr$ = "." 'everything else n > 1 is decimal 8/17
  325.     r$ = "10"
  326.     Do
  327.         While Left$(subtr$(r$, n$), 1) = "-" '   r - n < 0
  328.             outstr$ = outstr$ + "0" '            add 0 to the  output string
  329.             If Len(outstr$) = DP Then nInverse$ = outstr$: Exit Function 'check if we've reached DP length
  330.             r$ = r$ + "0"
  331.         Wend
  332.         For i = 9 To 1 Step -1
  333.             If LTE(m$(i), r$) Then d$ = _Trim$(Str$(i)): Exit For
  334.         Next
  335.         outstr$ = outstr$ + d$
  336.         If Len(outstr$) = DP Then nInverse$ = outstr$: Exit Function
  337.         r$ = subtr$(r$, mult$(d$, n$)) 'r = r -d*n
  338.         If r$ = "0" Then nInverse$ = outstr$: Exit Function 'found a perfect divisor
  339.         r$ = r$ + "0" 'add another place
  340.     Loop
  341.  
  342. Function mult$ (a$, b$) 'assume both positive integers prechecked as all digits strings
  343.     Dim la As Integer, lb As Integer, m As Integer, g As Integer, dp As Integer
  344.     Dim f18$, f1$, t$, build$, accum$
  345.     ' fixed mult$ don't trim lead 0's 2021-06-06
  346.     If _Trim$(a$) = "0" Then mult$ = "0": Exit Function
  347.     If _Trim$(b$) = "0" Then mult$ = "0": Exit Function
  348.     If _Trim$(a$) = "1" Then mult$ = _Trim$(b$): Exit Function
  349.     If _Trim$(b$) = "1" Then mult$ = _Trim$(a$): Exit Function
  350.     'find the longer number and make it a mult of 18 to take 18 digits at a time from it
  351.     la = Len(a$): lb = Len(b$)
  352.     If la > lb Then
  353.         m = Int(la / 18) + 1
  354.         f18$ = Right$(String$(m * 18, "0") + a$, m * 18)
  355.         f1$ = b$
  356.     Else
  357.         m = Int(lb / 18) + 1
  358.         f18$ = Right$(String$(m * 18, "0") + b$, m * 18)
  359.         f1$ = a$
  360.     End If
  361.     For dp = Len(f1$) To 1 Step -1 'dp = digit position of the f1$
  362.         build$ = "" 'line builder
  363.         co = 0
  364.         'now taking 18 digits at a time Thanks Steve McNeill
  365.         For g = 1 To m
  366.             v18 = Val(Mid$(f18$, m * 18 - g * 18 + 1, 18))
  367.             sd = Val(Mid$(f1$, dp, 1))
  368.             t$ = Right$(String$(19, "0") + _Trim$(Str$(v18 * sd + co)), 19)
  369.             co = Val(Mid$(t$, 1, 1))
  370.             build$ = Mid$(t$, 2) + build$
  371.         Next g
  372.         If co Then build$ = _Trim$(Str$(co)) + build$
  373.         If dp = Len(f1$) Then
  374.             accum$ = build$
  375.         Else
  376.             accum$ = add$(accum$, build$ + String$(Len(f1$) - dp, "0"))
  377.         End If
  378.     Next dp
  379.     mult$ = accum$
  380.  
  381. Function subtr$ (sum$, minus$) ' assume both numbers are positive all digits
  382.     Dim m As Integer, g As Integer, p As Integer
  383.     Dim ts$, tm$, sign$, LG$, sm$, t$, result$
  384.  
  385.     'ts$ = TrimLead0$(sum$): tm$ = TrimLead0$(minus$)   'orig with decent square root speed
  386.     ts$ = _Trim$(sum$): tm$ = _Trim$(minus$) ' fixed subtr$ 2021-06-05
  387.     If trim0(ts$) = trim0$(tm$) Then subtr$ = "0": Exit Function 'OK proceed with function knowing they are not equal
  388.     tenE18 = 1000000000000000000 'yes!!! no dang E's
  389.     If LTE(ts$, tm$) Then ' which is bigger? minus is bigger
  390.         sign$ = "-"
  391.         m = Int(Len(tm$) / 18) + 1
  392.         LG$ = Right$(String$(m * 18, "0") + tm$, m * 18)
  393.         sm$ = Right$(String$(m * 18, "0") + ts$, m * 18)
  394.     Else 'sum is bigger
  395.         sign$ = ""
  396.         m = Int(Len(ts$) / 18) + 1
  397.         LG$ = Right$(String$(m * 18, "0") + ts$, m * 18)
  398.         sm$ = Right$(String$(m * 18, "0") + tm$, m * 18)
  399.     End If
  400.     'now taking 18 digits at a time From Steve I learned we can do more than 1 digit at a time
  401.     For g = 1 To m
  402.         VB = Val(Mid$(LG$, m * 18 - g * 18 + 1, 18))
  403.         vs = Val(Mid$(sm$, m * 18 - g * 18 + 1, 18))
  404.         If vs > VB Then
  405.             t$ = Right$(String$(18, "0") + _Trim$(Str$(tenE18 - vs + VB)), 18)
  406.             p = (m - g) * 18
  407.             While p > 0 And Mid$(LG$, p, 1) = "0"
  408.                 Mid$(LG$, p, 1) = "9"
  409.                 p = p - 1
  410.             Wend
  411.             If p > 0 Then Mid$(LG$, p, 1) = _Trim$(Str$(Val(Mid$(LG$, p, 1)) - 1))
  412.         Else
  413.             t$ = Right$(String$(18, "0") + _Trim$(Str$(VB - vs)), 18)
  414.         End If
  415.         result$ = t$ + result$
  416.     Next
  417.     subtr$ = sign$ + result$
  418.  
  419. Function add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no spaces or - signs
  420.     'first thing is to set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
  421.     Dim la As Integer, lb As Integer, m As Integer, g As Integer
  422.     Dim fa$, fb$, t$, new$, result$
  423.     la = Len(a$): lb = Len(b$)
  424.     If la > lb Then m = Int(la / 18) + 1 Else m = Int(lb / 18) + 1
  425.     fa$ = Right$(String$(m * 18, "0") + a$, m * 18)
  426.     fb$ = Right$(String$(m * 18, "0") + b$, m * 18)
  427.  
  428.     'now taking 18 digits at a time Thanks Steve McNeill
  429.     For g = 1 To m
  430.         sa = Val(Mid$(fa$, m * 18 - g * 18 + 1, 18))
  431.         sb = Val(Mid$(fb$, m * 18 - g * 18 + 1, 18))
  432.         t$ = Right$(String$(36, "0") + _Trim$(Str$(sa + sb + co)), 36)
  433.         co = Val(Mid$(t$, 1, 18))
  434.         new$ = Mid$(t$, 19)
  435.         result$ = new$ + result$
  436.     Next
  437.     If co Then result$ = Str$(co) + result$
  438.     add$ = result$
  439.  
  440. ' String Math Helpers -----------------------------------------------
  441.  
  442. 'this function needs TrimLead0$(s$) a and b have decimal removed and a and b lengths aligned on right
  443. Function LTE (a$, b$) ' a$ Less Than or Equal b$  comparison of 2 strings
  444.     Dim ca$, cb$, la As Integer, lb As Integer, i As Integer
  445.     ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
  446.     la = Len(ca$): lb = Len(cb$)
  447.     If ca$ = cb$ Then
  448.         LTE = -1
  449.     ElseIf la < lb Then ' a is smaller
  450.         LTE = -1
  451.     ElseIf la > lb Then ' a is bigger
  452.         LTE = 0
  453.     ElseIf la = lb Then ' equal lengths
  454.         For i = 1 To Len(ca$)
  455.             If Val(Mid$(ca$, i, 1)) > Val(Mid$(cb$, i, 1)) Then
  456.                 LTE = 0: Exit Function
  457.             ElseIf Val(Mid$(ca$, i, 1)) < Val(Mid$(cb$, i, 1)) Then
  458.                 LTE = -1: Exit Function
  459.             End If
  460.         Next
  461.     End If
  462.  
  463. ' ------------------------------------- use these for final display
  464.  
  465. Function TrimLead0$ (s$) 'for treating strings as number (pos integers)
  466.     Dim copys$, i As Integer, find As Integer
  467.     copys$ = _Trim$(s$) 'might as well remove spaces too
  468.     i = 1: find = 0
  469.     While i < Len(copys$) And Mid$(copys$, i, 1) = "0"
  470.         i = i + 1: find = 1
  471.     Wend
  472.     If find = 1 Then copys$ = Mid$(copys$, i)
  473.     If copys$ = "" Then TrimLead0$ = "0" Else TrimLead0$ = copys$
  474.  
  475. Function TrimTail0$ (s$)
  476.     Dim copys$, dp As Integer, i As Integer, find As Integer
  477.     copys$ = _Trim$(s$) 'might as well remove spaces too
  478.     TrimTail0$ = copys$
  479.     dp = InStr(copys$, ".")
  480.     If dp > 0 Then
  481.         i = Len(copys$): find = 0
  482.         While i > dp And Mid$(copys$, i, 1) = "0"
  483.             i = i - 1: find = 1
  484.         Wend
  485.         If find = 1 Then
  486.             If i = dp Then
  487.                 TrimTail0$ = Mid$(copys$, 1, dp - 1)
  488.             Else
  489.                 TrimTail0$ = Mid$(copys$, 1, i)
  490.             End If
  491.         End If
  492.     End If
  493.  
  494. Function trim0$ (s$)
  495.     Dim cs$, si$
  496.     cs$ = s$
  497.     si$ = Left$(cs$, 1)
  498.     If si$ = "-" Then cs$ = Mid$(cs$, 2)
  499.     cs$ = TrimLead0$(cs$)
  500.     cs$ = TrimTail0$(cs$)
  501.     If Right$(cs$, 1) = "." Then cs$ = Mid$(cs$, 1, Len(cs$) - 1)
  502.     If si$ = "-" Then trim0$ = si$ + cs$ Else trim0$ = cs$
  503.  
  504. ' for displaying truncated numbers say to 60 digits
  505. Function showDP$ (num$, nDP As Integer)
  506.     Dim cNum$, dp As Integer, d As Integer, i As Integer
  507.     cNum$ = num$ 'since num$ could get changed
  508.     showDP$ = num$
  509.     dp = InStr(num$, ".")
  510.     If dp > 0 Then
  511.         If Len(Mid$(cNum$, dp + 1)) > nDP Then
  512.             d = Val(Mid$(cNum$, dp + nDP + 1, 1))
  513.             If d > 4 Then
  514.                 cNum$ = "0" + cNum$ ' tack on another 0 just in case 9's all the way to left
  515.                 dp = dp + 1
  516.                 i = dp + nDP
  517.                 While Mid$(cNum$, i, 1) = "9" Or Mid$(cNum$, i, 1) = "."
  518.                     If Mid$(cNum$, i, 1) = "9" Then
  519.                         Mid$(cNum$, i, 1) = "0"
  520.                     End If
  521.                     i = i - 1
  522.                 Wend
  523.                 Mid$(cNum$, i, 1) = _Trim$(Str$(Val(Mid$(cNum$, i, 1)) + 1)) 'last non 9 digit
  524.                 cNum$ = Mid$(cNum$, 1, dp + nDP) 'chop it
  525.                 showDP$ = trim0$(cNum$)
  526.             Else
  527.                 showDP$ = Mid$(cNum$, 1, dp + nDP)
  528.             End If
  529.         End If
  530.     End If
  531.  
Title: Re: String Math
Post by: NOVARSEG on June 06, 2021, 11:32:23 pm
This link might help

https://www.qb64.org/forum/index.php?topic=3716.15
Title: Re: String Math
Post by: bplus on June 14, 2021, 04:04:41 pm
Here is current state of the art for String Math. A number of fixes and improvements including sqrRoot$(), approx 412 LOC for the copy/paste procedures into your app.

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. 'Print mult3$("11", "1001")
  18. 'End
  19.  
  20. Dim r$, ruler$
  21. ruler$ = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" + Chr$(10)
  22. ruler$ = ruler$ + "0         1         2         3         4         5         6         7         8         9        10        11        12"
  23.  
  24. ' jack error reported 2021-06-04 confirmed!   fixed
  25. Print mr$(".00000000000000000000000000000000000000000000200000000000000054307978001764", "-", ".000000000000000000000000000000000000000000002")
  26. Print ".00000000000000000000000000000000000000000000000000000000000054307978001764"
  27. ' debug tests
  28. Print mr$("-5", "+", "-2100"), " OK if -2105"
  29. Print mr$("." + String$(20, "0") + "1", "+", "1" + String$(40, "0") + "1") ' first real test of add
  30. Print ruler$
  31. Print Val("." + String$(20, "0") + "1") + Val("1" + String$(40, "0") + "1") ' test print of big and small val
  32. Print mr$("-.00071", "+", ".00036" + String$(35, "0") + "9")
  33. '-.00071
  34. ' .00036000000000000000000000000000000000009
  35. Print "-.00034999999999999999999999999999999999991"
  36.  
  37. 'testing a different subtract sub
  38. Print mr$("10", "-", "5"), " 5 OK"
  39. Print mr$("-10", "+", "5"), " -5 OK"
  40. Print mr$("-10", "-", "-5"), " -5 OK"
  41. Print mr$("-10", "-", "5"), " -15  OK  added"
  42. Print mr$("10", "-", "-5"), " 15  OK  added"
  43. Print mr$("-.010", "-", "-5"), "4.99 OK"
  44. Print mr$("-.010", "-", "5"), "-5.01  OK just added"
  45. Print mr$(".010", "-", "5"), " -4.99 OK"
  46. ' jack error reported 2021-06-04 confirmed!  variation below
  47. r$ = mr$(".00000000200000000000000054307978001764", "-", ".0000000020000000000000001") '16 0 wrong  8 wrong
  48. Print "   mr$ rtnd:"; r$
  49. Print "    compare:.00000000000000000000000044307978001764" ' 2021-06-05 finally!
  50. '                  .0000000020000000000000001
  51. '                  .00000000200000000000000054307978001764
  52.  
  53. r$ = mr$(".00000000000000000100000000000000000011", "-", ".000000000000000001000000000000000001") ' bad too
  54. Print "   mr$ rtnd:"; r$
  55. '         ".000000000000000001000000000000000001"
  56. Print "    compare:-.00000000000000000000000000000000000089"
  57. r$ = mr$(".00000000000000000100000000000000000111", "-", ".000000000000000002000000000000000001")
  58. '       ".000000000000000002000000000000000001"
  59. '       ".00000000000000000100000000000000000111"
  60. '       ".00000000000000000099999999999999999989"
  61. Print "   mr$ rtnd:"; r$
  62. Print "    compare:-.00000000000000000099999999999999999989"
  63.  
  64. r$ = mr$(".00000000000000000000000000999", "-", "1")
  65. '-1.00000000000000000000000000000000000000000
  66. '  .00000000000000000000000000999
  67. ' -.99999999999999999999999999001
  68. Print "   mr$ rtnd:"; r$
  69. Print "    compare:-.99999999999999999999999999001"
  70.  
  71. r$ = mr$("1", "+", "-1000000000000000000000000000000000000000")
  72. Print "   mr$ rtnd:"; r$
  73. '1000000000000000000000000000000000000000
  74. '-999999999999999999999999999999999999999
  75. Print "    compare:-999999999999999999999999999999999999999"
  76.  
  77. ' check jack problems with FB translation  2021-06-03  errors must be in FB trans from QB64
  78. Print Mid$(mr$(" .1", "/", "3"), 1, 100) ' too long?
  79. Print Mid$(mr$("1.1", "/", "9"), 1, 100)
  80. Print Mid$(mr$("1.38", "/", "1.2"), 1, 100)
  81.  
  82. ' another error reported by jack 2021-06-06 fixed (same problem as subtr$)
  83. Print mr$("1.000000000000000000000001000000000000000000000001", "*", ".000000000000000000000000000000000000000000000001")
  84. Print ".000000000000000000000000000000000000000000000001"
  85. Print "                                                1000000000000000000000001000000000000000000000001"
  86. Print ruler$
  87. Print "checking inverse of 50 = .02 "; nInverse$("50", 20) 'OK  .020000... length 20
  88. Print "zzz... see inverse of STx number now takes 2.2 secs with fixed subtr$() sub,"
  89. Print "can find 115 Fibonacci terms in it."
  90. Dim inverseSTx$, start, done
  91. start = Timer(.001)
  92. inverseSTx$ = nInverse$("999999999999999999999998999999999999999999999999", 2785) ' now 2.2 secs with new subtr from 19 secs
  93. 'inverseSTx$ = nInverse2$("999999999999999999999998999999999999999999999999", 817) ' 13 sec damn added 7 secs! now 19.xx
  94. ' 816 in 4.22 secs only 64 terms   817 sigDigits matching in 12.93 secs gets all 115 Fibonacci Terms
  95. done = Timer(.001) - start
  96. Print Mid$(inverseSTx$, 1, 3000)
  97. Print "Inverse time:"; done; "   zzz... press any to search for Fibonacci Terms"
  98. Dim As Long startSearch, termN, find
  99. Dim f1$, f2$, searchFor$
  100. f1$ = "1"
  101. f2$ = "1"
  102. startSearch = 1
  103. termN = 2
  104.     searchFor$ = mr$(f1$, "+", f2$)
  105.     find = InStr(startSearch, inverseSTx$, searchFor$)
  106.     If find Then
  107.         termN = termN + 1
  108.         Print "Term Number"; termN; " = "; searchFor$; " found at"; find
  109.         f1$ = f2$
  110.         f2$ = searchFor$
  111.         startSearch = find + Len(searchFor$)
  112.     Else
  113.         Print searchFor$; " not found."
  114.         Exit Do
  115.     End If
  116. ' test factorial speed
  117. Dim fact$, i As _Unsigned _Integer64, refFact$, cont$
  118. Input "Press y for yes, let's do 10000 factorial test, takes quite a bit of time (3.25 mins) "; fact$
  119. If fact$ = "y" Then
  120.     start = Timer(.001)
  121.     fact$ = "1"
  122.     For i = 2 To 10000
  123.         fact$ = TrimLead0$(mult$(fact$, _Trim$(Str$(i))))
  124.         If i Mod 100 = 0 Then Print i; "factorial length ="; Len(fact$)
  125.     Next
  126.     done = Timer(.001) - start
  127.     Print i, Len(fact$), done
  128.     ' save it
  129.     Open "calc 10000!.txt" For Output As #1
  130.     Print #1, fact$
  131.     Close #1
  132.     Beep
  133.     Print Len(fact$), done, "   zzz... press any to compare to reference 10000!."
  134.     Sleep
  135.     _KeyClear
  136.     If _FileExists("10000!.txt") Then
  137.         Open "10000!.txt" For Input As #1
  138.         Input #1, refFact$
  139.         Close #1
  140.         Print "Comparing fact$ to reference fact$:"
  141.         For i = 1 To Len(fact$)
  142.             If Mid$(fact$, i, 1) <> Mid$(refFact$, i, 1) Then
  143.                 Print i, Mid$(fact$, i, 1), Mid$(refFact$, i, 1)
  144.                 Beep
  145.                 Input "Mismatch! Continue? y for yes "; cont$
  146.                 If cont$ <> "y" Then Exit For
  147.             End If
  148.         Next
  149.         Print "Compare finished, mismatchs already noted if any."
  150.     Else
  151.         Print "Can't find 10000!.txt reference file."
  152.     End If
  153.     _KeyClear
  154.     Print "zzz... press any to start sqr estimating"
  155.     Sleep
  156.     _KeyClear
  157.  
  158. Dim n$, result$
  159.     'remember everything is strings
  160.     Input "Enter a number to find it's square root, just enter to quit "; n$
  161.     If n$ = "" Then End
  162.     result$ = sqrRoot$(n$)
  163.     Print result$
  164.     Print "Length ="; Len(result$)
  165.     Print
  166.  
  167. ' =========  String Math Procedure start here (aprox 412 LOC for copy/paste into your app) ======
  168.  
  169. Function sqrRoot$ (nmbr$)
  170.     Dim n$, guess$, lastGuess$, other$, sum$, imaginary$, loopcnt
  171.     If Left$(nmbr$, 1) = "-" Then 'handle neg numbers
  172.         imaginary$ = "*i": n$ = Mid$(nmbr$, 2)
  173.     Else
  174.         imaginary$ = "": n$ = nmbr$
  175.     End If
  176.     guess$ = mr$(n$, "/", "2")
  177.     other$ = n$
  178.     Do
  179.         loopcnt = loopcnt + 1
  180.         If (Mid$(guess$, 1, 105) = Mid$(lastGuess$, 1, 105)) Then
  181.             ' go past 100 matching digits for 100 digit precision
  182.             sqrRoot$ = Mid$(other$, 1, 101) + imaginary$
  183.             ' try other factor for guess$  sometimes it nails answer without all digits
  184.             Exit Function
  185.         Else
  186.             lastGuess$ = guess$
  187.             sum$ = mr$(guess$, "+", other$)
  188.             guess$ = mr$(sum$, "/", "2")
  189.             other$ = mr$(n$, "/", guess$)
  190.         End If
  191.     Loop
  192.  
  193. Function add$ (a$, b$) 'add 2 positive integers assume a and b are just numbers no - signs
  194.     'set a and b numbers to same length and multiple of 18 so can take 18 digits at a time
  195.     Dim As Long la, lb, m, g
  196.     Dim fa$, fb$, t$, new$, result$
  197.     la = Len(a$): lb = Len(b$)
  198.     If la > lb Then m = Int(la / 18) + 1 Else m = Int(lb / 18) + 1
  199.     fa$ = Right$(String$(m * 18, "0") + a$, m * 18)
  200.     fb$ = Right$(String$(m * 18, "0") + b$, m * 18)
  201.  
  202.     'now taking 18 digits at a time Thanks Steve McNeill
  203.     For g = 1 To m
  204.         sa = Val(Mid$(fa$, (m - g) * 18 + 1, 18))
  205.         sb = Val(Mid$(fb$, (m - g) * 18 + 1, 18))
  206.         t$ = Right$(String$(36, "0") + _Trim$(Str$(sa + sb + co)), 36)
  207.         co = Val(Mid$(t$, 1, 18))
  208.         new$ = Mid$(t$, 19)
  209.         result$ = new$ + result$
  210.     Next
  211.     If co Then result$ = Str$(co) + result$
  212.     add$ = result$
  213.  
  214. ' This is used in nInverse$ not by Mr$ because there it saves time!
  215. Function subtr1$ (a$, b$)
  216.     Dim As Long la, lb, lResult, i, ca, cb, w
  217.     Dim result$, fa$, fb$
  218.  
  219.     la = Len(a$): lb = Len(b$)
  220.     If la > lb Then lResult = la Else lResult = lb
  221.     result$ = Space$(lResult)
  222.     fa$ = result$: fb$ = result$
  223.     Mid$(fa$, lResult - la + 1) = a$
  224.     Mid$(fb$, lResult - lb + 1) = b$
  225.     For i = lResult To 1 Step -1
  226.         ca = Val(Mid$(fa$, i, 1))
  227.         cb = Val(Mid$(fb$, i, 1))
  228.         If cb > ca Then ' borrow 10
  229.             Mid$(result$, i, 1) = Right$(Str$(10 + ca - cb), 1)
  230.             w = i - 1
  231.             While w > 0 And Mid$(fa$, w, 1) = "0"
  232.                 Mid$(fa$, w, 1) = "9"
  233.                 w = w - 1
  234.             Wend
  235.             Mid$(fa$, w, 1) = Right$(Str$(Val(Mid$(fa$, w, 1)) - 1), 1)
  236.         Else
  237.             Mid$(result$, i, 1) = Right$(Str$(ca - cb), 1)
  238.         End If
  239.     Next
  240.     subtr1$ = result$
  241.  
  242. ' 2021-06-08 fix up with new mr call that decides the sign and puts the greater number first
  243. Function subtr$ (sum$, minus$) ' assume both numbers are positive all digits
  244.     Dim As Long m, g, p
  245.     Dim ts$, tm$, sign$, LG$, sm$, t$, result$
  246.  
  247.     ts$ = _Trim$(sum$): tm$ = _Trim$(minus$) ' fixed subtr$ 2021-06-05
  248.     If trim0(ts$) = trim0$(tm$) Then subtr$ = "0": Exit Function 'proceed knowing not equal
  249.     tenE18 = 1000000000000000000 'yes!!! no dang E's
  250.     sign$ = ""
  251.     m = Int(Len(ts$) / 18) + 1
  252.     LG$ = Right$(String$(m * 18, "0") + ts$, m * 18)
  253.     sm$ = Right$(String$(m * 18, "0") + tm$, m * 18)
  254.  
  255.     'now taking 18 digits at a time From Steve I learned we can do more than 1 digit at a time
  256.     For g = 1 To m
  257.         VB = Val(Mid$(LG$, m * 18 - g * 18 + 1, 18))
  258.         vs = Val(Mid$(sm$, m * 18 - g * 18 + 1, 18))
  259.         If vs > VB Then
  260.             t$ = Right$(String$(18, "0") + _Trim$(Str$(tenE18 - vs + VB)), 18)
  261.             p = (m - g) * 18
  262.             While p > 0 And Mid$(LG$, p, 1) = "0"
  263.                 Mid$(LG$, p, 1) = "9"
  264.                 p = p - 1
  265.             Wend
  266.             If p > 0 Then Mid$(LG$, p, 1) = _Trim$(Str$(Val(Mid$(LG$, p, 1)) - 1))
  267.         Else
  268.             t$ = Right$(String$(18, "0") + _Trim$(Str$(VB - vs)), 18)
  269.         End If
  270.         result$ = t$ + result$
  271.     Next
  272.     subtr$ = result$
  273.  
  274. Function TrimLead0$ (s$) 'for treating strings as number (pos integers)
  275.     Dim copys$
  276.     Dim As Long i, find
  277.     copys$ = _Trim$(s$) 'might as well remove spaces too
  278.     i = 1: find = 0
  279.     While i < Len(copys$) And Mid$(copys$, i, 1) = "0"
  280.         i = i + 1: find = 1
  281.     Wend
  282.     If find = 1 Then copys$ = Mid$(copys$, i)
  283.     If copys$ = "" Then TrimLead0$ = "0" Else TrimLead0$ = copys$
  284.  
  285. ' catchy? mr$ for math regulator  cop$ = " + - * / " 1 of 4 basic arithmetics
  286. ' Fixed so that add and subtract have signs calc'd in Mr and correct call to add or subtract made
  287. ' with bigger minus smaller in subtr$() call
  288. Function mr$ (a$, cop$, b$)
  289.     Dim op$, ca$, cb$, aSgn$, bSgn$, postOp$, sgn$
  290.     Dim As Long adp, bdp, dp, lpop, aLTb
  291.  
  292.     op$ = _Trim$(cop$) 'save fixing each time
  293.     ca$ = _Trim$(a$): cb$ = _Trim$(b$) 'make copies in case we change
  294.     'strip signs and decimals
  295.     If Left$(ca$, 1) = "-" Then
  296.         aSgn$ = "-": ca$ = Mid$(ca$, 2)
  297.     Else
  298.         aSgn$ = ""
  299.     End If
  300.     dp = InStr(ca$, ".")
  301.     If dp > 0 Then
  302.         adp = Len(ca$) - dp
  303.         ca$ = Mid$(ca$, 1, dp - 1) + Mid$(ca$, dp + 1)
  304.     Else
  305.         adp = 0
  306.     End If
  307.     If Left$(cb$, 1) = "-" Then
  308.         bSgn$ = "-": cb$ = Mid$(cb$, 2)
  309.     Else
  310.         bSgn$ = ""
  311.     End If
  312.     dp = InStr(cb$, ".")
  313.     If dp > 0 Then
  314.         bdp = Len(cb$) - dp
  315.         cb$ = Mid$(cb$, 1, dp - 1) + Mid$(cb$, dp + 1)
  316.     Else
  317.         bdp = 0
  318.     End If
  319.     If op$ = "+" Or op$ = "-" Or op$ = "/" Then 'add or subtr  even up strings on right of decimal
  320.         'even up the right sides of decimals if any
  321.         If adp > bdp Then dp = adp Else dp = bdp
  322.         If adp < dp Then ca$ = ca$ + String$(dp - adp, "0")
  323.         If bdp < dp Then cb$ = cb$ + String$(dp - bdp, "0")
  324.     ElseIf op$ = "*" Then
  325.         dp = adp + bdp
  326.     End If
  327.     If op$ = "*" Or op$ = "/" Then
  328.         If aSgn$ = bSgn$ Then sgn$ = "" Else sgn$ = "-"
  329.     End If
  330.  
  331.     'now according to signs and op$ call add$ or subtr$
  332.     If op$ = "-" Then ' make it adding according to signs because that is done for + next!
  333.         If bSgn$ = "-" Then bSgn$ = "" Else bSgn$ = "-" ' flip bSgn$ with op$
  334.         op$ = "+" ' turn this over to + op already done! below
  335.     End If
  336.     If op$ = "+" Then
  337.         If aSgn$ = bSgn$ Then 'really add
  338.             postOp$ = add$(ca$, cb$)
  339.             sgn$ = aSgn$
  340.         ElseIf aSgn$ <> bSgn$ Then 'have a case of subtraction
  341.             'but which is first and which is 2nd and should final sign be pos or neg
  342.             If TrimLead0$(ca$) = TrimLead0(cb$) Then 'remove case a = b
  343.                 mr$ = "0": Exit Function
  344.             Else
  345.                 aLTb = LTE(ca$, cb$)
  346.                 If aSgn$ = "-" Then
  347.                     If aLTb Then ' b - a = pos
  348.                         postOp$ = subtr$(cb$, ca$)
  349.                         sgn$ = ""
  350.                     Else '  a > b so a - sgn wins  - (a - b)
  351.                         postOp$ = subtr$(ca$, cb$)
  352.                         sgn$ = "-"
  353.                     End If
  354.                 Else ' b has the - sgn
  355.                     If aLTb Then ' result is -
  356.                         postOp$ = subtr$(cb$, ca$)
  357.                         sgn$ = "-"
  358.                     Else ' result is pos
  359.                         postOp$ = subtr$(ca$, cb$)
  360.                         sgn$ = ""
  361.                     End If
  362.                 End If
  363.             End If
  364.         End If
  365.     ElseIf op$ = "*" Then
  366.         postOp$ = mult$(ca$, cb$)
  367.     ElseIf op$ = "/" Then
  368.         postOp$ = divide$(ca$, cb$)
  369.     End If ' which op
  370.     If op$ <> "/" Then 'put dp back
  371.         lpop = Len(postOp$) ' put decimal back if there is non zero stuff following it
  372.         If Len(Mid$(postOp$, lpop - dp + 1)) Then ' fix 1 extra dot appearing in 10000! ?!
  373.             If TrimLead0$(Mid$(postOp$, lpop - dp + 1)) <> "0" Then '  .0   or .00 or .000  ??
  374.                 postOp$ = Mid$(postOp$, 1, lpop - dp) + "." + Mid$(postOp$, lpop - dp + 1)
  375.             End If
  376.         End If
  377.     End If
  378.     mr$ = trim0$(postOp$) 'trim lead 0's then tack on sign
  379.     If mr$ <> "0" Then mr$ = sgn$ + mr$
  380.  
  381. Function divide$ (n$, d$) ' goal here is 100 digits precision not 100 digits past decimal
  382.     Dim di$, ndi$
  383.     Dim As Long nD
  384.     If n$ = "0" Then divide$ = "0": Exit Function
  385.     If d$ = "0" Then divide$ = "div 0": Exit Function
  386.     If d$ = "1" Then divide$ = n$: Exit Function
  387.  
  388.     ' aha! found a bug when d$ gets really huge 100 is no where near enough!!!!
  389.     ' 2021-06-03 fix by adding 100 to len(d$), plus have to go a little past 100 like 200
  390.     di$ = Mid$(nInverse$(d$, Len(d$) + 200), 2) 'chop off decimal point after
  391.     nD = Len(di$)
  392.     ndi$ = mult$(n$, di$)
  393.     ndi$ = Mid$(ndi$, 1, Len(ndi$) - nD) + "." + Right$(String$(nD, "0") + Right$(ndi$, nD), nD)
  394.     divide$ = ndi$
  395.  
  396. ' This uses Subtr1$ is Positive Integer only!
  397. ' DP = Decimal places = says when to quit if don't find perfect divisor before
  398. Function nInverse$ (n$, DP As Long) 'assume decimal at very start of the string of digits returned
  399.     Dim m$(1 To 9), si$, r$, outstr$, d$
  400.     Dim i As Long
  401.     For i = 1 To 9
  402.         si$ = _Trim$(Str$(i))
  403.         m$(i) = mult$(si$, n$)
  404.     Next
  405.     outstr$ = ""
  406.     If n$ = "0" Then nInverse$ = "Div 0": Exit Function
  407.     If n$ = "1" Then nInverse$ = "1": Exit Function
  408.     outstr$ = "." 'everything else n > 1 is decimal 8/17
  409.     r$ = "10"
  410.     Do
  411.         While LT(r$, n$) ' 2021-06-08 this should be strictly Less Than
  412.             outstr$ = outstr$ + "0" '            add 0 to the  output string
  413.             If Len(outstr$) = DP + 1 Then nInverse$ = outstr$: Exit Function 'DP length?
  414.             r$ = r$ + "0"
  415.         Wend
  416.         For i = 9 To 1 Step -1
  417.             If LTE(m$(i), r$) Then d$ = _Trim$(Str$(i)): Exit For
  418.         Next
  419.         outstr$ = outstr$ + d$
  420.         If Len(outstr$) = DP + 1 Then nInverse$ = outstr$: Exit Function
  421.         r$ = subtr1$(r$, mult$(d$, n$)) 'r = r -d*n    ' 2021-06-08 subtr1 works faster
  422.         If TrimLead0$(r$) = "0" Then nInverse$ = outstr$: Exit Function ' add trimlead0$ 6/08
  423.         r$ = r$ + "0" 'add another place
  424.     Loop
  425.  
  426. Function mult$ (a$, b$) 'assume both positive integers prechecked as all digits strings
  427.     Dim As Long la, lb, m, g, dp
  428.     Dim As _Unsigned _Integer64 v18, sd, co
  429.     Dim f18$, f1$, t$, build$, accum$
  430.  
  431.     If a$ = "0" Then mult$ = "0": Exit Function
  432.     If b$ = "0" Then mult$ = "0": Exit Function
  433.     If a$ = "1" Then mult$ = b$: Exit Function
  434.     If b$ = "1" Then mult$ = a$: Exit Function
  435.     'find the longer number and make it a mult of 18 to take 18 digits at a time from it
  436.     la = Len(a$): lb = Len(b$)
  437.     If la > lb Then
  438.         m = Int(la / 18) + 1
  439.         f18$ = Right$(String$(m * 18, "0") + a$, m * 18)
  440.         f1$ = b$
  441.     Else
  442.         m = Int(lb / 18) + 1
  443.         f18$ = Right$(String$(m * 18, "0") + b$, m * 18)
  444.         f1$ = a$
  445.     End If
  446.     For dp = Len(f1$) To 1 Step -1 'dp = digit position of the f1$
  447.         build$ = "" 'line builder
  448.         co = 0
  449.         'now taking 18 digits at a time Thanks Steve McNeill
  450.         For g = 1 To m
  451.             v18 = Val(Mid$(f18$, (m - g) * 18 + 1, 18))
  452.             sd = Val(Mid$(f1$, dp, 1))
  453.             t$ = Right$(String$(19, "0") + _Trim$(Str$(v18 * sd + co)), 19)
  454.             co = Val(Mid$(t$, 1, 1))
  455.             build$ = Mid$(t$, 2) + build$
  456.         Next g
  457.         If co Then build$ = _Trim$(Str$(co)) + build$
  458.         If dp = Len(f1$) Then
  459.             accum$ = build$
  460.         Else
  461.             accum$ = add$(accum$, build$ + String$(Len(f1$) - dp, "0"))
  462.         End If
  463.     Next dp
  464.     mult$ = accum$
  465.  
  466. 'this function needs TrimLead0$(s$)
  467. Function LTE (a$, b$) ' a$ Less Than or Equal b$  comparison of 2 strings
  468.     Dim ca$, cb$
  469.     Dim As Long la, lb, i
  470.     ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
  471.     la = Len(ca$): lb = Len(cb$)
  472.     If ca$ = cb$ Then
  473.         LTE = -1
  474.     ElseIf la < lb Then ' a is smaller
  475.         LTE = -1
  476.     ElseIf la > lb Then ' a is bigger
  477.         LTE = 0
  478.     ElseIf la = lb Then ' equal lengths
  479.         For i = 1 To Len(ca$)
  480.             If Val(Mid$(ca$, i, 1)) > Val(Mid$(cb$, i, 1)) Then
  481.                 LTE = 0: Exit Function
  482.             ElseIf Val(Mid$(ca$, i, 1)) < Val(Mid$(cb$, i, 1)) Then
  483.                 LTE = -1: Exit Function
  484.             End If
  485.         Next
  486.     End If
  487.  
  488. 'need this for ninverse faster than subtr$ for sign
  489. Function LT (a$, b$) ' a$ Less Than or Equal b$  comparison of 2 strings
  490.     Dim ca$, cb$
  491.     Dim As Long la, lb, i
  492.     ca$ = TrimLead0$(a$): cb$ = TrimLead0(b$)
  493.     la = Len(ca$): lb = Len(cb$)
  494.     If la < lb Then ' a is smaller
  495.         LT = -1
  496.     ElseIf la > lb Then ' a is bigger
  497.         LT = 0
  498.     ElseIf la = lb Then ' equal lengths
  499.         For i = 1 To Len(ca$)
  500.             If Val(Mid$(ca$, i, 1)) > Val(Mid$(cb$, i, 1)) Then
  501.                 LT = 0: Exit Function
  502.             ElseIf Val(Mid$(ca$, i, 1)) < Val(Mid$(cb$, i, 1)) Then
  503.                 LT = -1: Exit Function
  504.             End If
  505.         Next
  506.     End If
  507.  
  508. Function TrimTail0$ (s$)
  509.     Dim copys$
  510.     Dim As Long dp, i, find
  511.     copys$ = _Trim$(s$) 'might as well remove spaces too
  512.     TrimTail0$ = copys$
  513.     dp = InStr(copys$, ".")
  514.     If dp > 0 Then
  515.         i = Len(copys$): find = 0
  516.         While i > dp And Mid$(copys$, i, 1) = "0"
  517.             i = i - 1: find = 1
  518.         Wend
  519.         If find = 1 Then
  520.             If i = dp Then
  521.                 TrimTail0$ = Mid$(copys$, 1, dp - 1)
  522.             Else
  523.                 TrimTail0$ = Mid$(copys$, 1, i)
  524.             End If
  525.         End If
  526.     End If
  527.  
  528. Function trim0$ (s$)
  529.     Dim cs$, si$
  530.     cs$ = s$
  531.     si$ = Left$(cs$, 1)
  532.     If si$ = "-" Then cs$ = Mid$(cs$, 2)
  533.     cs$ = TrimLead0$(cs$)
  534.     cs$ = TrimTail0$(cs$)
  535.     If Right$(cs$, 1) = "." Then cs$ = Mid$(cs$, 1, Len(cs$) - 1)
  536.     If si$ = "-" Then trim0$ = si$ + cs$ Else trim0$ = cs$
  537.  
  538. ' for displaying truncated numbers say to 60 digits
  539. Function showDP$ (num$, nDP As Long)
  540.     Dim cNum$
  541.     Dim As Long dp, d, i
  542.     cNum$ = num$ 'since num$ could get changed
  543.     showDP$ = num$
  544.     dp = InStr(num$, ".")
  545.     If dp > 0 Then
  546.         If Len(Mid$(cNum$, dp + 1)) > nDP Then
  547.             d = Val(Mid$(cNum$, dp + nDP + 1, 1))
  548.             If d > 4 Then
  549.                 cNum$ = "0" + cNum$ ' tack on another 0 just in case 9's all the way to left
  550.                 dp = dp + 1
  551.                 i = dp + nDP
  552.                 While Mid$(cNum$, i, 1) = "9" Or Mid$(cNum$, i, 1) = "."
  553.                     If Mid$(cNum$, i, 1) = "9" Then
  554.                         Mid$(cNum$, i, 1) = "0"
  555.                     End If
  556.                     i = i - 1
  557.                 Wend
  558.                 Mid$(cNum$, i, 1) = _Trim$(Str$(Val(Mid$(cNum$, i, 1)) + 1)) 'last non 9 digit
  559.                 cNum$ = Mid$(cNum$, 1, dp + nDP) 'chop it
  560.                 showDP$ = trim0$(cNum$)
  561.             Else
  562.                 showDP$ = Mid$(cNum$, 1, dp + nDP)
  563.             End If
  564.         End If
  565.     End If
  566.  

The test code in main code area calculates 10000! and compares it to a reference file, if found.

Here is a copy if you'd like, the 3rd line in file is a link to where I got the number for reference in
10000!.txt:

Title: Re: String Math
Post by: jack on June 14, 2021, 06:50:23 pm
congrats bplus, 98 seconds for the factorial on my PC
Title: Re: String Math
Post by: bplus on June 14, 2021, 08:58:55 pm
Thanks @jack

BTW you mentioned you did not understand my nInverse$(), as I recall I coded it as I learned to do division in school only we are just dividing into 1 so we are always adding 0's to remainder until big enough to divide some more by denominator and find another set of remainder start digits upon which to add zeros... maybe you figured it out already.

This String Math will have to do until you or George or Luke get a full set of operations going with memory works or with decimal math segments building.
Title: Re: String Math
Post by: david_uwi on June 16, 2021, 02:17:18 pm
I am always confused when calculation that gives a large number of digits is generally tackled by using strings.
It seems obvious that you could store the digits in an array (rather than a string) which would avoid the VAL and STR$ which are slow.
Calculating the 115 Fibonacci is trivial even doing it the hard way of going through all the numbers 11235...to get to the 115 number. I've posted it before (but as I'm very proud of it) I will post it again.
Code: QB64: [Select]
  1. DIM a(30000) AS INTEGER, b(30000) AS INTEGER
  2. DEFLNG F-I
  3. DEFINT J-N
  4. INPUT "input the fibonacci number to calculate"; fmax
  5. tt = TIMER
  6. b(1) = 1: n = 1
  7. FOR i = 1 TO (fmax + 1) \ 2
  8.     FOR j = 1 TO n
  9.         b(j) = a(j) + b(j) + jc
  10.         IF b(j) > 9999 THEN b(j) = b(j) - 10000: jc = 1 ELSE jc = 0
  11.     NEXT j
  12.     IF jc = 1 THEN n = n + 1: b(n) = 1: jc = 0
  13.     FOR j = 1 TO n
  14.         a(j) = a(j) + b(j) + jc
  15.         IF a(j) > 9999 THEN a(j) = a(j) - 10000: jc = 1 ELSE jc = 0
  16.     NEXT j
  17.     IF jc = 1 THEN n = n + 1: a(n) = 1: jc = 0
  18. FOR j = n TO 1 STEP -1
  19.     IF fmax MOD 2 = 0 THEN
  20.         IF j = n THEN t$ = LTRIM$(STR$(a(j))) ELSE t$ = RIGHT$("0000" + LTRIM$(STR$(a(j))), 4)
  21.         PRINT USING "&"; t$;
  22.     END IF
  23.     IF fmax MOD 2 = 1 THEN
  24.         IF j = n THEN t$ = LTRIM$(STR$(b(j))) ELSE t$ = RIGHT$("0000" + LTRIM$(STR$(b(j))), 4)
  25.         PRINT USING "&"; t$;
  26.     END IF
  27. PRINT: PRINT "number of digits = ";
  28. IF fmax MOD 2 = 1 THEN PRINT (n - 1) * 4 + LEN(LTRIM$(STR$(a(n))))
  29. IF fmax MOD 2 = 0 THEN PRINT (n - 1) * 4 + LEN(LTRIM$(STR$(b(n))))
  30. PRINT "TIME TAKEN= "; TIMER - tt; "SECONDS"
Title: Re: String Math
Post by: bplus on June 16, 2021, 03:23:14 pm
I am always confused when calculation that gives a large number of digits is generally tackled by using strings.
It seems obvious that you could store the digits in an array (rather than a string) which would avoid the VAL and STR$ which are slow.
Calculating the 115 Fibonacci is trivial even doing it the hard way of going through all the numbers 11235...to get to the 115 number. I've posted it before (but as I'm very proud of it) I will post it again.
Code: QB64: [Select]
  1. DIM a(30000) AS INTEGER, b(30000) AS INTEGER
  2. DEFLNG F-I
  3. DEFINT J-N
  4. INPUT "input the fibonacci number to calculate"; fmax
  5. tt = TIMER
  6. b(1) = 1: n = 1
  7. FOR i = 1 TO (fmax + 1) \ 2
  8.     FOR j = 1 TO n
  9.         b(j) = a(j) + b(j) + jc
  10.         IF b(j) > 9999 THEN b(j) = b(j) - 10000: jc = 1 ELSE jc = 0
  11.     NEXT j
  12.     IF jc = 1 THEN n = n + 1: b(n) = 1: jc = 0
  13.     FOR j = 1 TO n
  14.         a(j) = a(j) + b(j) + jc
  15.         IF a(j) > 9999 THEN a(j) = a(j) - 10000: jc = 1 ELSE jc = 0
  16.     NEXT j
  17.     IF jc = 1 THEN n = n + 1: a(n) = 1: jc = 0
  18. FOR j = n TO 1 STEP -1
  19.     IF fmax MOD 2 = 0 THEN
  20.         IF j = n THEN t$ = LTRIM$(STR$(a(j))) ELSE t$ = RIGHT$("0000" + LTRIM$(STR$(a(j))), 4)
  21.         PRINT USING "&"; t$;
  22.     END IF
  23.     IF fmax MOD 2 = 1 THEN
  24.         IF j = n THEN t$ = LTRIM$(STR$(b(j))) ELSE t$ = RIGHT$("0000" + LTRIM$(STR$(b(j))), 4)
  25.         PRINT USING "&"; t$;
  26.     END IF
  27. PRINT: PRINT "number of digits = ";
  28. IF fmax MOD 2 = 1 THEN PRINT (n - 1) * 4 + LEN(LTRIM$(STR$(a(n))))
  29. IF fmax MOD 2 = 0 THEN PRINT (n - 1) * 4 + LEN(LTRIM$(STR$(b(n))))
  30. PRINT "TIME TAKEN= "; TIMER - tt; "SECONDS"

So what do you get for first 10,000 digit Fibonacci?

I get:
  [ This attachment cannot be displayed inline in 'Print Page' view ]  
Title: Re: String Math
Post by: bplus on June 16, 2021, 03:32:00 pm
Is fast!
 


But I never claimed to be faster, just a set of routines in about 412 LOC to extend math beyond Type restrictions of QB64.

Update: for the record, String Math took 21.1692 secs to get to 47,847th term, the first at 10,000 digits.

@david_uwi also for the record we are getting Fibonacci sequence of 115 terms from inverse of STx Number:
"999999999999999999999998999999999999999999999999"
which is so much cooler than merely adding up a bunch of terms.

If you get a whole set of routines in a fair amount of LOC then I will gladly swap out String Math routines in NY minute!

If someone needs something now, here it is, slow as it may be. :)
Title: Re: String Math
Post by: jack on June 16, 2021, 04:42:44 pm
hi bplus
try the reciprocal of 999999999999999999999999999999999999999999999999999999999999999999999999999999999999999998999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
it requires about 38,709 digits and gives 432 Fibo numbers starting with 0
here's pseudo code to produce them
Code: [Select]
s$=string$(89,"9")+"8"+string(90,"9")
z$=nInverse(s$)
j=instr(z$,".")
s$=mid(z$,j+1)
j=1
for i=1 to 432
print mid$(s,j,90)
j=j+90
next
Title: Re: String Math
Post by: bplus on June 16, 2021, 04:46:38 pm
@jack  cool! thanks :)

Confirmed:
Counting 0 as first, 432 terms found the last starts at 38792. I had to increase my number of decimals to 39000 from what I tried the first time, probably not minimum needed to find the 432 terms. 6.81 mins was the time for last run.
Title: Re: String Math
Post by: jack on June 16, 2021, 07:42:07 pm
Is fast!
1.7 seconds
that really fast for string math, well done bplus
Title: Re: String Math
Post by: bplus on June 16, 2021, 08:07:40 pm
that really fast for string math, well done bplus

I wish, that was David's code I was saying his was fast. I did say later post 21 secs for String Math.

I don't think you will catch me initiating time trials with String Math :)
Title: Re: String Math
Post by: jack on June 17, 2021, 03:58:03 pm
hi bplus
I found this interesting document MULTIPLE-LENGTH DIVISION REVISITED (https://surface.syr.edu/cgi/viewcontent.cgi?article=1162&context=eecs_techreports)
Title: Re: String Math
Post by: George McGinn on June 17, 2021, 10:51:24 pm
I read this 3 years ago when I came up with my division routine. It isn't exactly like this, but I got my idea from this paper.

@jack (Jack - I haven't forgotten about you, it is just that my finished division code isn't in the program file I have, and I am waiting to see if my friend has a copy. Either way I will send it to you tomorrow. If I have to rewrite the division code, I will at least send you the write up on how I coded it).


hi bplus
I found this interesting document MULTIPLE-LENGTH DIVISION REVISITED (https://surface.syr.edu/cgi/viewcontent.cgi?article=1162&context=eecs_techreports)
Title: Re: String Math
Post by: jack on June 17, 2021, 11:02:08 pm
thank you George
Title: Re: String Math
Post by: jack on June 21, 2021, 10:12:19 am
Hi bplus
I am still trying to get motivated to study division but as soon as I spend 1 minute reading I loose interest
anyway, in my decfloat routines I found to my disgust that if you take the reciprocal of a number consisting of a lot of 9's and then take the reciprocal of the resulting number, the convergence is very slow, for example
n$="99999999999999999999999999999999999999999999999999"
the inverse of that number is ".0000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000001"
in sci notation it's 1.00000000000000000000000000000000000000000000000001 E-50
trying to do a reciprocal on that number to get back the original is not easy as there's no way to get a good first approximation, the Newon-Raphson method will eventually converge but it takes many iterations depending on the precision used
I tried it on your math-regulator and it returns .0000000.... instead of the original number
bplus, please explain how you handle big numbers in your nInverse function
Title: Re: String Math
Post by: bplus on June 21, 2021, 05:47:21 pm
Oh jack did you find another bug!? I will check it out when I get time.
Title: Re: String Math
Post by: jack on June 21, 2021, 07:35:12 pm
bplus, there was an old-timer that wrote a bignum package in visual basic 3 or there about, his website has vanished but you can still get his programs from the wayback machine https://web.archive.org/web/20200220020034/http://www.rain.org/~mkummel/tbvault.html
the package to get are bnc and bignum, there may be things in his code to learn from, who knows?
Title: Re: String Math
Post by: jack on June 21, 2021, 08:19:16 pm
hi bplus
I extracted the routines needed to do division from bignum, and it actually works
have not cleaned it up for unnecessary constants or variables
Code: QB64: [Select]
  1. 'BIGNUM.BAS v0.n
  2. 'Sep-Dec 1996 by Marc Kummel aka Treebeard.
  3. 'Contact mkummel@rain.org, http://www.rain.org/~mkummel/
  4. ' https://web.archive.org/web/20200220020034/http://www.rain.org/~mkummel/tbvault.html
  5.  
  6. 'Big number arithmetic routines, done with a crypto project in mind.  It's
  7. 'an engaging task to work out these algorithms in BASIC, since we know them
  8. 'so well on paper.  My aim is to minimize string space; then speed.  Not
  9. 'quite "arbitrary precision" since numbers and answer must fit in BASIC
  10. 'string space, 64K with PDS far strings, much less for QBasic.  Don't use
  11. 'the same string twice in one call!  bMul(s$,s$,s$) should figure s=s*s, but
  12. 'it won't work because passed strings are modified (and restored) during the
  13. 'call, so use temporary strings or pass by value.  See bModPower() for an
  14. 'example.  Can be much optimized, but it works and makes sense.
  15. '
  16. '---------------------------------------------------------------------------
  17.  
  18. 'useful globals
  19. Const false = 0, true = Not false, nada = -1
  20. Const zero$ = "0", one$ = "1", two$ = "2", three$ = "3"
  21. Const four$ = "4", five$ = "5"
  22. Const dp$ = ".", neg$ = "-", asc0 = 48
  23. Const basechr$ = "B", basesep$ = "," 'number bases
  24. Const negative = -1, positive = 1 'returned by bComp()
  25. Const maxlongdig = 8 'max digits in long var&
  26. Const maxsegment = 65536 'memory segment size
  27. Const memget = 0, memput = 1, memclr = 2 'memory operations
  28. Const maxstack = 10 'stack depth
  29. Const maxmem = 14 'memory slots (0-9 + constants)
  30. Const pimem = 10, pi2mem = 11, emem = 12 'pi, 2pi, e
  31. Const ln10mem = 13, ln2mem = 14 'natural logs of 10 & 2
  32.  
  33. 'useful shared stuff
  34. Dim Shared digits%
  35. Dim Shared topline%, botline%, topview%, botview%
  36. Dim Shared esc$, null$, error$, abort$
  37.  
  38. 'Trick to give bmem$() its own 64k segment in PDS (but not QB).
  39. 'Watch out for overflows or conflicts with multiple modules.
  40. Common bmem$(), zmem$(), cname$()
  41. Dim bmem$(maxmem), zmem$(1 To maxstack), cname$(10 To maxmem)
  42.  
  43. esc$ = Chr$(27) 'useful strings
  44. null$ = ""
  45. error$ = "error! " 'unhelpful error messages
  46. abort$ = "<abort>"
  47. op$ = "+" 'default action
  48. digits% = 100 'digits for division etc
  49. bignumpath$ = null$ 'path for files (or current dir if null)
  50. prmcntfile$ = "BNPRMCNT.DAT" 'prime count table
  51. logfile$ = "BNLOG.LOG" 'log file
  52. ReDim prmcnt&(0) 'stub for prime count table
  53.  
  54. 'useful constants
  55. cname$(pimem) = "ã": bmem$(pimem) = "3.14159265358979323846264338327"
  56. cname$(pi2mem) = "2ã": bmem$(pi2mem) = "6.28318530717958647692528676654"
  57. cname$(emem) = "e": bmem$(emem) = "2.71828182845904523536028747135"
  58. cname$(ln10mem) = "ln(10)": bmem$(ln10mem) = "2.30258509299404568401799145468"
  59. cname$(ln2mem) = "ln(2)": bmem$(ln2mem) = ".693147180559945309417232121458"
  60.  
  61. 'screen size and window
  62. topline% = 1
  63. botline% = 25
  64. topview% = topline%
  65. botview% = botline% - 2
  66.  
  67. 'screen buffer for ScreenSave() and ScreenRestore()
  68. screenbufsize% = 80 * botline% * 2 'screen size, 2 bytes per character
  69. Dim screenbuf%(screenbufsize% - 1) 'buffer
  70. screenrow% = 1 'cursor row
  71. Def Seg = 0 'video segment for mono or color
  72. If Peek(&H463) = &HB4 Then screenseg& = &HB000 Else screenseg& = &HB800 'color
  73.  
  74. 'trap ctrl-esc for emergency exit.  Speed and size hog, but useful.
  75. KEY 15, Chr$(4) + Chr$(1)
  76. 'On Key(15) GoSub EventTrap
  77. Key(15) On
  78.  
  79. 'start up
  80. View Print topview% To botview% 'text window
  81. Randomize Timer 'I'll make a better RND() sometime
  82. ' LoadPrimeTable                       'prime count table
  83. ' ShowHelp                             'something to look at
  84.  
  85. '========================================================================
  86. n$ = "1"
  87. m$ = "99999999999999999999999999999999999999999999999999"
  88.  
  89. r$ = ""
  90. Call bDiv(n$, m$, r$)
  91. m$ = r$ ' you can't use the destination variable in the source
  92. Call bDiv(n$, m$, r$)
  93. '========================================================================
  94.  
  95.  
  96. 's = |s|
  97. '
  98. Sub bAbs (s$)
  99.     If Left$(s$, 1) = neg$ Then s$ = Mid$(s$, 2)
  100.  
  101. 'return true if s is negative
  102. '
  103. Function bIsNeg% (s$)
  104.     bIsNeg% = (Left$(s$, 1) = neg$)
  105.  
  106. 'return sign of number (-1 or +1)
  107. '
  108. Function bSign% (s$)
  109.     If bIsNeg(s$) Then bSign% = negative Else bSign% = positive
  110.  
  111. 'Strip leading 0s and final "." (but leave something)
  112. '
  113. Sub bStripZero (s$)
  114.     n% = 1
  115.     Do While Mid$(s$, n%, 1) = zero$
  116.         n% = n% + 1
  117.     Loop
  118.     If n% > 1 Then s$ = Mid$(s$, n%)
  119.     If Right$(s$, 1) = dp$ Then s$ = Left$(s$, Len(s$) - 1)
  120.     If Len(s$) = 0 Then s$ = zero$
  121.  
  122. 'Strip trailing 0s to "." (but leave something)
  123. '
  124. Sub bStripTail (s$)
  125.     n% = Len(s$)
  126.     Do While Mid$(s$, n%, 1) = zero$
  127.         n% = n% - 1
  128.         If n% <= 1 Then Exit Do
  129.     Loop
  130.     If n% Then If Mid$(s$, n%, 1) = dp$ Then n% = n% - 1
  131.     s$ = Left$(s$, n%)
  132.     If Len(s$) = 0 Then s$ = zero$
  133.  
  134. 'Strip s$ to whole number and base 10 integer logarithm and sign.  Decimal
  135. 'point is implied after the first digit, and slog% counts places left or
  136. 'right.  bLogPut() reverses the process, and bLogDp() gives info on the
  137. 'decimals. Tricky, but it works and simplifies dividing and multipling.
  138. 'eg s$ -> s$ , slog%
  139. '  660 -> 66 ,  2     (6.6 * 10^ 2)    (or 660,2 if zeroflag%=false)
  140. '  6.6 -> 66 ,  0     (6.6 * 10^ 0)
  141. ' .066 -> 66 , -2     (6.6 * 10^-2)
  142. 'bDiv(), bMul(), and bSqr() use this to trim unnecessary zeros and to locate
  143. 'decimal point.  These set zeroflag% to trim trailing zeros, but bDivIntMod()
  144. 'must set it false in order to figure remainder of division.  A kludge.
  145. '
  146. Sub bLogGet (s$, slog%, sign%, zeroflag%)
  147.     If Left$(s$, 1) = neg$ Then s$ = Mid$(s$, 2): sign% = negative Else sign% = positive
  148.     bStripZero s$
  149.     dpt% = InStr(s$, dp$)
  150.     Select Case dpt%
  151.         Case 0
  152.             slog% = Len(s$) - 1
  153.         Case 1
  154.             n% = dpt% + 1
  155.             Do While Mid$(s$, n%, 1) = zero$
  156.                 n% = n% + 1
  157.             Loop
  158.             s$ = Mid$(s$, n%)
  159.             slog% = dpt% - n%
  160.         Case Else
  161.             s$ = Left$(s$, dpt% - 1) + Mid$(s$, dpt% + 1)
  162.             slog% = dpt% - 2
  163.     End Select
  164.  
  165.     'remove trailing 0's if zeroflag%
  166.     If zeroflag% Then bStripTail s$
  167.  
  168.  
  169. 'Strip a number to "standard form" with no leading or trailing 0s and no
  170. 'final "."  All routines should return all arguments in this form.
  171. '
  172. Sub bClean (s$)
  173.     If Left$(s$, 1) = neg$ Then s$ = Mid$(s$, 2): sign% = true
  174.     bStripZero s$
  175.     If InStr(s$, dp$) Then bStripTail s$
  176.     If sign% And s$ <> zero$ Then s$ = neg$ + s$
  177.  
  178. 'Restore a number from the integer and log figured in bLogGet(). s$ is taken
  179. 'as a number with the decimal after first digit, and decimal is moved slog%
  180. 'places left or right, adding 0s as required. Called by bDiv() and bMul().
  181. '
  182. Sub bLogPut (s$, slog%, sign%)
  183.     last% = Len(s$)
  184.     If Len(s$) = 0 Or s$ = zero$ Then
  185.         s$ = zero$
  186.     ElseIf slog% < 0 Then
  187.         s$ = dp$ + String$(-slog% - 1, zero$) + s$
  188.     ElseIf slog% > last% - 1 Then
  189.         s$ = s$ + String$(slog% - last% + 1, zero$) + dp$
  190.     Else
  191.         s$ = Left$(s$, slog% + 1) + dp$ + Mid$(s$, slog% + 2)
  192.     End If
  193.     bClean s$
  194.     If sign% = negative Then s$ = neg$ + s$
  195.  
  196. 'shift decimal n% digits (minus=left), i.e multiply/divide by 10.
  197. '
  198. Sub bShift (s$, n%)
  199.     bLogGet s$, slog%, sign%, false
  200.     bLogPut s$, slog% + n%, sign%
  201.  
  202. 's = -s
  203. '
  204. Sub bNeg (s$)
  205.     If Left$(s$, 1) = neg$ Then s$ = Mid$(s$, 2) Else s$ = neg$ + s$
  206.  
  207. 'Take whole number and log from bLogGet() and return number of decimal
  208. 'places in the expanded number; OR take string and number of decimal points
  209. 'desired and return the log.  It works both ways.
  210. '
  211. Function bLogDp% (s$, logdp%)
  212.     bLogDp% = Len(s$) - 1 - logdp%
  213.  
  214. 'out = s1 / s2 using fast long-integer algorithm. s2$ must be <= 8 digits.
  215. 's1$ and s2$ must be stripped first, no decimals.
  216. '
  217. Sub bDivLong (s1$, s2$, quotient$, remainder$)
  218.     quotient$ = null$
  219.     remainder& = 0
  220.     divisor& = Val(s2$)
  221.  
  222.     For i% = 1 To digits%
  223.         dividend& = remainder& * 10& + Val(Mid$(s1$, i%, 1))
  224.         dig% = dividend& \ divisor&
  225.         quotient$ = quotient$ + Chr$(asc0 + dig%)
  226.         remainder& = dividend& - dig% * divisor&
  227.     Next i%
  228.  
  229.     If Len(quotient$) = 0 Then quotient$ = zero$
  230.     remainder$ = LTrim$(Str$(remainder&))
  231.  
  232.  
  233. 'out = s1 / s2 using character algorithm, digit by digit, slow but honest.
  234. 's1$ and s2$ must be stripped first, no decimals.
  235. '
  236. Sub bDivChar (s1$, s2$, quotient$, remainder$)
  237.     last1% = Len(s1$) 'length of the dividend
  238.     last2% = Len(s2$) 'length of the divisor
  239.     quotient$ = null$
  240.     remainder$ = null$
  241.  
  242.     For i% = 1 To digits%
  243.         'get next digit of dividend or zero$ if past end
  244.         If i% <= last1% Then
  245.             dvd$ = remainder$ + Mid$(s1$, i%, 1)
  246.         Else
  247.             dvd$ = remainder$ + zero$
  248.         End If
  249.  
  250.         'if dividend < divisor then digit%=0 else have to calculate it.
  251.         'do fast compare using string operations. see bComp%()
  252.         bStripZero dvd$
  253.         ldvd% = Len(dvd$)
  254.         If (ldvd% < last2%) Or ((ldvd% = last2%) And (dvd$ < s2$)) Then
  255.             'divisor is bigger, so digit is 0, easy!
  256.             dig% = 0
  257.             remainder$ = dvd$
  258.  
  259.         Else
  260.             'dividend is bigger, but no more than 9 times bigger.
  261.             'subtract divisor until we get remainder less than divisor.
  262.             'time hog, average is 5 tries through j% loop.  There's a better way.
  263.             For dig% = 1 To 9
  264.                 remainder$ = null$
  265.                 borrow% = 0
  266.                 For j% = 0 To ldvd% - 1
  267.                     n% = last2% - j%
  268.                     If n% < 1 Then n% = 0 Else n% = Val(Mid$(s2$, n%, 1))
  269.                     n% = Val(Mid$(dvd$, ldvd% - j%, 1)) - n% - borrow%
  270.                     If n% >= 0 Then borrow% = 0 Else borrow% = 1: n% = n% + 10
  271.                     remainder$ = Chr$(asc0 + n%) + remainder$
  272.                 Next j%
  273.  
  274.                 'if remainder < divisor then exit
  275.                 bStripZero remainder$
  276.                 lrem% = Len(remainder$)
  277.                 If (lrem% < last2%) Or ((lrem% = last2%) And (remainder$ < s2$)) Then Exit For
  278.  
  279.                 dvd$ = remainder$
  280.                 ldvd% = Len(dvd$)
  281.             Next dig%
  282.  
  283.         End If
  284.         quotient$ = quotient$ + Chr$(asc0 + dig%)
  285.     Next i%
  286.  
  287.  
  288. 'out = s1 / s2
  289. '
  290. Sub bDiv (s1$, s2$, out$)
  291.  
  292.     'strip divisor
  293.     t$ = s2$
  294.     bLogGet t$, slog2%, sign2%, true
  295.  
  296.     'divide by zero?
  297.     If t$ = zero$ Then
  298.         out$ = error$
  299.  
  300.         'do powers of 10 with shifts
  301.     ElseIf t$ = one$ Then
  302.         out$ = s1$
  303.         sign1% = bSign(out$)
  304.         If sign1% = negative Then bAbs out$
  305.         bShift out$, -slog2%
  306.         If sign1% <> sign2% Then bNeg out$
  307.  
  308.         'the hard way
  309.     Else
  310.         'strip all
  311.         s2$ = t$: t$ = null$
  312.         bLogGet s1$, slog1%, sign1%, true
  313.  
  314.         'figure decimal point and sign of answer
  315.         outlog% = slog1% + bLogDp(s2$, slog2%)
  316.         If sign1% <> sign2% Then outsign% = negative Else outsign% = positive
  317.  
  318.         'bump digits past leading zeros and always show whole quotient
  319.         olddigits% = digits%
  320.         digits% = digits% + Len(s2$)
  321.         If digits% < outlog% + 1 Then digits% = outlog% + 1
  322.  
  323.         'do it, ignore remainder
  324.         If Len(s2$) <= maxlongdig Then bDivLong s1$, s2$, out$, t$ Else bDivChar s1$, s2$, out$, t$
  325.  
  326.         'clean up
  327.         bLogPut out$, outlog%, outsign%
  328.         bLogPut s1$, slog1%, sign1%
  329.         bLogPut s2$, slog2%, sign2%
  330.         digits% = olddigits%
  331.     End If
  332.  
  333.  
Title: Re: String Math
Post by: bplus 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.  

 


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.
Title: Re: String Math
Post by: jack 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
Title: Re: String Math
Post by: bplus 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.
Title: Re: String Math
Post by: bplus 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.