Author Topic: Yuck String Math or What's wrong with FOO?  (Read 12470 times)

0 Members and 1 Guest are viewing this topic.

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Yuck String Math or What's wrong with FOO?
« on: February 23, 2019, 04:35:51 pm »
In response to Steve's "Yuck string math.." comment in thread: https://www.qb64.org/forum/index.php?topic=1091.msg102860#msg102860

Well, yes, it is messy. This would be my approach, and note this example only handles addition and  subtraction of positive or negative integers, at least for now.

I'm thinking if a few people are interested, it might be nice to have a way to calculate numbers for programs added to the new tool box section. It doesn't have to be this method, there are other ways, maybe better ones. Anyway, have a look and if anyone else has played around with ways to get your programs to convert counting numbers to base 10, please post.

Code: QB64: [Select]
  1. INPUT "Input first number:"; a
  2. a$ = LTRIM$(STR$(a))
  3. PRINT "Press plus or minus: ";
  4.     operator$ = INKEY$
  5.     IF LEN(operator$) THEN
  6.         IF operator$ = CHR$(27) THEN SYSTEM
  7.         IF operator$ = "+" OR operator$ = "=" OR operator$ = "-" THEN EXIT DO
  8.     END IF
  9. IF operator$ = "=" THEN operator$ = "+"
  10. PRINT operator$
  11. INPUT "Input second number:"; a
  12. b$ = LTRIM$(STR$(a))
  13. origa$ = a$: origb$ = b$: origoperator$ = operator$ ' Testing purposes only.
  14.  
  15. IF LEFT$(a$, 1) = "-" THEN sign1$ = "-" ELSE sign1$ = "+"
  16. IF LEFT$(b$, 1) = "-" THEN sign2$ = "-" ELSE sign2$ = "+"
  17.  
  18. sign = 0
  19. SELECT CASE sign1$ + operator$ + sign2$
  20.     CASE "+++", "+--"
  21.         operator$ = "+"
  22.         IF LEFT$(b$, 1) = "-" THEN b$ = MID$(b$, 2)
  23.     CASE "++-", "+-+"
  24.         operator$ = "-"
  25.         IF LEFT$(b$, 1) = "-" THEN b$ = MID$(b$, 2)
  26.         IF VAL(b$) > VAL(a$) THEN SWAP a$, b$: sign = -1
  27.     CASE "---", "-++"
  28.         operator$ = "-"
  29.         IF LEFT$(a$, 1) = "-" THEN a$ = MID$(a$, 2)
  30.         IF LEFT$(b$, 1) = "-" THEN b$ = MID$(b$, 2)
  31.         IF VAL(b$) > VAL(a$) THEN SWAP a$, b$ ELSE sign = -1
  32.     CASE "--+", "-+-"
  33.         operator$ = "+"
  34.         IF LEFT$(a$, 1) = "-" THEN a$ = MID$(a$, 2)
  35.         IF LEFT$(b$, 1) = "-" THEN b$ = MID$(b$, 2)
  36.         sign = -1
  37.  
  38. IF LEN(a$) > LEN(b$) THEN
  39.     b$ = STRING$(LEN(a$) - LEN(b$), "0") + b$
  40. ELSEIF LEN(a$) < LEN(b$) THEN
  41.     a$ = STRING$(LEN(b$) - LEN(a$), "0") + a$
  42. x1$ = ""
  43.  
  44. SELECT CASE operator$
  45.     CASE "+", "="
  46.         FOR i = LEN(a$) TO 1 STEP -1
  47.             x1 = VAL(MID$(a$, i, 1)) + VAL(MID$(b$, i, 1)) + carry
  48.             IF x1 > 9 THEN x1 = x1 - 10: carry = 1 ELSE carry = 0
  49.             x1$ = LTRIM$(STR$(x1)) + x1$
  50.         NEXT
  51.         IF carry THEN x1$ = "1" + x1$: carry = 0
  52.     CASE "-"
  53.         FOR i = LEN(a$) TO 1 STEP -1
  54.             x1 = VAL(MID$(a$, i, 1)) - VAL(MID$(b$, i, 1)) + carry
  55.             IF x1 < 0 THEN x1 = x1 + 10: carry = -1 ELSE carry = 0
  56.             x1$ = LTRIM$(STR$(x1)) + x1$
  57.         NEXT
  58.         DO UNTIL LEFT$(x1$, 1) <> "0" ' Remove leading zeros.
  59.             x1$ = MID$(x1$, 2)
  60.         LOOP
  61.         IF x1$ = "" THEN x1$ = "0": sign = 0
  62.         IF carry THEN x1$ = "-" + x1$: carry = 0
  63.  
  64. IF sign THEN
  65.     IF LEFT$(x1$, 1) = "-" THEN x1$ = MID$(x1$, 2) ELSE x1$ = "-" + x1$
  66.  
  67. PRINT " String total = "; x1$
  68.  
  69. SELECT CASE origoperator$ ' Testing purposes only.
  70.     CASE "+"
  71.         PRINT "Numeric total = "; LTRIM$(STR$(VAL(origa$) + VAL(origb$)))
  72.     CASE "-"
  73.         PRINT "Numeric total = "; LTRIM$(STR$(VAL(origa$) - VAL(origb$)))
  74.  
  75. PRINT "------------------------------"

Here is the above placed in a subroutine. It can be used as a progressive integer addition / subtraction calculator. Huge numbers welcome!

