Author Topic: String Math  (Read 18649 times)

0 Members and 1 Guest are viewing this topic.

This topic contains a post which is marked as Best Answer. Press here if you would like to see it.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
String Math
« 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.


« Last Edit: August 14, 2020, 05:11:16 pm by bplus »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: String Math
« Reply #1 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.
« Last Edit: August 14, 2020, 07:10:49 pm by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: String Math
« Reply #2 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.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: String Math
« Reply #3 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?

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: String Math
« Reply #4 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
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: String Math
« Reply #5 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.

 
val limit.PNG


AT 10 digits VAL() turns the 9's to gook!
« Last Edit: August 14, 2020, 10:37:12 pm by bplus »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: String Math
« Reply #6 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
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: String Math
« Reply #7 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:
 
new val limit.PNG
« Last Edit: August 14, 2020, 10:53:36 pm by bplus »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: String Math
« Reply #8 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
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: String Math
« Reply #9 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.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: String Math
« Reply #10 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:
 
new val limit.PNG


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. :)
« Last Edit: August 14, 2020, 11:04:40 pm by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: String Math
« Reply #11 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.

« Last Edit: August 15, 2020, 12:01:32 am by bplus »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: String Math
« Reply #12 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.)
« Last Edit: August 15, 2020, 12:17:01 am by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: String Math
« Reply #13 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.
« Last Edit: August 15, 2020, 02:07:49 pm by bplus »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: String Math
« Reply #14 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$)
« Last Edit: August 15, 2020, 03:54:03 pm by bplus »