Code: QB64: [Select]
  1.     INPUT "Input number:"; b$
  2.     CALL stringcalc(a$, operator$, b$)
  3.     IF operator$ <> "" THEN
  4.         PRINT "____________________"
  5.         PRINT "Sub Total = "; a$
  6.     END IF
  7.     PRINT "Plus or minus: ";
  8.     DO
  9.         operator$ = INKEY$
  10.         IF LEN(operator$) THEN
  11.             IF operator$ = CHR$(27) THEN SYSTEM
  12.             IF operator$ = "+" OR operator$ = "=" OR operator$ = "-" THEN EXIT DO
  13.         END IF
  14.     LOOP
  15.     IF operator$ = "=" THEN operator$ = "+"
  16.     PRINT operator$
  17.  
  18. SUB stringcalc (a$, operator$, b$)
  19. IF a$ <> "" AND operator$ <> "" AND b$ <> "" THEN
  20.     IF LEFT$(a$, 1) = "-" THEN sign1$ = "-" ELSE sign1$ = "+"
  21.     IF LEFT$(b$, 1) = "-" THEN sign2$ = "-" ELSE sign2$ = "+"
  22.  
  23.     sign = 0
  24.     SELECT CASE sign1$ + operator$ + sign2$
  25.         CASE "+++", "+--"
  26.             operator$ = "+"
  27.             IF LEFT$(b$, 1) = "-" THEN b$ = MID$(b$, 2)
  28.         CASE "++-", "+-+"
  29.             operator$ = "-"
  30.             IF LEFT$(b$, 1) = "-" THEN b$ = MID$(b$, 2)
  31.             IF VAL(b$) > VAL(a$) THEN SWAP a$, b$: sign = -1
  32.         CASE "---", "-++"
  33.             operator$ = "-"
  34.             IF LEFT$(a$, 1) = "-" THEN a$ = MID$(a$, 2)
  35.             IF LEFT$(b$, 1) = "-" THEN b$ = MID$(b$, 2)
  36.             IF VAL(b$) > VAL(a$) THEN SWAP a$, b$ ELSE sign = -1
  37.         CASE "--+", "-+-"
  38.             operator$ = "+"
  39.             IF LEFT$(a$, 1) = "-" THEN a$ = MID$(a$, 2)
  40.             IF LEFT$(b$, 1) = "-" THEN b$ = MID$(b$, 2)
  41.             sign = -1
  42.     END SELECT
  43.  
  44.     IF LEN(a$) > LEN(b$) THEN
  45.         b$ = STRING$(LEN(a$) - LEN(b$), "0") + b$
  46.     ELSEIF LEN(a$) < LEN(b$) THEN
  47.         a$ = STRING$(LEN(b$) - LEN(a$), "0") + a$
  48.     END IF
  49.     x1$ = ""
  50.  
  51.     SELECT CASE operator$
  52.         CASE "+", "="
  53.             FOR i = LEN(a$) TO 1 STEP -1
  54.                 x1 = VAL(MID$(a$, i, 1)) + VAL(MID$(b$, i, 1)) + carry
  55.                 IF x1 > 9 THEN x1 = x1 - 10: carry = 1 ELSE carry = 0
  56.                 x1$ = LTRIM$(STR$(x1)) + x1$
  57.             NEXT
  58.             IF carry THEN x1$ = "1" + x1$: carry = 0
  59.         CASE "-"
  60.             FOR i = LEN(a$) TO 1 STEP -1
  61.                 x1 = VAL(MID$(a$, i, 1)) - VAL(MID$(b$, i, 1)) + carry
  62.                 IF x1 < 0 THEN x1 = x1 + 10: carry = -1 ELSE carry = 0
  63.                 x1$ = LTRIM$(STR$(x1)) + x1$
  64.             NEXT
  65.             DO UNTIL LEFT$(x1$, 1) <> "0" ' Remove leading zeros.
  66.                 x1$ = MID$(x1$, 2)
  67.             LOOP
  68.             IF x1$ = "" THEN x1$ = "0": sign = 0
  69.             IF carry THEN x1$ = "-" + x1$: carry = 0
  70.     END SELECT
  71.  
  72.     IF sign THEN
  73.         IF LEFT$(x1$, 1) = "-" THEN x1$ = MID$(x1$, 2) ELSE x1$ = "-" + x1$
  74.     END IF
  75.     a$ = x1$
  76.     a$ = b$


Pete
« Last Edit: February 23, 2019, 09:48:20 pm by Pete »
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Yuck String Math or What's wrong with FOO?
« Reply #1 on: February 24, 2019, 01:41:23 pm »
Steve might have said "Yuck" to string math but he had a pretty good working set at Walter's forum, if I am recalling correctly.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Yuck String Math or What's wrong with FOO?
« Reply #2 on: February 24, 2019, 03:36:12 pm »
I do.  I’ll see about digging it up and sharing sometime soonish.  ;)

As a rule, I don’t like string math simply for the speed (sloooow), and I don’t have any real need for it usually.  LONGs get the majority of my work done, with _FLOATS used only when necessary (and even less often, SINGLE or DOUBLE), and string math is just a novelty needed by others much more than by me.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Yuck String Math or What's wrong with FOO?
« Reply #3 on: February 25, 2019, 11:54:03 am »
Well I think I'm at the "Yuck" part. Division. I finished addition, subtraction, and multiplication for positive and negative integers and decimal numbers, but get the steps needed for friggin' string division...

' 1) Add 20 zeros more than the divisor to the dividend.
' 2) Make a string the length of the divisor out of the left side of the dividend.
' 3) Compare cut dividend to divisor. If larger, go to next step. If smaller, bring next digit down and go onto next step.
' 4) Multiply divisor backwards from 9 until product is the same or smaller than cut dividend then save that number to a new string variable.
' 5) Subtract.
' 6) Bring down next digit.
' 7) Repeat at step 3.

I'll probably make a numeric pseudo one first, just to be certain my logic pans out. Square root strings follow a somewhat similar pattern. As far as trig functions, multiplying sine and cosine is easy enough, but there are probably some hair raising steps involved in other functions that Bill could bring to light. I'll probably stop at division, and call it a year.

Pete 
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline jack

  • Seasoned Forum Regular
  • Posts: 408
    • View Profile
Re: Yuck String Math or What's wrong with FOO?
« Reply #4 on: February 25, 2019, 03:27:27 pm »
Pete, about division, how about using Newton-Raphson with a double or _float division as the first estimate?
Quote
The steps of Newton–Raphson division are:
Calculate an estimate for the reciprocal of the divisor .
Compute successively more accurate estimates. of the reciprocal. This is where one employs the Newton–Raphson method as such.
Compute the quotient by multiplying the dividend by the reciprocal of the divisor
https://en.wikipedia.org/wiki/Division_algorithm
https://web.stanford.edu/class/ee486/doc/chap5.pdf
« Last Edit: February 25, 2019, 03:38:46 pm by jack »

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Yuck String Math or What's wrong with FOO?
« Reply #5 on: February 25, 2019, 04:25:09 pm »
Hi Jack,

Oh crap, I'm on a plane. I'm glad I didin't type that out loud...

Anyway. Yes, I'll have to look into that as a testing method. It's probably similar to the trick to estimate square roots. For now, I've been banging it out old school. Here is what I have so far, which just works for positive and negative integers. I'll figure out how to divide decimals later. Also, this is NOT a sting function. I don't want all that extra code in the way until I get a working routine. I'll convert it then.

Code: QB64: [Select]
  1. ' Division
  2. ' 1) Compare each digit of dividend to divisor. If dividend is larger, exit loop. If smaller, bring next digit down and loop.
  3. ' 2) Multiply divisor backwards from 9 until product is the same or smaller than cut dividend then save that number to a new string variable.
  4. ' 3) Subtract.
  5. ' 4) Repeat.
  6.  
  7. LINE INPUT "Divisor: "; a$
  8. LINE INPUT "Dividend: "; b$
  9. m1$ = a$
  10. m2$ = b$
  11. IF LEFT$(m1$, 1) = "0" THEN PRINT "Division by zero not allowed.": END
  12. IF LEFT$(m1$, 1) = "-" THEN sign = -1: m1$ = MID$(m1$, 2)
  13. IF LEFT$(m2$, 1) = "-" THEN
  14.     IF sign THEN
  15.         sign = 0
  16.     ELSE
  17.         sign = -1
  18.     END IF
  19.     m2$ = MID$(m2$, 2)
  20.     DO UNTIL LEN(cutd$) > LEN(m1$) OR LEN(cutd$) = LEN(m1$) AND cutd$ >= m1$
  21.         cutd% = cutd% + 1: cutd$ = cutd$ + MID$(m2$, cutd%, 1)
  22.         IF MID$(m2$, cutd%, 1) = "" THEN
  23.             IF LEFT$(cutd$, 1) = "0" THEN divflag% = -1: EXIT DO
  24.             IF INSTR(q$, ".") = 0 THEN
  25.                 q$ = q$ + "."
  26.             ELSE
  27.                 IF LEN(MID$(q$, INSTR(q$, "."))) > 3 THEN divflag% = -2: EXIT DO
  28.                 q$ = q$ + "0"
  29.             END IF
  30.             cutd$ = cutd$ + "0" ' No more digits to bring down.
  31.         END IF
  32.     LOOP
  33.     IF divflag% THEN divflag% = 0: EXIT DO
  34.     FOR i = 9 TO 1 STEP -1
  35.         IF i * VAL(a$) <= VAL(cutd$) THEN EXIT FOR
  36.     NEXT
  37.     x1$ = LTRIM$(STR$(VAL(LTRIM$(STR$(i))) * VAL(m1$)))
  38.     q$ = q$ + LTRIM$(STR$(i))
  39.     cutd$ = LTRIM$(STR$(VAL(cutd$) - VAL(x1$)))
  40. IF q$ = "" THEN q$ = "0" ' dividend is zero.
  41. IF LEFT$(q$, 1) = "0" THEN sign = 0
  42. IF sign THEN q$ = "-" + q$
  43.  
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Yuck String Math or What's wrong with FOO?
« Reply #6 on: February 25, 2019, 05:43:39 pm »
De Ja Vu, Steve will tell you (unless I did) that you can bring down more than 1 digit at a time.

I remember having to create a routine for >, >= .... for numeric strings to judge if one number string was bigger in numeric value than the other. That's when I hit the yuck level.
« Last Edit: February 25, 2019, 05:47:13 pm by bplus »

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Yuck String Math or What's wrong with FOO?
« Reply #7 on: February 25, 2019, 05:51:02 pm »
Yes, you can have it pull down so the remainder ends up as the number of digits of the divisor, before ever having to evaluate it.

As far as the > < evaluations, simple. If the string length is smaller, it's smaller. If it's the same size, just evaluate the LEFT$(string$, 1) of each string. The larger is the larger. If the string length is larger, it's larger. Of course signs have to be considered in this evaluation, too. It's the opposite for negative, and positives are always larger than negatives.

Pete
« Last Edit: February 25, 2019, 05:54:15 pm by Pete »
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline jack

  • Seasoned Forum Regular
  • Posts: 408
    • View Profile
Re: Yuck String Math or What's wrong with FOO?
« Reply #8 on: February 26, 2019, 11:23:50 am »
here's Visual Basic 3 source for string-bignum called bnc http://www.rain.org/~mkummel/tbvault.html, it may give you some ideas.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Yuck String Math or What's wrong with FOO?
« Reply #9 on: February 26, 2019, 02:53:37 pm »
Yes, you can have it pull down so the remainder ends up as the number of digits of the divisor, before ever having to evaluate it.

As far as the > < evaluations, simple. If the string length is smaller, it's smaller. If it's the same size, just evaluate the LEFT$(string$, 1) of each string. The larger is the larger. If the string length is larger, it's larger. Of course signs have to be considered in this evaluation, too. It's the opposite for negative, and positives are always larger than negatives.

Pete

You pull it down based on the size of your variable...

For instance “1234567890” + “2345678901”....

We can solve this byte by byte:

“0” + “1”...   1, no carry over
“9” + “0” + 0 carry over...  9, no carry over
“8” + “9” + 0 carry over...  7, carry over 1
“7” + “8” + 1 carry over...  6, carry over 1...

And so on...

OR...

We can pull numbers down 2 digits at a time, with the same process:
“90” + “01” = “91”, no carry over
“78” + “89” = “67”, carry over 1

Same process, just pull larger values so we’re not reading a single string character at a time, but instead are reading a value from 0 to 99 instead...

Half the work, twice the speed!

(And If we use INT64 values, we pull down 18 [I think] digits per pass, instead of 1.)

No need to solve byte by byte.  Solve INT64 by INT64 instead.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Yuck String Math or What's wrong with FOO?
« Reply #10 on: February 27, 2019, 01:14:39 am »
Division eats up a lot of lines of code. Decimal placement seems to require four conditions. I'm not sure if I want to try to optimize this more, or just substitute in the string math.

Code: QB64: [Select]
  1. ' Division
  2. ' 1) Compare each digit of dividend to divisor. If dividend is larger, exit loop. If smaller, bring next digit down and loop.
  3. ' 2) Multiply divisor backwards from 9 until product is the same or smaller than cut dividend then save that number to a new string variable.
  4. ' 3) Subtract.
  5. ' 4) Repeat.
  6.  
  7. LINE INPUT "Divisor: "; a$
  8. LINE INPUT "Dividend: "; b$
  9. m1$ = a$
  10. m2$ = b$
  11. IF LEFT$(m1$, 1) = "0" AND LEN(m1$) = 1 THEN PRINT "Division by zero not allowed.": END
  12. IF LEFT$(m1$, 1) = "-" THEN sign = -1: m1$ = MID$(m1$, 2)
  13. IF LEFT$(m2$, 1) = "-" THEN
  14.     IF sign THEN
  15.         sign = 0
  16.     ELSE
  17.         sign = -1
  18.     END IF
  19.     m2$ = MID$(m2$, 2)
  20. IF INSTR(m1$, ".") <> 0 THEN
  21.     DO UNTIL RIGHT$(m1$, 1) <> "0"
  22.         m1$ = MID$(m1$, 1, LEN(m1$) - 1) ' Strip off trailing zeros
  23.     LOOP
  24.     place% = LEN(m1$) - INSTR(m1$, ".")
  25.     m1$ = MID$(m1$, 1, INSTR(m1$, ".") - 1) + MID$(m1$, INSTR(m1$, ".") + 1) ' Strip off decimal point.
  26.     DO UNTIL LEFT$(m1$, 1) <> "0"
  27.         m1$ = MID$(m1$, 2) ' Strip off leading zeros for divisors smaller than .1
  28.     LOOP
  29. IF INSTR(m2$, ".") <> 0 THEN
  30.     m2$ = m2$ + STRING$(place% - LEN(m2$) - INSTR(m2$, "."), "0") ' Add any zeros based on the length of dividend at decimal - length of divisor at decimal. If less than zero, nothing added.
  31.     place2% = INSTR(m2$, ".")
  32.     DO UNTIL RIGHT$(m2$, 1) <> "0"
  33.         m2$ = MID$(m2$, 1, LEN(m2$) - 1) ' Strip off trailing zeros
  34.     LOOP
  35.     m2$ = MID$(m2$, 1, INSTR(m2$, ".") - 1) + MID$(m2$, INSTR(m2$, ".") + 1) ' Strip off decimal point.
  36.     m2$ = m2$ + STRING$(place%, "0") ' Add any zeros based on the length of dividend at decimal - length of divisor at decimal. If less than zero, nothing added.
  37.     place% = 0
  38.     DO
  39.         cutd% = cutd% + 1: cutd$ = cutd$ + MID$(m2$, cutd%, 1)
  40.         IF MID$(m2$, cutd%, 1) = "" THEN
  41.             IF LEFT$(cutd$, 1) = "0" AND LEN(q$) > LEN(m2$) THEN divflag% = -1: EXIT DO
  42.             carry = carry + 1
  43.             IF carry = 1 THEN place3% = cutd% - 1 ' larger whole number divisor smaller whol number dividend.
  44.             IF carry > 20 + LEN(m1$) THEN divflag% = -2: EXIT DO
  45.             cutd$ = cutd$ + "0" ' No more digits to bring down.
  46.         END IF
  47.         IF LEN(cutd$) > LEN(m1$) OR LEN(cutd$) = LEN(m1$) AND cutd$ >= m1$ THEN EXIT DO
  48.         q$ = q$ + "0"
  49.     LOOP
  50.     IF divflag% THEN divflag% = 0: EXIT DO
  51.     FOR i = 9 TO 1 STEP -1
  52.         IF i * VAL(m1$) <= VAL(cutd$) THEN EXIT FOR
  53.     NEXT
  54.     '-------------------------Multiply Function Needed Here.
  55.     x1$ = LTRIM$(STR$(VAL(LTRIM$(STR$(i))) * VAL(m1$)))
  56.     q$ = q$ + LTRIM$(STR$(i))
  57.     '-------------------------Subtraction Function Needed Here.
  58.     cutd$ = LTRIM$(STR$(VAL(cutd$) - VAL(x1$)))
  59. IF place% = 0 AND place2% = 0 THEN place% = place3%
  60. IF place2% THEN place% = place% + place2% - 1
  61. IF q$ = "" THEN q$ = "0": place% = 0 ' dividend is zero.
  62. IF place% OR place2% THEN
  63.     IF place% < LEN(q$) OR place2% THEN
  64.         q$ = MID$(q$, 1, place%) + "." + MID$(q$, place% + 1)
  65.     END IF
  66.     DO UNTIL RIGHT$(q$, 1) <> "0"
  67.         q$ = MID$(q$, 1, LEN(q$) - 1) ' Strip off trailing zeros
  68.     LOOP
  69.     IF RIGHT$(q$, 1) = "." THEN q$ = MID$(q$, 1, LEN(q$) - 1) ' Strip off abandoned decimal.
  70. DO UNTIL LEFT$(q$, 1) <> "0"
  71.     q$ = MID$(q$, 2) ' Strip off leading zeros
  72. IF q$ = "" THEN q$ = "0": sign = 0
  73. IF sign THEN q$ = "-" + q$
  74. PRINT q$, VAL(b$) / VAL(a$)
  75.  
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Yuck String Math or What's wrong with FOO?
« Reply #11 on: February 27, 2019, 10:43:34 am »
Hey guys, did you know this...

So I'm considering what to do about rounding the trailing decimal digit in quotients that need to be truncated. What I discovered is that QB math seems to round everything up. At least it appeared that way, when I tried...

18 / 7

My string math gets:   2.571428571428571428571
Using numeric QB math I get: 2.571428571428572

So let's look at those so my number (top) has the same number of digits as the numeric calculation...

2.571428571428571..........428571
2.571428571428572

Now you can check my number with MS Windows Calculator, which also gives 2.571428571428571, my number truncated to 15 decimal places.

So what should we do here? Do I round a remainder like ...428571 up, to match QB math, or take the math traditional road of rounding anything less than 5 down to zero?

Pete
« Last Edit: February 27, 2019, 11:22:03 am by Pete »
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Yuck String Math or What's wrong with FOO?
« Reply #12 on: February 27, 2019, 11:20:58 am »
What’s the value after the 1?

...5714?  Or ...5715 and higher?
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Yuck String Math or What's wrong with FOO?
« Reply #13 on: February 27, 2019, 11:35:44 am »
428571

Try the Windows calculator with 18 / 7. I'm not sure if the Windows calculator rounds or just truncates, as it won't display past that digit. I was just surprised QB math rounded 428571 up, so instead of the 15 digit ( the number just in front of the 4 of 428571, ending in 1, it rounded it up to 2!

2.571428571428571 Windows calculator and mine up to this number of digits, mine goes a few digits more.
2.571428571428572 QB numeric results from doing VAL("18") / VAL("7")

Edit: Windows Calculator definitely rounds last digit up. Try .2222 / .34 Windows Calculator rounds the last digit it can display up to a 9. On my string math calc function, it is 882352941, which should get rounded up to 9.

Pete

« Last Edit: February 27, 2019, 06:34:53 pm by Pete »
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Yuck String Math or What's wrong with FOO?
« Reply #14 on: February 28, 2019, 07:17:34 pm »
Well, while Steve is playing with his clock, I decided that in my math world, .5 gets rounded up. Here is what I have for division. It incorporates the multiplication and subtraction string math, so, it's a lot of lines.

It's a mess right now. I need to clean up some of the variable names... no, not those kind of names, but a$, b$, etc. just make it easier for me to make the initial framework.

I also need to put together a way I want to call these functions. Anyway, this should divide large positive or negative whole and/or decimal numbers.

Repeated numbers, like 7 / 18 = .3888888 I decided not to round, but get this, QB64 rounds that to ...89 but if we try 2 / 3, QB64 lets it stay as ...66 repeating. Apparently, QB64 only considers it repeating if all digits after the decimal are the same. I'll have to look into that while Steve is playing with his clock. Well, at least I got my "l" key working again. :D

Pete

Code: QB64: [Select]
  1. LINE INPUT "Divisor: "; a$
  2. LINE INPUT "Dividend: "; b$
  3. d1$ = a$
  4. d2$ = b$
  5. IF LEFT$(d1$, 1) = "0" AND LEN(d1$) = 1 THEN PRINT "Division by zero not allowed.": END
  6. IF LEFT$(d1$, 1) = "-" THEN divsign% = -1: d1$ = MID$(d1$, 2)
  7. IF LEFT$(d2$, 1) = "-" THEN
  8.     IF divsign% THEN
  9.         divsign% = 0
  10.     ELSE
  11.         divsign% = -1
  12.     END IF
  13.     d2$ = MID$(d2$, 2)
  14. IF INSTR(d1$, ".") <> 0 THEN
  15.     DO UNTIL RIGHT$(d1$, 1) <> "0"
  16.         d1$ = MID$(d1$, 1, LEN(d1$) - 1) ' Strip off trailing zeros
  17.     LOOP
  18.     divplace% = LEN(d1$) - INSTR(d1$, ".")
  19.     d1$ = MID$(d1$, 1, INSTR(d1$, ".") - 1) + MID$(d1$, INSTR(d1$, ".") + 1) ' Strip off decimal point.
  20.     DO UNTIL LEFT$(d1$, 1) <> "0"
  21.         d1$ = MID$(d1$, 2) ' Strip off leading zeros for divisors smaller than .1
  22.     LOOP
  23.  
  24. IF INSTR(d2$, ".") <> 0 THEN
  25.     d2$ = d2$ + STRING$(divplace% - LEN(d2$) - INSTR(d2$, "."), "0") ' Add any zeros based on the length of dividend at decimal - length of divisor at decimal. If less than zero, nothing added.
  26.     divplace2% = INSTR(d2$, ".")
  27.     DO UNTIL RIGHT$(d2$, 1) <> "0"
  28.         d2$ = MID$(d2$, 1, LEN(d2$) - 1) ' Strip off trailing zeros
  29.     LOOP
  30.     d2$ = MID$(d2$, 1, INSTR(d2$, ".") - 1) + MID$(d2$, INSTR(d2$, ".") + 1) ' Strip off decimal point.
  31.     d2$ = d2$ + STRING$(divplace%, "0") ' Add any zeros based on the length of dividend at decimal - length of divisor at decimal. If less than zero, nothing added.
  32.     divplace% = 0
  33.     DO
  34.         cutd% = cutd% + 1: cutd$ = cutd$ + MID$(d2$, cutd%, 1)
  35.         IF MID$(d2$, cutd%, 1) = "" THEN
  36.             IF cutd$ = STRING$(LEN(cutd$), "0") AND LEN(q$) > LEN(d2$) THEN divflag% = -1: EXIT DO
  37.             divcarry = divcarry + 1
  38.             IF divcarry = 1 THEN divplace3% = cutd% - 1 ' larger whole number divisor smaller whol number dividend.
  39.             IF divcarry > 20 + LEN(d1$) THEN divflag% = -2: EXIT DO
  40.             cutd$ = cutd$ + "0" ' No more digits to bring down.
  41.         END IF
  42.         IF LEN(cutd$) > LEN(d1$) OR LEN(cutd$) = LEN(d1$) AND cutd$ >= d1$ THEN EXIT DO
  43.         q$ = q$ + "0"
  44.     LOOP
  45.     IF divflag% THEN divflag% = 0: EXIT DO
  46.     FOR idiv% = 9 TO 1 STEP -1
  47.         m1$ = LTRIM$(STR$(idiv%)): m2$ = d1$
  48.         product$ = "": GOSUB stringmultiply
  49.         tempcutd$ = cutd$ ' cutd$ can be 00 or other leading zero values.
  50.         DO
  51.             IF LEN(tempcutd$) = 1 THEN EXIT DO
  52.             IF LEFT$(tempcutd$, 1) = "0" THEN
  53.                 tempcutd$ = MID$(tempcutd$, 2)
  54.             ELSE
  55.                 EXIT DO
  56.             END IF
  57.         LOOP
  58.         IF LEN(tempcutd$) > LEN(product$) OR LEN(tempcutd$) = LEN(product$) AND product$ <= tempcutd$ THEN EXIT FOR
  59.     NEXT
  60.     q$ = q$ + LTRIM$(STR$(idiv%))
  61.     m1$ = LTRIM$(STR$(idiv%)): m2$ = d1$
  62.     product$ = "": GOSUB stringmultiply
  63.     operator$ = "-": a$ = cutd$: b$ = product$: GOSUB stringcalc: cutd$ = a$
  64. '''PRINT "Raw q$ = "; q$
  65. IF divplace% = 0 AND divplace2% = 0 THEN divplace% = divplace3%
  66. IF divplace2% THEN divplace% = divplace% + divplace2% - 1
  67. IF q$ = "" THEN divplace% = 0 ' dividend is zero.
  68. IF divplace% OR divplace2% THEN
  69.     tempq$ = MID$(q$, INSTR(q$, ".") + 1)
  70.     IF LEN(tempq$) >= 3 THEN
  71.         IF RIGHT$(tempq$, 3) <> STRING$(3, RIGHT$(tempq$, 1)) THEN
  72.             IF RIGHT$(q$, 1) < "5" THEN
  73.                 q$ = MID$(q$, 1, LEN(q$) - 1)
  74.             ELSE
  75.                 a$ = MID$(q$, 1, LEN(q$))
  76.                 b$ = STRING$(LEN(a$) - 1, "0") + LTRIM$(STR$(10 - VAL(RIGHT$(q$, 1))))
  77.                 operator$ = "+": GOSUB stringcalc
  78.                 q$ = a$
  79.             END IF
  80.         END IF
  81.     END IF
  82.     IF divplace% < LEN(q$) OR divplace2% THEN
  83.         q$ = MID$(q$, 1, divplace%) + "." + MID$(q$, divplace% + 1)
  84.     END IF
  85.     DO UNTIL RIGHT$(q$, 1) <> "0"
  86.         q$ = MID$(q$, 1, LEN(q$) - 1) ' Strip off trailing zeros
  87.     LOOP
  88.     IF RIGHT$(q$, 1) = "." THEN q$ = MID$(q$, 1, LEN(q$) - 1) ' Strip off abandoned decimal.
  89. DO UNTIL LEFT$(q$, 1) <> "0"
  90.     q$ = MID$(q$, 2) ' Strip off leading zeros
  91. IF q$ = "" THEN q$ = "0": divsign% = 0
  92. IF divsign% THEN q$ = "-" + q$
  93. PRINT "Pete's String Division: "; q$
  94. PRINT "QB64 Numeric Division:  "; LTRIM$(STR$(VAL(d2$) / VAL(d1$)))
  95.  
  96. stringmultiply:
  97. multsumcnt% = 0: multcarry% = 0: multplace% = 0: x1$ = ""
  98. IF LEN(b$) > LEN(a$) THEN SWAP m1$, m2$
  99. IF LEFT$(m1$, 1) = "-" THEN m1$ = MID$(m1$, 2): multsign% = -1
  100. IF LEFT$(m2$, 1) = "-" THEN m2$ = MID$(m2$, 2): IF multsign% THEN multsign% = 0 ELSE multsign% = -1
  101. IF INSTR(m1$, ".") <> 0 THEN multplace% = LEN(m1$) - INSTR(m1$, "."): m1$ = MID$(m1$, 1, INSTR(m1$, ".") - 1) + MID$(m1$, INSTR(m1$, ".") + 1)
  102. IF INSTR(m2$, ".") <> 0 THEN multplace% = multplace% + LEN(m2$) - INSTR(m2$, "."): m2$ = MID$(m2$, 1, INSTR(m2$, ".") - 1) + MID$(m2$, INSTR(m2$, ".") + 1)
  103.  
  104. FOR multii% = LEN(m2$) TO 1 STEP -1
  105.     n2$ = MID$(m2$, multii%, 1)
  106.     x1$ = ""
  107.     FOR multjj% = LEN(m1$) TO 1 STEP -1
  108.         n1$ = MID$(m1$, multjj%, 1)
  109.         multkk = VAL(n1$) * VAL(n2$) + multcarry%
  110.         IF multkk > 9 THEN
  111.             multcarry% = VAL(LEFT$(LTRIM$(STR$(multkk)), 1))
  112.             multkk = VAL(RIGHT$(LTRIM$(STR$(multkk)), 1))
  113.         ELSE
  114.             multcarry% = 0
  115.         END IF
  116.         x1$ = LTRIM$(STR$(multkk)) + x1$
  117.     NEXT
  118.     IF multcarry% THEN x1$ = LTRIM$(STR$(multcarry%)) + x1$: multcarry% = 0
  119.     GOSUB multsums
  120. IF multplace% AND product$ <> "0" THEN
  121.     product$ = MID$(product$, 1, LEN(product$) - multplace%) + "." + MID$(product$, LEN(product$) - multplace% + 1)
  122. DO UNTIL LEFT$(product$, 1) <> "0" ' Remove leading zeros.
  123.     product$ = MID$(product$, 2)
  124. IF product$ = "" THEN product$ = "0": multsign% = 0
  125. IF multsign% THEN product$ = "-" + product$
  126. ' Product$ is the multiplication product variable to return.
  127.  
  128. multsums:
  129. IF product$ <> "" THEN
  130.     multsumcnt% = multsumcnt% + 1
  131.     x1$ = x1$ + STRING$(multsumcnt%, "0")
  132.     multhh% = 0
  133.     DO
  134.         IF multhh% < LEN(product$) THEN ms1$ = MID$(product$, LEN(product$) - multhh%, 1) ELSE ms1$ = ""
  135.         IF multhh% < LEN(x1$) THEN ms2$ = MID$(x1$, LEN(x1$) - multhh%, 1) ELSE ms2$ = ""
  136.         IF ms1$ = "" AND ms2$ = "" THEN EXIT DO
  137.         x1 = VAL(ms1$) + VAL(ms2$) + multcarry%
  138.         IF x1 > 9 THEN x1 = x1 - 10: multcarry% = 1 ELSE multcarry% = 0
  139.         xproduct$ = LTRIM$(STR$(x1)) + xproduct$
  140.         multhh% = multhh% + 1
  141.     LOOP
  142.     IF multcarry% THEN product$ = "1" + xproduct$: multcarry% = 0 ELSE product$ = xproduct$
  143.     IF multplace% THEN
  144.         DO UNTIL RIGHT$(product$, 1) <> "0" ' Remove trailing zeros in a decimal sum.
  145.             product$ = MID$(product$, 1, LEN(product$) - 1)
  146.             multplace% = multplace% - 1
  147.         LOOP
  148.     END IF
  149.     xproduct$ = ""
  150.     product$ = x1$: x1$ = ""
  151.  
  152. stringcalc:
  153. IF a$ <> "" AND operator$ <> "" AND b$ <> "" THEN
  154.     IF INSTR(a$, ".") <> 0 THEN ' Evaluate sum for decimal fraction.
  155.         sumplace% = LEN(a$) - INSTR(a$, ".")
  156.         a$ = MID$(a$, 1, INSTR(a$, ".") - 1) + MID$(a$, INSTR(a$, ".") + 1) ' Strip out decimal
  157.     END IF
  158.     IF INSTR(b$, ".") <> 0 THEN ' Evaluate number for decimal fraction.
  159.         numplace% = LEN(b$) - INSTR(b$, ".")
  160.         b$ = MID$(b$, 1, INSTR(b$, ".") - 1) + MID$(b$, INSTR(b$, ".") + 1) ' Strip out decimal
  161.     END IF
  162.     IF sumplace% > numplace% THEN place% = sumplace% ELSE place% = numplace%
  163.     IF sumplace% > place% THEN
  164.         a$ = a$ + STRING$(sumplace% - place%, "0")
  165.     ELSEIF place% > sumplace% THEN
  166.         a$ = a$ + STRING$(place% - sumplace%, "0")
  167.     END IF
  168.     IF numplace% > place% THEN
  169.         b$ = b$ + STRING$(numplace% - place%, "0")
  170.     ELSEIF place% > numplace% THEN
  171.         b$ = b$ + STRING$(place% - numplace%, "0")
  172.     END IF ' END Decimal evaluations........................
  173.  
  174.     IF LEFT$(a$, 1) = "-" THEN sign1$ = "-" ELSE sign1$ = "+"
  175.     IF LEFT$(b$, 1) = "-" THEN sign2$ = "-" ELSE sign2$ = "+"
  176.  
  177.     sign = 0
  178.     SELECT CASE sign1$ + operator$ + sign2$
  179.         CASE "+++", "+--"
  180.             operator$ = "+"
  181.             IF LEFT$(b$, 1) = "-" THEN b$ = MID$(b$, 2)
  182.         CASE "++-", "+-+"
  183.             operator$ = "-"
  184.             IF LEFT$(b$, 1) = "-" THEN b$ = MID$(b$, 2)
  185.             IF VAL(b$) > VAL(a$) THEN SWAP a$, b$: sign = -1
  186.         CASE "---", "-++"
  187.             operator$ = "-"
  188.             IF LEFT$(a$, 1) = "-" THEN a$ = MID$(a$, 2)
  189.             IF LEFT$(b$, 1) = "-" THEN b$ = MID$(b$, 2)
  190.             IF VAL(b$) > VAL(a$) THEN SWAP a$, b$ ELSE sign = -1
  191.         CASE "--+", "-+-"
  192.             operator$ = "+"
  193.             IF LEFT$(a$, 1) = "-" THEN a$ = MID$(a$, 2)
  194.             IF LEFT$(b$, 1) = "-" THEN b$ = MID$(b$, 2)
  195.             sign = -1
  196.     END SELECT
  197.  
  198.     IF LEN(a$) > LEN(b$) THEN
  199.         b$ = STRING$(LEN(a$) - LEN(b$), "0") + b$
  200.     ELSEIF LEN(a$) < LEN(b$) THEN
  201.         a$ = STRING$(LEN(b$) - LEN(a$), "0") + a$
  202.     END IF
  203.     x1$ = ""
  204.  
  205.     SELECT CASE operator$
  206.         CASE "+", "="
  207.             FOR i = LEN(a$) TO 1 STEP -1
  208.                 x1 = VAL(MID$(a$, i, 1)) + VAL(MID$(b$, i, 1)) + carry
  209.                 IF x1 > 9 THEN x1 = x1 - 10: carry = 1 ELSE carry = 0
  210.                 x1$ = LTRIM$(STR$(x1)) + x1$
  211.             NEXT
  212.             IF carry THEN x1$ = "1" + x1$: carry = 0
  213.             GOSUB replacedecimal
  214.         CASE "-"
  215.             FOR i = LEN(a$) TO 1 STEP -1
  216.                 x1 = VAL(MID$(a$, i, 1)) - VAL(MID$(b$, i, 1)) + carry
  217.                 IF x1 < 0 THEN x1 = x1 + 10: carry = -1 ELSE carry = 0
  218.                 x1$ = LTRIM$(STR$(x1)) + x1$
  219.             NEXT
  220.             IF x1$ <> "" AND x1$ <> STRING$(LEN(x1$), "0") THEN GOSUB replacedecimal
  221.             DO UNTIL LEFT$(x1$, 1) <> "0" ' Remove leading zeros.
  222.                 x1$ = MID$(x1$, 2)
  223.             LOOP
  224.             IF x1$ = "" THEN
  225.                 x1$ = "0": sign = 0
  226.             ELSE
  227.                 IF carry THEN x1$ = "-" + x1$: carry = 0
  228.             END IF
  229.     END SELECT
  230.  
  231.     IF sign THEN
  232.         IF LEFT$(x1$, 1) = "-" THEN x1$ = MID$(x1$, 2) ELSE x1$ = "-" + x1$
  233.     END IF
  234.     a$ = x1$
  235.     a$ = b$
  236.  
  237. replacedecimal:
  238. IF place% THEN
  239.     x1$ = STRING$(place% - LEN(x1$), "0") + x1$
  240.     DO UNTIL RIGHT$(x1$, 1) <> "0" ' Remove trailing zeros in a decimal sum.
  241.         x1$ = MID$(x1$, 1, LEN(x1$) - 1)
  242.         place% = place% - 1
  243.     LOOP
  244.     IF place% > 0 THEN ' Do not replace decimal if total has become an integer amount.
  245.         x1$ = MID$(x1$, 1, LEN(x1$) - place%) + "." + MID$(x1$, LEN(x1$) - place% + 1)
  246.     END IF
  247.  

Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/