QB64.org Forum

Active Forums => QB64 Discussion => Topic started by: Pete on February 23, 2019, 04:35:51 pm

Title: Yuck String Math or What's wrong with FOO?
Post by: Pete 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
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: bplus 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.
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: SMcNeill 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.  ;)
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Pete 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 
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: jack 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
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Pete 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.  
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: bplus 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.
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Pete 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
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: jack 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.
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: SMcNeill 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.
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Pete 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.  
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Pete 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
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: SMcNeill on February 27, 2019, 11:20:58 am
What’s the value after the 1?

...5714?  Or ...5715 and higher?
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Pete 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

Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Pete 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.  

Title: Re: Yuck String Math or What's wrong with FOO?
Post by: jack on March 01, 2019, 08:44:17 am
hello Pete
just tried printing the fractions you mentioned in your favorite language, FreeBasic, and the results are rounded as you mentioned
then I looked at the hexadecimal representation of the numbers and it makes sense that the numbers are rounded the way they are
for example, try printing 2/3 in single precision, you will get 0.6666667
the hex representation for 2/3 is 3F2AAAAB single precision and 3FE5555555555555 double precision
you can try it yourself with the online IEEE-754 Calculator at https://babbage.cs.qc.cuny.edu/IEEE-754/
you can enter fractions in the input box and it will be evaluated, for example 18/7 or 2/3
btw, the online calculator uses javascript to do the calculations, you can save the web page to your hard drive and use it offline.
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Pete on March 01, 2019, 11:38:13 am
My favorite language, F'Basic. :D

I need to check a few sources, like the ones you posted, and I also need to decide if I want just digits, or notation like take D-8, etc.

Well here is what I have so far, It can be used as a calculator for +-*/ of positive and negative whole numbers and decimals. It has not been well tested and I would still like to clean up some variable names.

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

Pete
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: jack on March 01, 2019, 01:50:45 pm
found some links that may be of interest, bigfloat in javascript, MIT license https://github.com/charto/bigfloat and predicates in C, license in the public domain https://github.com/danshapero/predicates
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Pete on March 02, 2019, 05:11:17 am
Those are some HUGE number places. I haven't decided how to account for some displays. I mean an advantage to string math is it can go on forever, but at some point you need a better way to display the results than just a million characters OFF the screen. For now, I have it carry out 20 places past the decimal, but that can be user regulated with a variable in the future. It doesn't use D or E notation to report leading, trailing zeros yet. I may or may not add that option. In this edition, I put in an input validator.

One bug found in trailing zeros part of division. Hope to have it fixed soon and reposted.

Pete
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Pete on March 02, 2019, 07:46:48 pm
Fixed on bug. I still have some rounding work to do. I'm a bit ticked that some repeating decimals like .66666 form 2 / 3 don't get rounded, while others like .5555555555555555 get rounded to .5555555555555556. Really? Anyway, I'm not quite sure what I want to do about accuracy, meaning number of digits to display. Right now, it is unlimited for integer values, and user limited by the variable divaccuracy%, which is currently set to 30 places.

Anyway, still not well tested so try it and report any bugs, but of course there is no warranty of use; so use it at your own foolish risk for anything other than just beta testing.

Oh, speaking of testing, I do have it show the numeric math for comparison, but I quickly learned because the string math is more accurate, several computations would lead to a wider discrepancy of results. To eliminate that, for each computation the numeric value is set to the string value after both are displayed.

Pete

Code: QB64: [Select]
  1. divaccuracy% = 40
  2.     DO
  3.         LINE INPUT "Number: "; stringmathb$
  4.         origb$ = stringmathb$
  5.         CALL stringmath(stringmatha$, operator$, stringmathb$, runningtotal$, divaccuracy%)
  6.         IF stringmathb$ <> "invalid number" THEN EXIT DO
  7.     LOOP
  8.     IF runningtotal$ <> "" THEN
  9.         SELECT CASE origoperator$
  10.             CASE "+"
  11.                 runningtotal# = VAL(origa$) + VAL(origb$)
  12.             CASE "-"
  13.                 runningtotal# = VAL(origa$) - VAL(origb$)
  14.             CASE "*"
  15.                 runningtotal# = VAL(origa$) * VAL(origb$)
  16.             CASE "/"
  17.                 runningtotal# = VAL(origa$) / VAL(origb$)
  18.         END SELECT
  19.         origa$ = LTRIM$(STR$(runningtotal#))
  20.         COLOR 8, 0: PRINT "Numeric Total: "; origa$: COLOR 7, 0
  21.         PRINT "String Total:  "; runningtotal$
  22.         origa$ = runningtotal$ ' Allign variable total for next computation.
  23.     ELSE
  24.         origa$ = stringmatha$
  25.     END IF
  26.     COLOR 2, 0: PRINT "Operator: +-/*: ";: COLOR 7, 0
  27.     DO
  28.         operator$ = INKEY$
  29.         IF LEN(operator$) THEN
  30.             IF operator$ = CHR$(27) THEN SYSTEM
  31.             IF INSTR("-+/*=8", operator$) THEN EXIT DO
  32.         END IF
  33.     LOOP
  34.     IF operator$ = "=" THEN operator$ = "+"
  35.     IF operator$ = "8" THEN operator$ = "*"
  36.     PRINT operator$
  37.     origoperator$ = operator$
  38.  
  39. SUB stringmath (stringmatha$, operator$, stringmathb$, runningtotal$, divaccuracy%)
  40. IF stringmatha$ = "" THEN
  41.     stringmatha$ = stringmathb$
  42.     GOSUB validatestringnumber
  43.     IF stringmathb$ = "invalid number" THEN stringmatha$ = ""
  44.     EXIT SUB
  45.     GOSUB validatestringnumber
  46.     IF stringmathb$ = "invalid number" THEN EXIT SUB
  47. IF runningtotal$ <> "" THEN stringmatha$ = runningtotal$
  48.  
  49. SELECT CASE operator$
  50.     CASE "+", "-"
  51.         stringcalc:
  52.         IF INSTR(stringmatha$, ".") <> 0 THEN ' Evaluate sum for decimal fraction.
  53.             sumplace% = LEN(stringmatha$) - INSTR(stringmatha$, ".")
  54.             stringmatha$ = MID$(stringmatha$, 1, INSTR(stringmatha$, ".") - 1) + MID$(stringmatha$, INSTR(stringmatha$, ".") + 1) ' Strip out decimal
  55.         END IF
  56.         IF INSTR(stringmathb$, ".") <> 0 THEN ' Evaluate number for decimal fraction.
  57.             numplace% = LEN(stringmathb$) - INSTR(stringmathb$, ".")
  58.             stringmathb$ = MID$(stringmathb$, 1, INSTR(stringmathb$, ".") - 1) + MID$(stringmathb$, INSTR(stringmathb$, ".") + 1) ' Strip out decimal
  59.         END IF
  60.         IF sumplace% > numplace% THEN addsubplace% = sumplace% ELSE addsubplace% = numplace%
  61.         IF sumplace% > addsubplace% THEN
  62.             stringmatha$ = stringmatha$ + STRING$(sumplace% - addsubplace%, "0")
  63.         ELSEIF addsubplace% > sumplace% THEN
  64.             stringmatha$ = stringmatha$ + STRING$(addsubplace% - sumplace%, "0")
  65.         END IF
  66.         IF numplace% > addsubplace% THEN
  67.             stringmathb$ = stringmathb$ + STRING$(numplace% - addsubplace%, "0")
  68.         ELSEIF addsubplace% > numplace% THEN
  69.             stringmathb$ = stringmathb$ + STRING$(addsubplace% - numplace%, "0")
  70.         END IF ' END Decimal evaluations........................
  71.  
  72.         IF LEFT$(stringmatha$, 1) = "-" THEN sign1$ = "-" ELSE sign1$ = "+"
  73.         IF LEFT$(stringmathb$, 1) = "-" THEN sign2$ = "-" ELSE sign2$ = "+"
  74.  
  75.         addsubsign% = 0
  76.         SELECT CASE sign1$ + operator$ + sign2$
  77.             CASE "+++", "+--"
  78.                 operator$ = "+"
  79.                 IF LEFT$(stringmathb$, 1) = "-" THEN stringmathb$ = MID$(stringmathb$, 2)
  80.             CASE "++-", "+-+"
  81.                 operator$ = "-"
  82.                 IF LEFT$(stringmathb$, 1) = "-" THEN stringmathb$ = MID$(stringmathb$, 2)
  83.                 IF VAL(stringmathb$) > VAL(stringmatha$) THEN
  84.                     SWAP stringmatha$, stringmathb$
  85.                     addsubsign% = -1
  86.                     SWAP origa$, origb$
  87.                 END IF
  88.             CASE "---", "-++"
  89.                 operator$ = "-"
  90.                 IF LEFT$(stringmatha$, 1) = "-" THEN stringmatha$ = MID$(stringmatha$, 2)
  91.                 IF LEFT$(stringmathb$, 1) = "-" THEN stringmathb$ = MID$(stringmathb$, 2)
  92.                 IF VAL(stringmathb$) > VAL(stringmatha$) THEN
  93.                     SWAP stringmatha$, stringmathb$
  94.                     SWAP origa$, origb$
  95.                 ELSE
  96.                     addsubsign% = -1
  97.                 END IF
  98.             CASE "--+", "-+-"
  99.                 operator$ = "+"
  100.                 IF LEFT$(stringmatha$, 1) = "-" THEN stringmatha$ = MID$(stringmatha$, 2)
  101.                 IF LEFT$(stringmathb$, 1) = "-" THEN stringmathb$ = MID$(stringmathb$, 2)
  102.                 addsubsign% = -1
  103.         END SELECT
  104.  
  105.         IF LEN(stringmatha$) > LEN(stringmathb$) THEN
  106.             stringmathb$ = STRING$(LEN(stringmatha$) - LEN(stringmathb$), "0") + stringmathb$
  107.         ELSEIF LEN(stringmatha$) < LEN(stringmathb$) THEN
  108.             stringmatha$ = STRING$(LEN(stringmathb$) - LEN(stringmatha$), "0") + stringmatha$
  109.         END IF
  110.         addsubx1$ = ""
  111.  
  112.         SELECT CASE operator$
  113.             CASE "+", "="
  114.                 FOR addsubii% = LEN(stringmatha$) TO 1 STEP -1
  115.                     addsubx1% = VAL(MID$(stringmatha$, addsubii%, 1)) + VAL(MID$(stringmathb$, addsubii%, 1)) + addsubcarry%
  116.                     IF addsubx1% > 9 THEN addsubx1% = addsubx1% - 10: addsubcarry% = 1 ELSE addsubcarry% = 0
  117.                     addsubx1$ = LTRIM$(STR$(addsubx1%)) + addsubx1$
  118.                 NEXT
  119.                 IF addsubcarry% THEN addsubx1$ = "1" + addsubx1$: addsubcarry% = 0
  120.                 GOSUB replacedecimal
  121.             CASE "-"
  122.                 FOR addsubii% = LEN(stringmatha$) TO 1 STEP -1
  123.                     addsubx1% = VAL(MID$(stringmatha$, addsubii%, 1)) - VAL(MID$(stringmathb$, addsubii%, 1)) + addsubcarry%
  124.                     IF addsubx1% < 0 THEN addsubx1% = addsubx1% + 10: addsubcarry% = -1 ELSE addsubcarry% = 0
  125.                     addsubx1$ = LTRIM$(STR$(addsubx1%)) + addsubx1$
  126.                 NEXT
  127.                 IF addsubx1$ <> "" AND addsubx1$ <> STRING$(LEN(addsubx1$), "0") THEN GOSUB replacedecimal
  128.                 DO UNTIL LEFT$(addsubx1$, 1) <> "0" ' Remove leading zeros.
  129.                     addsubx1$ = MID$(addsubx1$, 2)
  130.                 LOOP
  131.                 IF addsubx1$ = "" THEN
  132.                     addsubx1$ = "0": addsubsign% = 0
  133.                 ELSE
  134.                     IF addsubcarry% THEN addsubx1$ = "-" + addsubx1$: addsubcarry% = 0
  135.                 END IF
  136.         END SELECT
  137.  
  138.         IF addsubsign% THEN
  139.             IF LEFT$(addsubx1$, 1) = "-" THEN addsubx1$ = MID$(addsubx1$, 2) ELSE addsubx1$ = "-" + addsubx1$
  140.         END IF
  141.         stringmatha$ = addsubx1$
  142.         IF operationdivision% THEN RETURN
  143.         runningtotal$ = stringmatha$
  144.         EXIT SUB
  145.  
  146.         replacedecimal:
  147.         IF addsubplace% THEN
  148.             addsubx1$ = STRING$(addsubplace% - LEN(addsubx1$), "0") + addsubx1$
  149.             DO UNTIL RIGHT$(addsubx1$, 1) <> "0" ' Remove trailing zeros in a decimal sum.
  150.                 addsubx1$ = MID$(addsubx1$, 1, LEN(addsubx1$) - 1)
  151.                 addsubplace% = addsubplace% - 1
  152.             LOOP
  153.             IF addsubplace% > 0 THEN ' Do not replace decimal if total has become an integer amount.
  154.                 addsubx1$ = MID$(addsubx1$, 1, LEN(addsubx1$) - addsubplace%) + "." + MID$(addsubx1$, LEN(addsubx1$) - addsubplace% + 1)
  155.             END IF
  156.         END IF
  157.         RETURN
  158.  
  159.     CASE "*"
  160.         stringmultiply:
  161.         multsumcnt% = 0: multcarry% = 0: multplace% = 0: multiconcat$ = ""
  162.         factor1$ = stringmatha$: factor2$ = stringmathb$
  163.         IF LEN(stringmathb$) > LEN(stringmatha$) THEN
  164.             SWAP factor1$, factor2$
  165.         END IF
  166.         IF LEFT$(factor1$, 1) = "-" THEN factor1$ = MID$(factor1$, 2): multsign% = -1
  167.         IF LEFT$(factor2$, 1) = "-" THEN factor2$ = MID$(factor2$, 2): IF multsign% THEN multsign% = 0 ELSE multsign% = -1
  168.         IF INSTR(factor1$, ".") <> 0 THEN multplace% = LEN(factor1$) - INSTR(factor1$, "."): factor1$ = MID$(factor1$, 1, INSTR(factor1$, ".") - 1) + MID$(factor1$, INSTR(factor1$, ".") + 1)
  169.         IF INSTR(factor2$, ".") <> 0 THEN multplace% = multplace% + LEN(factor2$) - INSTR(factor2$, "."): factor2$ = MID$(factor2$, 1, INSTR(factor2$, ".") - 1) + MID$(factor2$, INSTR(factor2$, ".") + 1)
  170.  
  171.         FOR multii% = LEN(factor2$) TO 1 STEP -1
  172.             multii$ = MID$(factor2$, multii%, 1)
  173.             multiiconcat$ = ""
  174.             FOR multjj% = LEN(factor1$) TO 1 STEP -1
  175.                 multkk% = VAL(MID$(factor1$, multjj%, 1)) * VAL(multii$) + multcarry%
  176.                 IF multkk% > 9 THEN
  177.                     multcarry% = VAL(LEFT$(LTRIM$(STR$(multkk%)), 1))
  178.                     multkk% = VAL(RIGHT$(LTRIM$(STR$(multkk%)), 1))
  179.                 ELSE
  180.                     multcarry% = 0
  181.                 END IF
  182.                 multiiconcat$ = LTRIM$(STR$(multkk%)) + multiiconcat$
  183.             NEXT
  184.             IF multcarry% THEN multiiconcat$ = LTRIM$(STR$(multcarry%)) + multiiconcat$: multcarry% = 0
  185.             GOSUB multsums
  186.         NEXT
  187.         IF multplace% AND product$ <> "0" THEN
  188.             product$ = MID$(product$, 1, LEN(product$) - multplace%) + "." + MID$(product$, LEN(product$) - multplace% + 1)
  189.         END IF
  190.         DO UNTIL LEFT$(product$, 1) <> "0" ' Remove leading zeros.
  191.             product$ = MID$(product$, 2)
  192.         LOOP
  193.         IF product$ = "" THEN product$ = "0": multsign% = 0
  194.         IF multsign% THEN product$ = "-" + product$
  195.         IF RIGHT$(product$, 1) = "." THEN product$ = MID$(product$, 1, LEN(product$) - 1) ' Remove decimal from the end of an integer total.
  196.         IF operator$ = "/" THEN RETURN
  197.         runningtotal$ = product$
  198.         EXIT SUB
  199.  
  200.         multsums:
  201.         IF product$ <> "" THEN
  202.             multsumcnt% = multsumcnt% + 1
  203.             multiiconcat$ = multiiconcat$ + STRING$(multsumcnt%, "0")
  204.             multhh% = 0
  205.             DO
  206.                 IF multhh% < LEN(product$) THEN ms1$ = MID$(product$, LEN(product$) - multhh%, 1) ELSE ms1$ = ""
  207.                 IF multhh% < LEN(multiiconcat$) THEN ms2$ = MID$(multiiconcat$, LEN(multiiconcat$) - multhh%, 1) ELSE ms2$ = ""
  208.                 IF ms1$ = "" AND ms2$ = "" THEN EXIT DO
  209.                 addsubx1% = VAL(ms1$) + VAL(ms2$) + multcarry%
  210.                 IF addsubx1% > 9 THEN addsubx1% = addsubx1% - 10: multcarry% = 1 ELSE multcarry% = 0
  211.                 xproduct$ = LTRIM$(STR$(addsubx1%)) + xproduct$
  212.                 multhh% = multhh% + 1
  213.             LOOP
  214.             IF multcarry% THEN product$ = "1" + xproduct$: multcarry% = 0 ELSE product$ = xproduct$
  215.             IF multplace% THEN
  216.                 DO UNTIL RIGHT$(product$, 1) <> "0" ' Remove trailing zeros in a decimal sum.
  217.                     product$ = MID$(product$, 1, LEN(product$) - 1)
  218.                     multplace% = multplace% - 1
  219.                 LOOP
  220.             END IF
  221.             xproduct$ = ""
  222.         ELSE
  223.             product$ = multiiconcat$: multiiconcat$ = ""
  224.         END IF
  225.         RETURN
  226.  
  227.     CASE "/"
  228.         operationdivision% = -1: quotient$ = ""
  229.         d2dividend$ = stringmatha$
  230.         d1divisor$ = stringmathb$
  231.         IF LEFT$(d1divisor$, 1) = "0" AND LEN(d1divisor$) = 1 THEN PRINT "Division by zero not allowed.": END
  232.         IF LEFT$(d1divisor$, 1) = "-" THEN divsign% = -1: d1divisor$ = MID$(d1divisor$, 2)
  233.         IF LEFT$(d2dividend$, 1) = "-" THEN
  234.             IF divsign% THEN
  235.                 divsign% = 0
  236.             ELSE
  237.                 divsign% = -1
  238.             END IF
  239.             d2dividend$ = MID$(d2dividend$, 2)
  240.         END IF
  241.         IF INSTR(d1divisor$, ".") <> 0 THEN
  242.             DO UNTIL RIGHT$(d1divisor$, 1) <> "0"
  243.                 d1divisor$ = MID$(d1divisor$, 1, LEN(d1divisor$) - 1) ' Strip off trailing zeros
  244.             LOOP
  245.             divplace% = LEN(d1divisor$) - INSTR(d1divisor$, ".")
  246.             d1divisor$ = MID$(d1divisor$, 1, INSTR(d1divisor$, ".") - 1) + MID$(d1divisor$, INSTR(d1divisor$, ".") + 1) ' Strip off decimal point.
  247.             DO UNTIL LEFT$(d1divisor$, 1) <> "0"
  248.                 d1divisor$ = MID$(d1divisor$, 2) ' Strip off leading zeros for divisors smaller than .1
  249.             LOOP
  250.         END IF
  251.  
  252.         IF INSTR(d2dividend$, ".") <> 0 THEN
  253.             d2dividend$ = d2dividend$ + STRING$(divplace% - LEN(d2dividend$) - INSTR(d2dividend$, "."), "0") ' Add any zeros based on the length of dividend at decimal - length of divisor at decimal. If less than zero, nothing added.
  254.             divplace2% = INSTR(d2dividend$, ".")
  255.             DO UNTIL RIGHT$(d2dividend$, 1) <> "0"
  256.                 d2dividend$ = MID$(d2dividend$, 1, LEN(d2dividend$) - 1) ' Strip off trailing zeros
  257.             LOOP
  258.             d2dividend$ = MID$(d2dividend$, 1, INSTR(d2dividend$, ".") - 1) + MID$(d2dividend$, INSTR(d2dividend$, ".") + 1) ' Strip off decimal point.
  259.         ELSE
  260.             d2dividend$ = d2dividend$ + 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.
  261.             divplace% = 0
  262.         END IF
  263.         DO
  264.             DO
  265.                 ii = ii + 1
  266.                 divremainder% = divremainder% + 1: divremainder$ = divremainder$ + MID$(d2dividend$, divremainder%, 1)
  267.                 IF MID$(d2dividend$, divremainder%, 1) = "" THEN
  268.                     IF divremainder$ = STRING$(LEN(divremainder$), "0") AND LEN(quotient$) > LEN(d2dividend$) THEN divflag% = -1: EXIT DO
  269.                     divcarry% = divcarry% + 1
  270.                     IF divcarry% = 1 THEN divplace3% = divremainder% - 1
  271.                     IF divcarry% > divaccuracy% THEN divflag% = -2: EXIT DO
  272.                     divremainder$ = divremainder$ + "0" ' No more digits to bring down.
  273.                 END IF
  274.                 IF LEN(divremainder$) > LEN(d1divisor$) OR LEN(divremainder$) = LEN(d1divisor$) AND divremainder$ >= d1divisor$ THEN EXIT DO
  275.                 quotient$ = quotient$ + "0"
  276.             LOOP
  277.             IF divflag% THEN divflag% = 0: EXIT DO
  278.             FOR idiv% = 9 TO 1 STEP -1
  279.                 stringmatha$ = LTRIM$(STR$(idiv%)): stringmathb$ = d1divisor$
  280.                 product$ = "": GOSUB stringmultiply
  281.                 tempcutd$ = divremainder$ ' divremainder$ can be 00 or other leading zero values.
  282.                 DO
  283.                     IF LEN(tempcutd$) = 1 THEN EXIT DO
  284.                     IF LEFT$(tempcutd$, 1) = "0" THEN
  285.                         tempcutd$ = MID$(tempcutd$, 2)
  286.                     ELSE
  287.                         EXIT DO
  288.                     END IF
  289.                 LOOP
  290.                 IF LEN(tempcutd$) > LEN(product$) OR LEN(tempcutd$) = LEN(product$) AND product$ <= tempcutd$ THEN EXIT FOR
  291.             NEXT
  292.             quotient$ = quotient$ + LTRIM$(STR$(idiv%))
  293.             stringmatha$ = LTRIM$(STR$(idiv%)): stringmathb$ = d1divisor$
  294.             product$ = "": GOSUB stringmultiply
  295.             operator$ = "-": stringmatha$ = divremainder$: stringmathb$ = product$: GOSUB stringcalc: divremainder$ = stringmatha$: operator$ = "/"
  296.         LOOP
  297.         IF divplace% = 0 AND divplace2% = 0 THEN divplace% = divplace3%
  298.         IF divplace2% THEN divplace% = divplace% + divplace2% - 1
  299.         IF quotient$ = "" THEN divplace% = 0 ' dividend is zero.
  300.         IF divplace% OR divplace2% THEN
  301.             tempq$ = MID$(quotient$, INSTR(quotient$, ".") + 1)
  302.             IF LEN(tempq$) >= 3 THEN
  303.                 IF STRING$(LEN(divremainder$), "0") = divremainder$ AND divcarry% THEN
  304.                 ELSE
  305.                     IF RIGHT$(tempq$, 3) <> STRING$(3, RIGHT$(tempq$, 1)) THEN
  306.                         IF RIGHT$(quotient$, 1) < "5" THEN
  307.                             quotient$ = MID$(quotient$, 1, LEN(quotient$) - 1)
  308.                         ELSE
  309.                             stringmatha$ = MID$(quotient$, 1, LEN(quotient$))
  310.                             stringmathb$ = STRING$(LEN(stringmatha$) - 1, "0") + LTRIM$(STR$(10 - VAL(RIGHT$(quotient$, 1))))
  311.                             operator$ = "+": GOSUB stringcalc:: operator$ = "/"
  312.                             quotient$ = stringmatha$
  313.                         END IF
  314.                     END IF
  315.                 END IF
  316.             END IF
  317.             quotient$ = MID$(quotient$, 1, divplace%) + "." + MID$(quotient$, divplace% + 1, divaccuracy% + 1) ' One extra digit for rounding.
  318.             DO UNTIL RIGHT$(quotient$, 1) <> "0"
  319.                 quotient$ = MID$(quotient$, 1, LEN(quotient$) - 1) ' Strip off trailing zeros
  320.             LOOP
  321.             IF RIGHT$(quotient$, 1) = "." THEN quotient$ = MID$(quotient$, 1, LEN(quotient$) - 1) ' Strip off abandoned decimal.
  322.         END IF
  323.         DO UNTIL LEFT$(quotient$, 1) <> "0"
  324.             quotient$ = MID$(quotient$, 2) ' Strip off leading zeros
  325.         LOOP
  326.         IF quotient$ = "" THEN quotient$ = "0": divsign% = 0
  327.         IF divsign% THEN quotient$ = "-" + quotient$
  328.         runningtotal$ = quotient$
  329.         operationdivision% = 0
  330.         EXIT SUB
  331.  
  332. validatestringnumber:
  333. valnum% = 0: negcnt% = 0: poscnt% = 0: nonzerospresent% = 0: zerospresent% = 0
  334. IF RIGHT$(stringmathb$, 1) = "." THEN stringmathb$ = MID$(stringmathb$, 1, LEN(stringmathb$) - 1)
  335. FOR vii% = 1 TO LEN(stringmathb$)
  336.     validatenum$ = MID$(stringmathb$, vii%, 1)
  337.     SELECT CASE validatenum$
  338.         CASE "."
  339.             decimalcnt% = decimalcnt% + 1
  340.         CASE "+"
  341.             poscnt% = vii%
  342.         CASE "-"
  343.             negcnt% = vii%
  344.         CASE "0"
  345.             zerospresent% = -1
  346.         CASE "1" TO "9"
  347.             nonzerospresent% = -1
  348.         CASE ELSE
  349.             stringmathb$ = "invalid number": RETURN
  350.     END SELECT
  351. IF decimalcnt% > 1 OR negcnt% > 1 OR poscnt% > 1 THEN
  352.     stringmathb$ = "invalid number": RETURN
  353. IF nonzerospresent% = 0 THEN
  354.     IF zerospresent% THEN
  355.         stringmathb$ = "0"
  356.     ELSE
  357.         stringmathb$ = "invalid number"
  358.     END IF
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: codeguy on March 03, 2019, 04:22:38 am
Arbitrary precision factorial for QB64 posted at RosettaCode. Coded by yours truly

Code: QB64: [Select]
  1.  
  2. REDIM fac#(0)
  3. Factorial fac#(), 655, 10, power#
  4. PRINT power#
  5. SUB Factorial (fac#(), n&, numdigits%, power#)
  6. power# = 0
  7. fac#(0) = 1
  8. remain# = 0
  9. stx& = 0
  10. slog# = 0
  11. NumDiv# = 10 ^ numdigits%
  12. FOR fac# = 1 TO n&
  13.     slog# = slog# + LOG(fac#) / LOG(10)
  14.     FOR x& = 0 TO stx&
  15.         fac#(x&) = fac#(x&) * fac# + remain#
  16.         tx# = fac#(x&) MOD NumDiv#
  17.         remain# = (fac#(x&) - tx#) / NumDiv#
  18.         fac#(x&) = tx#
  19.     NEXT
  20.     IF remain# > 0 THEN
  21.         stx& = UBOUND(fac#) + 1
  22.         REDIM _PRESERVE fac#(stx&)
  23.         fac#(stx&) = remain#
  24.         remain# = 0
  25.     END IF
  26.  
  27. scanz& = LBOUND(fac#)
  28.     IF scanz& < UBOUND(fac#) THEN
  29.         IF fac#(scanz&) THEN
  30.             EXIT DO
  31.         ELSE
  32.             scanz& = scanz& + 1
  33.         END IF
  34.     ELSE
  35.         EXIT DO
  36.     END IF
  37.  
  38. FOR x& = UBOUND(fac#) TO scanz& STEP -1
  39.     m$ = LTRIM$(RTRIM$(STR$(fac#(x&))))
  40.     IF x& < UBOUND(fac#) THEN
  41.         WHILE LEN(m$) < numdigits%
  42.             m$ = "0" + m$
  43.         WEND
  44.     END IF
  45.     PRINT m$; " ";
  46.     power# = power# + LEN(m$)
  47. power# = power# + (scanz& * numdigits%) - 1
  48. PRINT slog#
  49.  
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Pete on March 03, 2019, 11:17:19 pm
Well, here's something that isn't Greek to me...

Adding a way to calculate / convert to scientific notation. The variable limit% can be adjusted to the max. number of digits to be displayed. This routine is for positive numbers only. I didn't need to evaluate for negatives, because the routine I'm putting this in does that.

Code: QB64: [Select]
  1. limit% = 5
  2. LINE INPUT "Number: "; n$
  3. IF LEFT$(n$, 1) = "-" THEN n$ = MID$(n$, 2): n2sign$ = "-"
  4. ii% = INSTR(n$, ".") - 2: IF ii% = -2 THEN ii% = LEN(n$) - 1 ' No decimal.
  5. n2$ = MID$(n$, 1, INSTR(n$, ".") - 1) + MID$(n$, INSTR(n$, ".") + 1)
  6. IF LEFT$(n2$, 1) = "0" AND LEN(n2$) > 1 OR ii% = -1 THEN
  7.     DO UNTIL LEFT$(n2$, 1) <> "0" ' Remove leading zeros to consider rounding.
  8.         n2$ = MID$(n2$, 2)
  9.         ii% = ii% - 1
  10.     LOOP
  11.     esign$ = "-"
  12.     esign$ = "+"
  13. n2$ = MID$(n2$, 1, limit% + 1)
  14. n% = LEN(n2$)
  15. IF n% > limit% THEN
  16.     IF RIGHT$(n2$, 1) > "4" THEN
  17.         ' Substitute string math addition routine for line below.
  18.         n2$ = LTRIM$(STR$(VAL(n2$) + 10 - VAL(RIGHT$(n2$, 1))))
  19.     END IF
  20. DO UNTIL RIGHT$(n2$, 1) <> "0" ' Remove trailing zeros.
  21.     n2$ = MID$(n2$, 1, LEN(n2$) - 1)
  22. IF n2$ = "" THEN n2$ = "0": esign$ = "+": ii% = 1
  23. n$ = n2sign$ + LEFT$(n2$, 1) + "." + MID$(n2$, 2, limit% - 1) + "e" + esign$ + LTRIM$(STR$(ABS(ii%)))
  24.  

Pete
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Pete on March 04, 2019, 06:08:19 pm
Well, since the function posted in the Wiki by Clippy can't handle exponents over 308, I decided to make my own, but to get it working faster, I did fudge it a bit on the exponents, which are limited by passing with the VAL statement to an _INTEGER64 variable. Going much beyond that would take some pretty funky counting routine, but it could be done. So it looks like string math can go to memory capacity in decimals, but if converting back and forth in scientific notation will be limited.

Code: QB64: [Select]
  1. ' Good up to exponents of 9,223,372,036,854,775,807 (Limited by _integer64 in VAL statement).
  2. LINE INPUT "Number in scientific notation: "; n$
  3. IF LEFT$(n$, 1) = "-" THEN n2sign$ = "-"
  4. IF VAL(LEFT$(n$, 1)) = 0 AND LEFT$(n$, 1) <> "0" THEN n2$ = MID$(n$, 2, 1) + UCASE$(MID$(n$, 4)) ELSE n2$ = MID$(n$, 1, 1) + UCASE$(MID$(n$, 3)) ' Strip of + or - sign and remove decimal.
  5. FOR ii% = LEN(n2$) TO 1 STEP -1
  6.     ii$ = MID$(n2$, ii%, 1)
  7.     iiconcat$ = ii$ + iiconcat$
  8.     IF ii$ = "E" OR ii$ = "D" THEN n2$ = MID$(n2$, 1, ii% - 1): EXIT FOR
  9. IF VAL(MID$(iiconcat$, 2)) < LEN(n2$) - 1 THEN n2point$ = "."
  10. n2zeros$ = STRING$(VAL(MID$(iiconcat$, 3)) - LEN(n2$) + 1, "0")
  11. jj&& = VAL(MID$(iiconcat$, 2)) + 1
  12. IF jj&& < 0 THEN
  13.     lzeros$ = STRING$(ABS(jj&&), "0")
  14.     jj&& = 0
  15.     IF jj&& > LEN(n2$) AND n2$ <> "0" THEN
  16.         tzeros$ = STRING$(jj&& - LEN(n2$), "0")
  17.         jj&& = LEN(n2$)
  18.     END IF
  19. PRINT n2sign$ + MID$(n2$, 1, jj&&) + n2point$ + lzeros$ + MID$(n2$, jj&& + 1) + tzeros$
  20.  

Pete
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Pete on March 05, 2019, 02:32:59 pm
So I put together a routine to validate numeric and scientific notation input, and then convert back ad forth. If there's a bug, the conversions won't match. I should be able to put together a unit checker, soon. The limit% variable limits the display to 15 digits for scientific notation. I'll work on changing some of the smaller variable names in due time.

Code: QB64: [Select]
  1. limit% = 15
  2. LINE INPUT "Number: "; n$
  3. GOSUB validatestringnumber
  4. PRINT "Validation Results: "; n$
  5. PRINT "Limited to"; limit%; "digits..."
  6. IF INSTR(UCASE$(n$), "D") OR INSTR(UCASE$(n$), "E") THEN
  7.     GOSUB scientifictonumeric
  8.     PRINT "Numeric Representation: "; n$
  9.     GOSUB numerictoscientific
  10.     PRINT "Scientific Notation: "; n$
  11.     GOSUB numerictoscientific
  12.     PRINT "Scientific Notation: "; n$
  13.     GOSUB scientifictonumeric
  14.     PRINT "Numeric Representation: "; n$
  15.  
  16. numerictoscientific:
  17. IF LEFT$(n$, 1) = "-" THEN n$ = MID$(n$, 2): n2sign$ = "-"
  18. ii% = INSTR(n$, ".") - 2: IF ii% = -2 THEN ii% = LEN(n$) - 1 ' No decimal.
  19. n2$ = MID$(n$, 1, INSTR(n$, ".") - 1) + MID$(n$, INSTR(n$, ".") + 1)
  20. IF LEFT$(n2$, 1) = "0" AND LEN(n2$) > 1 OR ii% = -1 THEN
  21.     DO UNTIL LEFT$(n2$, 1) <> "0" ' Remove leading zeros to consider rounding.
  22.         n2$ = MID$(n2$, 2)
  23.         ii% = ii% - 1
  24.     LOOP
  25.     esign$ = "-"
  26.     esign$ = "+"
  27. n2$ = MID$(n2$, 1, limit% + 1)
  28. n% = LEN(n2$)
  29. IF n% > limit% THEN
  30.     IF RIGHT$(n2$, 1) > "4" THEN
  31.         ' Substitute string math addition routine for line below. Only good to limit% = 15 or less without string math routine.
  32.         n2$ = LTRIM$(STR$(VAL(n2$) + 10 - VAL(RIGHT$(n2$, 1))))
  33.     END IF
  34. DO UNTIL RIGHT$(n2$, 1) <> "0" ' Remove trailing zeros.
  35.     n2$ = MID$(n2$, 1, LEN(n2$) - 1)
  36. IF n2$ = "" THEN n2$ = "0": esign$ = "+": ii% = 0
  37. n$ = n2sign$ + LEFT$(n2$, 1) + "." + MID$(n2$, 2, limit% - 1) + "e" + esign$ + LTRIM$(STR$(ABS(ii%)))
  38. n2sign$ = "": esign$ = "": ii% = 0
  39.  
  40. scientifictonumeric:
  41. ' Good to exponents of 9,223,372,036,854,775,807 (Limited by _integer64 in VAL statement).
  42. IF LEFT$(n$, 1) = "-" THEN n2sign$ = "-"
  43. IF VAL(LEFT$(n$, 1)) = 0 AND LEFT$(n$, 1) <> "0" THEN n2$ = MID$(n$, 2, 1) + UCASE$(MID$(n$, 4)) ELSE n2$ = MID$(n$, 1, 1) + UCASE$(MID$(n$, 3)) ' Strip of + or - sign and remove decimal.
  44. FOR ii% = LEN(n2$) TO 1 STEP -1
  45.     ii$ = MID$(n2$, ii%, 1)
  46.     iiconcat$ = ii$ + iiconcat$
  47.     IF ii$ = "E" OR ii$ = "D" THEN n2$ = MID$(n2$, 1, ii% - 1): EXIT FOR
  48. IF VAL(MID$(iiconcat$, 2)) < LEN(n2$) - 1 THEN n2point$ = "."
  49. n2zeros$ = STRING$(VAL(MID$(iiconcat$, 3)) - LEN(n2$) + 1, "0")
  50. jj&& = VAL(MID$(iiconcat$, 2)) + 1
  51. IF jj&& < 0 THEN
  52.     lzeros$ = STRING$(ABS(jj&&), "0")
  53.     jj&& = 0
  54.     IF jj&& > LEN(n2$) AND n2$ <> "0" THEN
  55.         tzeros$ = STRING$(jj&& - LEN(n2$), "0")
  56.         jj&& = LEN(n2$)
  57.     END IF
  58. IF LEN(n2$ + lzeros$ + tzeros$) > limit% THEN ' Rounding routine.
  59.     IF RIGHT$(n2$, 1) > "4" THEN
  60.         ' Substitute string math addition routine for line below. Only good to limit% = 15 or less without string math routine.
  61.         n2$ = LTRIM$(STR$(VAL(n2$) + 10 - VAL(RIGHT$(n2$, 1))))
  62.     END IF
  63. n$ = MID$(n2$, 1, jj&&) + n2point$ + lzeros$ + MID$(n2$, jj&& + 1) + tzeros$ ' Required with or without rounding routine lines.
  64. n$ = n2sign$ + MID$(n$, 1, limit% + LEN(n2point$)) ' For rounding purposes.
  65. IF lzeros$ <> "" THEN ' Decimal fraction. Only needed when rouning routine is used.
  66.     DO UNTIL RIGHT$(n$, 1) <> "0"
  67.         n$ = MID$(n$, 1, LEN(n$) - 1)
  68.     LOOP
  69.     IF RIGHT$(n$, 1) = "." THEN n$ = MID$(n$, 1, LEN(n$) - 1)
  70.     IF n$ = "" THEN n$ = "0"
  71.  
  72. validatestringnumber:
  73. IF INSTR(UCASE$(n$), "D") OR INSTR(UCASE$(n$), "E") THEN ' Evaluate for Scientific Notation.
  74.     ' Modify n$ to a standard.
  75.     jjd% = INSTR(UCASE$(n$), "D")
  76.     jje% = INSTR(UCASE$(n$), "E")
  77.     IF jjd% THEN jjed% = jjd% ELSE jjed% = jje% ' Get position of notation.
  78.     IF INSTR(n$, ".") = 0 THEN ' Add a decimal if not present to original number.
  79.         n$ = MID$(n$, 1, 1) + "." + MID$(n$, 2)
  80.         jjed% = 3
  81.     END IF
  82.     n2$ = MID$(n$, jjed%): n$ = MID$(n$, 1, jjed% - 1) ' n$ is +- single digit whole number, decimal point and decimal number. n2$ is notation, sign and exponent.
  83.     DO UNTIL RIGHT$(n$, 1) <> "0" ' Remove any trailing zeros for number. Example 1.0d3 or 1.0000d3, etc.
  84.         n$ = MID$(n$, 1, LEN(n$) - 1)
  85.         jjed% = jjed% - 1
  86.     LOOP
  87.     IF VAL(MID$(n$, 1, INSTR(n$, ".") - 1)) = 0 THEN
  88.         IF RIGHT$(n$, 1) = "." THEN
  89.             n$ = "0.e+0" ' Handles all types of zero entries.
  90.         ELSE
  91.             n$ = "invalid number"
  92.         END IF
  93.         RETURN
  94.     END IF
  95.     n$ = n$ + n2$
  96.     IF MID$(n$, jjed% + 1, 1) <> "-" THEN
  97.         IF MID$(n$, jjed% + 1, 1) <> "+" THEN
  98.             n$ = MID$(n$, 1, jjed%) + "+" + MID$(n$, jjed% + 1) ' Add a + sign.
  99.         END IF
  100.     END IF
  101.     n2$ = MID$(n$, 1, jjed% - 1) ' Vailidate the number portion.
  102.     IF LEFT$(n2$, 1) = "-" THEN n2$ = MID$(n2$, 2) ' Strip off any leading - sign.
  103.     IF LEFT$(n2$, 1) = "+" THEN n2$ = MID$(n2$, 2): n$ = MID$(n$, 2) ' Strip off any leading + sign.
  104.     IF MID$(n2$, 2, 1) <> "." THEN n$ = "invalid number": RETURN
  105.     FOR ii% = 1 TO LEN(n2$)
  106.         IF MID$(n2$, ii%, 1) <> "." THEN
  107.             IF VAL(MID$(n2$, ii%, 1)) = 0 AND MID$(n2$, ii%, 1) <> "0" THEN n$ = "invalid number": RETURN
  108.         END IF
  109.     NEXT
  110.     IF n2$ = "" THEN n$ = "invalid number": RETURN
  111.     n2$ = MID$(n$, jjed% + 2) ' Validate exponent. Allows -0 exponent as in: 0.e-0
  112.     IF n2$ = "" THEN n$ = "invalid number": RETURN ' Example: 1.3d
  113.     IF MID$(n2$, 1, 1) = "0" AND LEN(n2$) > 1 THEN n$ = "invalid number": RETURN ' Example 1.3d+01
  114.     FOR ii% = 1 TO LEN(n2$)
  115.         IF VAL(MID$(n2$, ii%, 1)) = 0 AND MID$(n2$, ii%, 1) <> "0" THEN n$ = "invalid number": RETURN
  116.     NEXT
  117.     RETURN
  118.     ' Evaluate standard number.
  119.     valnum% = 0: negcnt% = 0: poscnt% = 0: nonzerospresent% = 0: zerospresent% = 0
  120.     IF RIGHT$(stringmathb$, 1) = "." THEN stringmathb$ = MID$(stringmathb$, 1, LEN(stringmathb$) - 1)
  121.     FOR vii% = 1 TO LEN(stringmathb$)
  122.         validatenum$ = MID$(stringmathb$, vii%, 1)
  123.         SELECT CASE validatenum$
  124.             CASE "."
  125.                 decimalcnt% = decimalcnt% + 1
  126.             CASE "+"
  127.                 poscnt% = vii%
  128.             CASE "-"
  129.                 negcnt% = vii%
  130.             CASE "0"
  131.                 zerospresent% = -1
  132.             CASE "1" TO "9"
  133.                 nonzerospresent% = -1
  134.             CASE ELSE
  135.                 stringmathb$ = "invalid number": RETURN
  136.         END SELECT
  137.     NEXT
  138.     IF decimalcnt% > 1 OR negcnt% > 1 OR poscnt% > 1 THEN
  139.         stringmathb$ = "invalid number": RETURN
  140.     END IF
  141.     IF nonzerospresent% = 0 THEN
  142.         IF zerospresent% THEN
  143.             stringmathb$ = "0"
  144.         ELSE
  145.             stringmathb$ = "invalid number"
  146.         END IF
  147.     END IF
  148.  

Pete
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: codeguy on March 07, 2019, 02:55:54 pm
Bad news: old hp laptop died. Good news: replaced with i7. Haven't contributed in a while but I hope others have stolen my code. Yes, it's not flashy, but I I'm glad if anyone has been able to use it.
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Jack002 on March 14, 2019, 11:27:51 am
In reference to your 18/7 discussion:
1/7 is the repeating decimal 0.142857142857142857
Its a repeating pattern that goes on forever... type 142857, then do it again and again. Not unlike 1/3 is 0.33333333333

This thread is fascinating to me. These long string numbers can make some interesting patterns
Try 1/97 for as many possible digits you can. There are some neat patterns in some decimal fraction equivalants
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: bplus on March 14, 2019, 11:47:40 am
In reference to your 18/7 discussion:
1/7 is the repeating decimal 0.142857142857142857
Its a repeating pattern that goes on forever... type 142857, then do it again and again. Not unlike 1/3 is 0.33333333333

This thread is fascinating to me. These long string numbers can make some interesting patterns
Try 1/97 for as many possible digits you can. There are some neat patterns in some decimal fraction equivalants

If you convert the decimal to base 2, use a white rectangle for 1 and a black rectangle for 0 (or vice versa), so that you get a sort of bar code for the base 2 number.

Then stack all the bar codes from 1/n to (n-1)/n, I am pretty sure you will be astounded how fraction patterns that can be reduced to lower terms have same pattern as in the lower n stack for instance 3/15 = 1/5 and both have the same bar code pattern.
  [ This attachment cannot be displayed inline in 'Print Page' view ]  

Also notice how the stack is symmetric about n/2, black is mirror image of white, and how the pattern changes with each increase of 1 in numerator.
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Jack002 on March 14, 2019, 12:11:34 pm
Very interesting. I can see the  1/n relationship to (n-1)/n. They are opposites, so XOR of each other. I doubt patterns seen in numbers like 1/7 will look too interesting in base 2.

I've never seen a program do things like this. Very cool
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: bplus on March 14, 2019, 10:37:36 pm
Here is some old code that captures the exact part that repeats:
Code: QB64: [Select]
  1. _TITLE "Decimal Expansion of Division without Dividing  by bplus 2017-12-03"
  2. ' dove tailing Adrians and my recent dividing programs
  3.  
  4. DEFLNG A-Z
  5.     PRINT: PRINT "Enter 2 integers < 3200, numerator / denominator, 0's quit, don't forget / "
  6.     INPUT nd$
  7.     slash = INSTR(nd$, "/")
  8.     IF slash THEN
  9.         dvsr = VAL(MID$(nd$, slash + 1))
  10.         IF dvsr = 0 THEN PRINT "Divisor is 0, bye.": END
  11.         numerator = VAL(MID$(nd$, 1, slash - 1))
  12.         IF numerator = 0 THEN PRINT "Numerator is 0, bye.": END
  13.     ELSE
  14.         PRINT "No slash found, bye.": END
  15.     END IF
  16.     PRINT numerator; " / "; dvsr; " = "; divide$(numerator, dvsr)
  17.  
  18. FUNCTION divide$ (n, d)
  19.     'n = original product or numerator (preserve value of n)
  20.     'd = divisor  (also preserve value)
  21.     c = n 'copy of n to be reduced until <= d, c will be the remainder part of division
  22.     a = 0 'a is for answer or accumulate, the integer part of the division result
  23.  
  24.     'find lowest power of 10 such that: d * 10^p > n
  25.     p = 0 'power of 10
  26.     WHILE d * (10 ^ p) < n
  27.         p = p + 1
  28.     WEND
  29.     WHILE c >= d
  30.         IF c = d THEN a = a + 1: c = 0: EXIT WHILE
  31.         p = p - 1
  32.         IF p >= 0 THEN
  33.             m = 0
  34.             WHILE d * m * 10 ^ p < c
  35.                 m = m + 1
  36.             WEND
  37.             m = m - 1
  38.             c = c - d * m * 10 ^ p
  39.             a = a + m * 10 ^ p
  40.         END IF
  41.     WEND
  42.  
  43.     'Now for the decimal expansion isolating the repeating part if one
  44.     IF c <> 0 THEN
  45.         DIM b(d)
  46.         b$ = "."
  47.         WHILE c <> 0
  48.  
  49.             'emergency bug out!
  50.             loopct = loopct + 1 'loop count should not exceed 1000 for numbers I am testing
  51.             IF loopct > 1000 THEN PRINT "Error: loop too long, bugging out! ": GOTO skip
  52.  
  53.             'track repeats  b() tracks been here once, b2() tracks been here twice
  54.             IF b(c) = 1 THEN 'been here!
  55.                 IF rFlag = 1 THEN 'been here twice!
  56.                     IF b2(c) = 1 THEN EXIT WHILE 'strike 3, we're out of here
  57.                     b2(c) = 1
  58.                 ELSE
  59.                     rFlag = 1
  60.                     DIM b2(d)
  61.                     b$ = b$ + " repeat "
  62.                     b2(c) = 1
  63.                 END IF
  64.             ELSE
  65.                 b(c) = 1
  66.             END IF
  67.  
  68.             'c was last remainder, mult by 10 and see if some m * d > can reduce it
  69.             tc = 10 * c
  70.             flag = 0
  71.             FOR m = 0 TO 9
  72.                 IF ((tc - m * d) >= 0) AND ((tc - (m + 1) * d) < 0) THEN
  73.                     flag = 1: b$ = b$ + LTRIM$(STR$(m))
  74.                     EXIT FOR
  75.                 END IF
  76.             NEXT
  77.             IF flag = 0 THEN b$ = b$ + "0": m = 0
  78.             c = tc - d * m
  79.         WEND
  80.     END IF
  81.  
  82.     'OK either d divided n eventually or there is a repeated pattern recorded in b$
  83.     skip: '< needed for debugging
  84.     r$ = STR$(a)
  85.     IF b$ <> "" THEN r$ = r$ + b$
  86.     divide$ = r$
  87.  
  88.  

The longest patterns usually come from biggest prime number denominators.

1 digit repeating pattern series 1/15, 1/30, 1/60, 1/120, 1/240,... alternate 3... and 6...
also with 1/3, 1/6, 1/12, 1/24, ...
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Jack002 on March 15, 2019, 08:42:58 am
Very cool, bplus. I like the large prime number recips. 1/97 was there as I remember. This is great. Thanks.
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: MWheatley on March 20, 2019, 08:55:12 am
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.  ;)

Out of interest, what sort of things do you use LONGs for?

Malcolm
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: bplus on March 20, 2019, 10:31:01 am
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.  ;)

Out of interest, what sort of things do you use LONGs for?

Malcolm

Signed INTEGERs only have a range of -32,7678 to 32,767 whereas LONG has a range of about 10 digits.

If you just squared the width of a screen you are likely to exceed INTEGER range.
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Pete on March 27, 2019, 11:51:23 pm
I use LONG in my String Math routine, as LEN() is limited by the LONG variable type limits. Oh, speaking of which, I posted the latest version here: https://www.tapatalk.com/groups/qbasic/viewtopic.php?f=648955&t=39442&p=212636#p212636

Pete
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Pete on April 06, 2019, 02:48:03 pm
Well, I'm a little bit excited about the first unit testing results in my string math routine. I've ran it a 10 times with all good results. It will pause when a potential problem arises, but, so far, all of those seem to be generated from the higher degree of accuracy with more digits used in many of the string math operations. The pause at least allows me to view and compare, and so far, so good. I'm only using 17+ digits for now. If the first 12 of the 16 digit QB numeric result matches the string math result, it counts it as accepted and moves on. If not, it pauses.

Here is a screen shot where it paused on a 17 digit limit, rounding enabled. I checked in on a calculator to see if it missed a rounding opportunity, and as you can see, it actually didn't!

  [ This attachment cannot be displayed inline in 'Print Page' view ]  

So far, I've only tested limits between 17 to 140 digits, rounding enabled, show rounding, and no to everything else. Here's the code I'm testing. Press any key, if it pauses, to keep testing. If anyone does test it and finds a fail, please take a screen shot and post it. Also, let me know the parameters you tested it with, especially the digit limit.

Code: QB64: [Select]
  1. WIDTH 156, 43
  2. INPUT "Limit Display: "; limit&&
  3. LINE INPUT "Use Rounding? Y/N: "; ans$
  4. IF UCASE$(ans$) = "Y" THEN
  5.     round_total% = -1
  6.     LINE INPUT "Show if Rounded? Y/N: "; ans$
  7.     IF UCASE$(ans$) = "Y" THEN show_rounding% = -1 ELSE show_rounding% = 0
  8.     round_total% = 0
  9. LINE INPUT "Display in Scientific Notation? Y/N: "; ans$
  10. IF UCASE$(ans$) = "Y" THEN
  11.     snconvert% = -1
  12.     snconvert% = 0
  13.     LINE INPUT "Display in Dollars and Cents? Y/N: "; ans$
  14.     IF UCASE$(ans$) = "Y" THEN currency_display% = -1 ELSE currency_display% = 0
  15.     LINE INPUT "Display Results with Commas? Y/N: "; ans$
  16. IF UCASE$(ans$) = "Y" THEN comma_display% = -1 ELSE comma_display% = 0
  17.     DO
  18.         '''LINE INPUT "Number: "; stringmathb$
  19.         ' UNIT TESTING============================
  20.         stringmathb$ = LTRIM$(STR$(RND * 1000000))
  21.         '=========================================
  22.         IF UCASE$(stringmathb$) = "C" THEN RUN
  23.         origb$ = stringmathb$
  24.         CALL stringmath(stringmatha$, operator$, stringmathb$, runningtotal$, snconvert%, round_total%, show_rounding%, comma_display%, currency_display%, limit&&)
  25.         IF stringmathb$ <> "invalid number" AND stringmathb$ <> "overflow" THEN
  26.             EXIT DO
  27.         ELSE
  28.             ' UNIT TESTING============================
  29.             IF stringmathb$ = "overflow" THEN
  30.                 operator% = RND * 3
  31.                 SELECT CASE operator%
  32.                     CASE 0: operator$ = "+"
  33.                     CASE 1: operator$ = "-"
  34.                     CASE 2: operator$ = "*"
  35.                     CASE 3: operator$ = "/"
  36.                 END SELECT
  37.                 PRINT stringmathb$
  38.                 PRINT "Change Operator: "; operator$: orig_operator$ = operator$
  39.                 ' UNIT TESTING============================
  40.                 ''' PRINT stringmathb$
  41.             ELSE
  42.                 PRINT stringmathb$
  43.                 END
  44.             END IF
  45.         END IF
  46.     LOOP
  47.     IF operator$ <> "" THEN
  48.         DO UNTIL INSTR(origa$, ",") = 0
  49.             origa$ = MID$(origa$, 1, INSTR(origa$, ",") - 1) + MID$(origa$, INSTR(origa$, ",") + 1)
  50.         LOOP
  51.         DO UNTIL INSTR(origb$, ",") = 0
  52.             origb$ = MID$(origb$, 1, INSTR(origb$, ",") - 1) + MID$(origb$, INSTR(origb$, ",") + 1)
  53.         LOOP
  54.         IF INSTR(origb$, "$") THEN origb$ = MID$(origb$, 1, INSTR(origb$, "$") - 1) + MID$(origb$, INSTR(origb$, "$") + 1)
  55.         SELECT CASE orig_operator$
  56.             CASE "+"
  57.                 runningtotal# = VAL(origa$) + VAL(origb$)
  58.             CASE "-"
  59.                 runningtotal# = VAL(origa$) - VAL(origb$)
  60.             CASE "*"
  61.                 runningtotal# = VAL(origa$) * VAL(origb$)
  62.             CASE "/"
  63.                 runningtotal# = VAL(origa$) / VAL(origb$)
  64.             CASE "C", "c"
  65.                 RUN
  66.         END SELECT
  67.         origa$ = LTRIM$(STR$(runningtotal#))
  68.         COLOR 8, 0: PRINT "Numeric Total: "; origa$: COLOR 7, 0
  69.         PRINT "String Total:  "; runningtotal$
  70.         ' UNIT TESTING============================
  71.         q1$ = LTRIM$(STR$(runningtotal#))
  72.         q2$ = runningtotal$
  73.         IF INSTR(q1$, ".") THEN q1$ = MID$(q1$, 1, INSTR(q1$, ".") - 1) + MID$(q1$, INSTR(q1$, ".") + 1)
  74.         IF INSTR(q2$, ".") THEN q2$ = MID$(q2$, 1, INSTR(q2$, ".") - 1) + MID$(q2$, INSTR(q2$, ".") + 1)
  75.         IF RIGHT$(UCASE$(q2$), 1) = "R" THEN q2$ = MID$(q2$, 1, LEN(q2$) - 1)
  76.         FOR i% = 1 TO 12
  77.             IF MID$(q1$, i%, 1) <> MID$(q2$, i%, 1) THEN
  78.                  DO
  79.                      _LIMIT 10
  80.                  LOOP UNTIL INKEY$ <> ""
  81.             EXIT FOR
  82.             END IF
  83.         NEXT
  84.         '===========================================
  85.     ELSE
  86.         origa$ = runningtotal$: IF INSTR(origa$, "$") THEN origa$ = MID$(origa$, 1, INSTR(origa$, "$") - 1) + MID$(origa$, INSTR(origa$, "$") + 1)
  87.     END IF
  88.     COLOR 2, 0: PRINT "Operator: +-/*: ";: COLOR 7, 0
  89.     DO
  90.         operator% = RND * 3
  91.         SELECT CASE operator%
  92.             CASE 0: operator$ = "+"
  93.             CASE 1: operator$ = "-"
  94.             CASE 2: operator$ = "*"
  95.             CASE 3: operator$ = "/"
  96.         END SELECT
  97.         EXIT DO
  98.  
  99.         operator$ = INKEY$
  100.         IF LEN(operator$) THEN
  101.             IF operator$ = CHR$(27) THEN SYSTEM
  102.             IF INSTR("-+/*=8cC", operator$) THEN EXIT DO
  103.         END IF
  104.     LOOP
  105.     IF UCASE$(operator$) = "C" THEN RUN
  106.     IF operator$ = "=" THEN operator$ = "+"
  107.     IF operator$ = "8" THEN operator$ = "*"
  108.     orig_operator$ = operator$
  109.     PRINT operator$
  110.  
  111. SUB stringmath (stringmatha$, operator$, stringmathb$, runningtotal$, snconvert%, round_total%, show_rounding%, comma_display%, currency_display%, limit&&)
  112. stringmathround$ = ""
  113. IF limit&& > 2147483640 THEN limit&& = 2147483640
  114. IF limit&& = 0 THEN limit&& = 70 ' Default.
  115.  
  116. IF RIGHT$(UCASE$(runningtotal$), 1) = "R" THEN runningtotal$ = MID$(runningtotal$, 1, LEN(runningtotal$) - 1) 'Strip off rounding designation.
  117. ' Check running total. If S.N. convert to numeric for operations.
  118. IF INSTR(runningtotal$, ",") <> 0 OR INSTR(runningtotal$, "e") <> 0 THEN
  119.     holdstringmathb$ = stringmathb$
  120.     stringmathb$ = runningtotal$
  121.     IF INSTR(runningtotal$, ",") <> 0 THEN GOSUB comma_removal ELSE GOSUB scientific_to_numeric
  122.     runningtotal$ = stringmathb$: stringmathb$ = holdstringmathb$: holdstringmathb$ = ""
  123. ' Check input number. If S.N. convert to numeric for operations.
  124. IF INSTR(UCASE$(stringmathb$), "D") <> 0 OR INSTR(UCASE$(stringmathb$), "E") <> 0 THEN
  125.     GOSUB validate_string_number
  126.     IF stringmathb$ = "invalid number" THEN EXIT SUB
  127.     GOSUB scientific_to_numeric
  128.  
  129. IF runningtotal$ = "" THEN
  130.     GOSUB validate_string_number
  131.     IF stringmathb$ = "invalid number" THEN EXIT SUB
  132.  
  133.     IF LEFT$(stringmathb$, 1) = "-" THEN
  134.         stringmathb$ = MID$(stringmathb$, 2)
  135.         n2sign$ = "-"
  136.     ELSE
  137.         n2sign$ = ""
  138.     END IF
  139.     GOSUB limit_round_convert
  140.     IF stringmathb$ = "overflow" THEN
  141.         n2sign$ = "": PRINT "Validated: "; stringmathb$: EXIT SUB
  142.     END IF
  143.     runningtotal$ = n2sign$ + stringmathb$: n2sign$ = ""
  144.     IF stringmathround$ <> "" THEN runningtotal$ = runningtotal$ + stringmathround$
  145.     PRINT "Validated: "; runningtotal$
  146.     IF INSTR(LCASE$(stringmathb$), "e") <> 0 THEN BEEP: GOSUB scientific_to_numeric
  147.     GOSUB validate_string_number
  148.     PRINT "Validated: "; stringmathb$
  149.     IF stringmathb$ = "invalid number" THEN EXIT SUB
  150.     IF INSTR(UCASE$(stringmathb$), "e") <> 0 THEN GOSUB scientific_to_numeric
  151. IF runningtotal$ <> "" THEN stringmatha$ = runningtotal$
  152.  
  153. SELECT CASE operator$
  154.     CASE "+", "-"
  155.         string_add_subtract:
  156.         IF INSTR(stringmatha$, ".") <> 0 THEN ' Evaluate sum for decimal fraction.
  157.             sumplace& = LEN(stringmatha$) - INSTR(stringmatha$, ".")
  158.             stringmatha$ = MID$(stringmatha$, 1, INSTR(stringmatha$, ".") - 1) + MID$(stringmatha$, INSTR(stringmatha$, ".") + 1) ' Strip out decimal
  159.         END IF
  160.         IF INSTR(stringmathb$, ".") <> 0 THEN ' Evaluate number for decimal fraction.
  161.             numplace& = LEN(stringmathb$) - INSTR(stringmathb$, ".")
  162.             stringmathb$ = MID$(stringmathb$, 1, INSTR(stringmathb$, ".") - 1) + MID$(stringmathb$, INSTR(stringmathb$, ".") + 1) ' Strip out decimal
  163.         END IF
  164.         IF sumplace& > numplace& THEN addsubplace& = sumplace& ELSE addsubplace& = numplace&
  165.         IF sumplace& > addsubplace& THEN
  166.             stringmatha$ = stringmatha$ + STRING$(sumplace& - addsubplace&, "0")
  167.         ELSEIF addsubplace& > sumplace& THEN
  168.             stringmatha$ = stringmatha$ + STRING$(addsubplace& - sumplace&, "0")
  169.         END IF
  170.         IF numplace& > addsubplace& THEN
  171.             stringmathb$ = stringmathb$ + STRING$(numplace& - addsubplace&, "0")
  172.         ELSEIF addsubplace& > numplace& THEN
  173.             stringmathb$ = stringmathb$ + STRING$(addsubplace& - numplace&, "0")
  174.         END IF ' END Decimal evaluations.
  175.  
  176.         IF LEFT$(stringmatha$, 1) = "-" THEN sign_input$ = "-" ELSE sign_input$ = "+"
  177.         IF LEFT$(stringmathb$, 1) = "-" THEN sign_total$ = "-" ELSE sign_total$ = "+"
  178.  
  179.         addsubsign% = 0
  180.         SELECT CASE sign_input$ + operator$ + sign_total$
  181.             CASE "+++", "+--"
  182.                 operator$ = "+"
  183.                 IF LEFT$(stringmathb$, 1) = "-" THEN stringmathb$ = MID$(stringmathb$, 2)
  184.             CASE "++-", "+-+"
  185.                 operator$ = "-"
  186.                 IF LEFT$(stringmathb$, 1) = "-" THEN stringmathb$ = MID$(stringmathb$, 2)
  187.                 IF VAL(stringmathb$) > VAL(stringmatha$) THEN SWAP stringmatha$, stringmathb$: addsubsign% = -1
  188.             CASE "---", "-++"
  189.                 operator$ = "-"
  190.                 IF LEFT$(stringmatha$, 1) = "-" THEN stringmatha$ = MID$(stringmatha$, 2)
  191.                 IF LEFT$(stringmathb$, 1) = "-" THEN stringmathb$ = MID$(stringmathb$, 2)
  192.                 IF VAL(stringmathb$) > VAL(stringmatha$) THEN SWAP stringmatha$, stringmathb$ ELSE addsubsign% = -1
  193.             CASE "--+", "-+-"
  194.                 operator$ = "+"
  195.                 IF LEFT$(stringmatha$, 1) = "-" THEN stringmatha$ = MID$(stringmatha$, 2)
  196.                 IF LEFT$(stringmathb$, 1) = "-" THEN stringmathb$ = MID$(stringmathb$, 2)
  197.                 addsubsign% = -1
  198.         END SELECT
  199.  
  200.         IF LEN(stringmatha$) > LEN(stringmathb$) THEN
  201.             stringmathb$ = STRING$(LEN(stringmatha$) - LEN(stringmathb$), "0") + stringmathb$
  202.         ELSEIF LEN(stringmatha$) < LEN(stringmathb$) THEN
  203.             stringmatha$ = STRING$(LEN(stringmathb$) - LEN(stringmatha$), "0") + stringmatha$
  204.         END IF
  205.         addsubx1$ = ""
  206.  
  207.         SELECT CASE operator$
  208.             CASE "+", "="
  209.                 FOR addsubii& = LEN(stringmatha$) TO 1 STEP -1
  210.                     addsubx1% = VAL(MID$(stringmatha$, addsubii&, 1)) + VAL(MID$(stringmathb$, addsubii&, 1)) + addsubcarry%
  211.                     IF addsubx1% > 9 THEN addsubx1% = addsubx1% - 10: addsubcarry% = 1 ELSE addsubcarry% = 0
  212.                     addsubx1$ = LTRIM$(STR$(addsubx1%)) + addsubx1$
  213.                 NEXT
  214.                 IF addsubcarry% THEN addsubx1$ = "1" + addsubx1$: addsubcarry% = 0
  215.                 GOSUB replace_decimal
  216.             CASE "-"
  217.                 FOR addsubii& = LEN(stringmatha$) TO 1 STEP -1
  218.                     addsubx1% = VAL(MID$(stringmatha$, addsubii&, 1)) - VAL(MID$(stringmathb$, addsubii&, 1)) + addsubcarry%
  219.                     IF addsubx1% < 0 THEN addsubx1% = addsubx1% + 10: addsubcarry% = -1 ELSE addsubcarry% = 0
  220.                     addsubx1$ = LTRIM$(STR$(addsubx1%)) + addsubx1$
  221.                 NEXT
  222.                 IF addsubx1$ <> "" AND addsubx1$ <> STRING$(LEN(addsubx1$), "0") THEN GOSUB replace_decimal
  223.                 DO UNTIL LEFT$(addsubx1$, 1) <> "0" ' Remove leading zeros.
  224.                     addsubx1$ = MID$(addsubx1$, 2)
  225.                 LOOP
  226.                 IF addsubx1$ = "" THEN
  227.                     addsubx1$ = "0": addsubsign% = 0
  228.                 ELSE
  229.                     IF addsubcarry% THEN addsubx1$ = "-" + addsubx1$: addsubcarry% = 0
  230.                 END IF
  231.         END SELECT
  232.  
  233.         IF addsubsign% THEN
  234.             IF LEFT$(addsubx1$, 1) = "-" THEN addsubx1$ = MID$(addsubx1$, 2) ELSE addsubx1$ = "-" + addsubx1$
  235.         END IF
  236.         stringmatha$ = addsubx1$: addsubx1$ = ""
  237.         IF operationdivision% THEN RETURN
  238.         stringmathb$ = stringmatha$: stringmatha$ = ""
  239.         IF LEFT$(stringmathb$, 1) = "-" THEN
  240.             stringmathb$ = MID$(stringmathb$, 2)
  241.             n2sign$ = "-"
  242.         ELSE
  243.             n2sign$ = ""
  244.         END IF
  245.         GOSUB limit_round_convert
  246.         IF stringmathb$ = "overflow" THEN n2sign$ = "": EXIT SUB
  247.         GOSUB sm_converter
  248.         runningtotal$ = n2sign$ + stringmathb$: n2sign$ = ""
  249.  
  250.     CASE "*"
  251.         string_multiply:
  252.         fac1$ = stringmatha$: fac2$ = stringmathb$ ' Make numbers whole numbers and remove any - sign.
  253.         IF LEFT$(fac1$, 1) = "-" THEN fac1$ = MID$(fac1$, 2): m_sign% = -1
  254.         IF LEFT$(fac2$, 1) = "-" THEN fac2$ = MID$(fac2$, 2): IF m_sign% THEN m_sign% = 0 ELSE m_sign% = -1
  255.         IF INSTR(fac1$, ".") <> 0 THEN m_decimal_places& = LEN(fac1$) - INSTR(fac1$, "."): fac1$ = MID$(fac1$, 1, INSTR(fac1$, ".") - 1) + MID$(fac1$, INSTR(fac1$, ".") + 1)
  256.         IF INSTR(fac2$, ".") <> 0 THEN m_decimal_places& = m_decimal_places& + LEN(fac2$) - INSTR(fac2$, "."): fac2$ = MID$(fac2$, 1, INSTR(fac2$, ".") - 1) + MID$(fac2$, INSTR(fac2$, ".") + 1)
  257.         FOR m_i& = LEN(fac2$) TO 1 STEP -1 ' Multiply each charter top and bottom.
  258.             m_k& = m_l&
  259.             m_x2$ = MID$(fac2$, m_i&, 1)
  260.             FOR m_j& = LEN(fac1$) TO 1 STEP -1
  261.                 m_x1$ = MID$(fac1$, m_j&, 1)
  262.                 IF m_product$ <> "" THEN
  263.                     m_add$ = LTRIM$(STR$(VAL(m_x1$) * VAL(m_x2$))) + STRING$(m_k&, "0")
  264.                     m_t& = 0: m_xproduct$ = "": m_carry% = 0
  265.                     DO ' Add multiplied characters together.
  266.                         m_x3$ = MID$(m_add$, LEN(m_add$) - m_t&, 1)
  267.                         m_x4$ = MID$(m_product$, LEN(m_product$) - m_t&, 1)
  268.                         IF m_x3$ = "" AND m_x4$ = "" THEN
  269.                             IF m_carry% THEN m_xproduct$ = "1" + m_xproduct$
  270.                             EXIT DO
  271.                         END IF
  272.                         m_g% = VAL(m_x3$) + VAL(m_x4$) + m_carry%
  273.                         IF m_g% >= 10 THEN m_g% = m_g% - 10: m_carry% = 1 ELSE m_carry% = 0
  274.                         m_xproduct$ = LTRIM$(STR$(m_g%)) + m_xproduct$
  275.                         m_t& = m_t& + 1
  276.                     LOOP
  277.                     m_product$ = m_xproduct$: m_xproduct$ = ""
  278.                 ELSE
  279.                     m_product$ = LTRIM$(STR$(VAL(m_x1$) * VAL(m_x2$))) + STRING$(m_k&, "0") ' First loop makes variable here.
  280.                 END IF
  281.                 m_k& = m_k& + 1 ' Adds trailing zeros multiplication
  282.             NEXT
  283.             m_l& = m_l& + 1 ' Used to reset value for m_k& adding one trailing zer for each loop.
  284.         NEXT
  285.         fac1$ = "": fac2$ = "": m_l& = 0: m_k& = 0: m_t& = 0
  286.         IF m_decimal_places& > LEN(m_product$) THEN m_product$ = STRING$(m_decimal_places& - LEN(m_product$), "0") + m_product$ ' Add any leading zeros to a decimal. Ex: .02 * .01 is factored as 002. It needs one leading zero before adding the decimal point, .0002.
  287.         IF m_decimal_places& AND m_product$ <> "0" THEN ' Replace any decimal point.
  288.             m_product$ = MID$(m_product$, 1, LEN(m_product$) - m_decimal_places&) + "." + MID$(m_product$, LEN(m_product$) - m_decimal_places& + 1)
  289.         END IF
  290.         DO UNTIL LEFT$(m_product$, 1) <> "0" ' Remove leading zeros.
  291.             m_product$ = MID$(m_product$, 2)
  292.         LOOP
  293.         IF m_decimal_places& THEN
  294.             DO UNTIL RIGHT$(m_product$, 1) <> "0" ' Remove trailing zeros in a decimal sum.
  295.                 m_product$ = MID$(m_product$, 1, LEN(m_product$) - 1)
  296.             LOOP
  297.         END IF
  298.         IF m_product$ = "" THEN m_product$ = "0": m_sign% = 0
  299.         IF RIGHT$(m_product$, 1) = "." THEN m_product$ = MID$(m_product$, 1, LEN(m_product$) - 1) ' Remove decimal from the end of an integer total.
  300.         IF operationdivision% THEN m_sign% = 0: RETURN
  301.         stringmathb$ = m_product$: m_product$ = "": GOSUB limit_round_convert
  302.         IF stringmathb$ = "overflow" THEN EXIT SUB
  303.         GOSUB sm_converter
  304.         runningtotal$ = stringmathb$: stringmathb$ = ""
  305.         IF m_sign% THEN runningtotal$ = "-" + runningtotal$: m_sign% = 0
  306.  
  307.     CASE "/"
  308.         operationdivision% = -1
  309.         divbuffer& = LEN(stringmathb$) - LEN(stringmatha$)
  310.         IF divbuffer& < 0 THEN divbuffer& = 0
  311.         d2dividend$ = stringmatha$
  312.         d1divisor$ = stringmathb$
  313.         IF LEFT$(d1divisor$, 1) = "0" AND LEN(d1divisor$) = 1 THEN PRINT "Division by zero not allowed.": END
  314.         IF LEFT$(d1divisor$, 1) = "-" THEN divsign% = -1: d1divisor$ = MID$(d1divisor$, 2)
  315.         IF LEFT$(d2dividend$, 1) = "-" THEN
  316.             IF divsign% THEN
  317.                 divsign% = 0
  318.             ELSE
  319.                 divsign% = -1
  320.             END IF
  321.             d2dividend$ = MID$(d2dividend$, 2)
  322.         END IF
  323.         IF INSTR(d1divisor$, ".") <> 0 THEN
  324.             DO UNTIL RIGHT$(d1divisor$, 1) <> "0"
  325.                 d1divisor$ = MID$(d1divisor$, 1, LEN(d1divisor$) - 1) ' Strip off trailing zeros
  326.             LOOP
  327.             divplace& = LEN(d1divisor$) - INSTR(d1divisor$, ".")
  328.             d1divisor$ = MID$(d1divisor$, 1, INSTR(d1divisor$, ".") - 1) + MID$(d1divisor$, INSTR(d1divisor$, ".") + 1) ' Strip off decimal point.
  329.             DO UNTIL LEFT$(d1divisor$, 1) <> "0"
  330.                 d1divisor$ = MID$(d1divisor$, 2) ' Strip off leading zeros for divisors smaller than .1
  331.             LOOP
  332.         END IF
  333.  
  334.         IF INSTR(d2dividend$, ".") <> 0 THEN
  335.             d2dividend$ = d2dividend$ + STRING$(divplace& - LEN(d2dividend$) - INSTR(d2dividend$, "."), "0") ' Add any zeros based on the length of dividend at decimal - length of divisor at decimal. If less than zero, nothing added.
  336.             divplace2& = INSTR(d2dividend$, ".")
  337.             DO UNTIL RIGHT$(d2dividend$, 1) <> "0"
  338.                 d2dividend$ = MID$(d2dividend$, 1, LEN(d2dividend$) - 1) ' Strip off trailing zeros
  339.             LOOP
  340.             d2dividend$ = MID$(d2dividend$, 1, INSTR(d2dividend$, ".") - 1) + MID$(d2dividend$, INSTR(d2dividend$, ".") + 1) ' Strip off decimal point.
  341.         ELSE
  342.             d2dividend$ = d2dividend$ + 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.
  343.             divplace& = 0
  344.         END IF
  345.         DO
  346.             DO
  347.                 divremainder& = divremainder& + 1: divremainder$ = divremainder$ + MID$(d2dividend$, divremainder&, 1)
  348.                 IF MID$(d2dividend$, divremainder&, 1) = "" THEN
  349.                     IF divremainder$ = STRING$(LEN(divremainder$), "0") AND LEN(quotient$) > LEN(d2dividend$) THEN divflag% = -1: EXIT DO
  350.                     divcarry& = divcarry& + 1
  351.                     IF divcarry& = 1 THEN divplace3& = divremainder& - 1
  352.                     IF divcarry& > limit&& + 1 + divbuffer& THEN
  353.                         divflag% = -2: EXIT DO
  354.                     END IF
  355.                     divremainder$ = divremainder$ + "0" ' No more digits to bring down.
  356.                 END IF
  357.                 IF LEN(divremainder$) > LEN(d1divisor$) OR LEN(divremainder$) = LEN(d1divisor$) AND divremainder$ >= d1divisor$ THEN EXIT DO
  358.                 quotient$ = quotient$ + "0"
  359.             LOOP
  360.             IF divflag% THEN divflag% = 0: EXIT DO
  361.             FOR div_i% = 9 TO 1 STEP -1
  362.                 stringmatha$ = LTRIM$(STR$(div_i%)): stringmathb$ = d1divisor$
  363.                 m_product$ = "": GOSUB string_multiply
  364.                 tempcutd$ = divremainder$ ' divremainder$ can be 00 or other leading zero values.
  365.                 DO
  366.                     IF LEN(tempcutd$) = 1 THEN EXIT DO
  367.                     IF LEFT$(tempcutd$, 1) = "0" THEN
  368.                         tempcutd$ = MID$(tempcutd$, 2)
  369.                     ELSE
  370.                         EXIT DO
  371.                     END IF
  372.                 LOOP
  373.                 IF LEN(tempcutd$) > LEN(m_product$) OR LEN(tempcutd$) = LEN(m_product$) AND m_product$ <= tempcutd$ THEN EXIT FOR
  374.             NEXT
  375.             quotient$ = quotient$ + LTRIM$(STR$(div_i%))
  376.             stringmatha$ = LTRIM$(STR$(div_i%)): stringmathb$ = d1divisor$
  377.             m_product$ = "": GOSUB string_multiply
  378.             operator$ = "-"
  379.             stringmatha$ = divremainder$
  380.             stringmathb$ = m_product$
  381.             GOSUB string_add_subtract
  382.             divremainder$ = stringmatha$
  383.             operator$ = "/"
  384.         LOOP
  385.         IF divplace& = 0 AND divplace2& = 0 THEN divplace& = divplace3&
  386.         IF divplace2& THEN divplace& = divplace& + divplace2& - 1
  387.         IF quotient$ = "" THEN divplace& = 0 ' dividend is zero.
  388.         IF divplace& OR divplace2& THEN
  389.             quotient$ = MID$(quotient$, 1, divplace&) + "." + MID$(quotient$, divplace& + 1)
  390.             DO UNTIL RIGHT$(quotient$, 1) <> "0"
  391.                 quotient$ = MID$(quotient$, 1, LEN(quotient$) - 1) ' Strip off trailing zeros
  392.             LOOP
  393.             IF RIGHT$(quotient$, 1) = "." THEN quotient$ = MID$(quotient$, 1, LEN(quotient$) - 1) ' Strip off abandoned decimal.
  394.         END IF
  395.         DO UNTIL LEFT$(quotient$, 1) <> "0"
  396.             quotient$ = MID$(quotient$, 2) ' Strip off leading zeros
  397.         LOOP
  398.         IF quotient$ = "" THEN quotient$ = "0": divsign% = 0
  399.         operationdivision% = 0
  400.         stringmathb$ = quotient$: quotient$ = "": GOSUB limit_round_convert
  401.         IF stringmathb$ = "overflow" THEN divsign% = 0: EXIT SUB
  402.         GOSUB sm_converter
  403.         runningtotal$ = stringmathb$: stringmathb$ = ""
  404.         IF divsign% THEN runningtotal$ = "-" + runningtotal$
  405. IF stringmathround$ <> "" THEN runningtotal$ = runningtotal$ + stringmathround$
  406.  
  407. validate_string_number:
  408. vsn_negcnt& = 0: vsn_poscnt& = 0: vsn_depresent& = 0: decimalcnt& = 0: vsn_numberpresent& = 0: vsn_zerospresent& = 0
  409. IF LEFT$(stringmathb$, 1) = "-" THEN stringmathb$ = MID$(stringmathb$, 2): sm_sign$ = "-" ELSE sm_sign$ = ""
  410. IF LEFT$(stringmathb$, 1) = "+" THEN IF sm_sign$ <> "-" THEN stringmathb$ = MID$(stringmathb$, 2) ELSE stringmathb$ = "invalid number": RETURN
  411. IF INSTR(UCASE$(stringmathb$), "D") OR INSTR(UCASE$(stringmathb$), "E") THEN ' Evaluate for Scientific Notation.
  412.     FOR sm_i& = 1 TO LEN(stringmathb$)
  413.         validatenum$ = MID$(UCASE$(stringmathb$), sm_i&, 1)
  414.         SELECT CASE validatenum$
  415.             CASE "+"
  416.                 IF vsn_depresent& THEN vsn_poscnt& = vsn_poscnt& + 1 ELSE stringmathb$ = "invalid number": RETURN
  417.             CASE "-"
  418.                 IF vsn_depresent& THEN vsn_negcnt& = vsn_negcnt& + 1 ELSE stringmathb$ = "invalid number": RETURN
  419.             CASE "0" TO "9"
  420.                 vsn_numberpresent& = -1
  421.             CASE "D", "E"
  422.                 vsn_depresent& = vsn_depresent& + 1
  423.                 IF decimalcnt& = 0 AND sm_i& <> 2 OR vsn_depresent& > 1 OR vsn_numberpresent& = 0 OR vsn_negcnt& > 1 OR vsn_poscnt& > 1 OR vsn_negcnt& = 1 AND vsn_poscnt& >= 1 THEN vsn_numberpresent& = 0: EXIT FOR
  424.                 vsn_numberpresent& = 0
  425.                 MID$(stringmathb$, sm_i&, 1) = "e" ' Standardize
  426.             CASE "."
  427.                 decimalcnt& = decimalcnt& + 1
  428.                 IF sm_i& <> 2 THEN vsn_numberpresent& = 0: EXIT FOR
  429.             CASE ELSE
  430.                 vsn_numberpresent& = 0: EXIT FOR
  431.         END SELECT
  432.     NEXT
  433.     IF decimalcnt& = 0 THEN stringmathb$ = MID$(stringmathb$, 1, 1) + "." + MID$(stringmathb$, 2) ' Standardize "."
  434.     IF vsn_numberpresent& = 0 OR vsn_negcnt& = 1 AND vsn_poscnt& = 1 OR decimalcnt& > 1 OR INSTR(stringmathb$, ".") <> 2 THEN stringmathb$ = "invalid number": RETURN
  435.     vsn_depresent& = INSTR(stringmathb$, "e")
  436.     sm_x$ = MID$(stringmathb$, vsn_depresent& + 1, 1) ' Standardize exponent "+" these two lines.
  437.     IF sm_x$ <> "+" AND sm_x$ <> "-" THEN stringmathb$ = MID$(stringmathb$, 1, vsn_depresent&) + "+" + MID$(stringmathb$, vsn_depresent& + 1)
  438.     IF MID$(stringmathb$, vsn_depresent& + 2, 1) = "0" THEN
  439.         IF MID$(stringmathb$, vsn_depresent& + 3, 1) <> "" THEN stringmathb$ = "invalid number": RETURN ' No leading zeros allowed in exponent notation.
  440.     END IF
  441.     jjed& = INSTR(stringmathb$, "e") ' Get position of notation.
  442.     valexpside$ = MID$(stringmathb$, jjed&) ' These two lines break up into number and notation
  443.     stringmathb$ = MID$(stringmathb$, 1, jjed& - 1) ' stringmathb$ is +- single digit whole number, decimal point and decimal number. valexpside$ is notation, sign and exponent.
  444.     DO UNTIL RIGHT$(stringmathb$, 1) <> "0" ' Remove any trailing zeros for number. Example 1.0d3 or 1.0000d3, etc.
  445.         stringmathb$ = MID$(stringmathb$, 1, LEN(stringmathb$) - 1)
  446.     LOOP
  447.     IF VAL(MID$(stringmathb$, 1, INSTR(stringmathb$, ".") - 1)) = 0 THEN
  448.         IF RIGHT$(stringmathb$, 1) = "." THEN
  449.             stringmathb$ = "0.e+0" ' Handles all types of zero entries.
  450.         ELSE
  451.             stringmathb$ = "invalid number": RETURN
  452.         END IF
  453.         RETURN
  454.     END IF
  455.     stringmathb$ = sm_sign$ + stringmathb$ + valexpside$
  456.     RETURN
  457.     FOR sm_i& = 1 TO LEN(stringmathb$)
  458.         validatenum$ = MID$(stringmathb$, sm_i&, 1)
  459.         SELECT CASE validatenum$
  460.             CASE "."
  461.                 decimalcnt& = decimalcnt& + 1
  462.             CASE "0"
  463.                 vsn_zerospresent& = -1
  464.             CASE "1" TO "9"
  465.                 vsn_numberpresent& = -1
  466.             CASE "$"
  467.             CASE ELSE
  468.                 stringmathb$ = "invalid number": RETURN
  469.         END SELECT
  470.     NEXT
  471.     IF decimalcnt& > 1 OR vsn_negcnt& > 1 OR vsn_poscnt& > 1 OR vsn_negcnt& >= 1 AND vsn_poscnt& >= 1 THEN
  472.         stringmathb$ = "invalid number": RETURN
  473.     END IF
  474.     IF INSTR(stringmathb$, "$") THEN GOSUB currency_validate
  475.     IF INSTR(stringmathb$, ",") THEN
  476.         GOSUB comma_validation
  477.         IF stringmathb$ = "invalid number" THEN RETURN
  478.         GOSUB comma_removal
  479.     END IF
  480.     IF RIGHT$(stringmathb$, 1) = "." THEN stringmathb$ = MID$(stringmathb$, 1, LEN(stringmathb$) - 1)
  481.     DO UNTIL LEFT$(stringmathb$, 1) <> "0" ' Strip off any leading zeros.
  482.         stringmathb$ = MID$(stringmathb$, 2)
  483.     LOOP
  484.     stringmathb$ = sm_sign$ + stringmathb$
  485.     IF INSTR(stringmathb$, ".") THEN
  486.         DO UNTIL RIGHT$(stringmathb$, 1) <> "0" ' Strip off any trailing zeros in a decimal.
  487.             stringmathb$ = MID$(stringmathb$, 1, LEN(stringmathb$) - 1)
  488.         LOOP
  489.     END IF
  490.     IF RIGHT$(stringmathb$, 1) = "." THEN stringmathb$ = MID$(stringmathb$, 1, LEN(stringmathb$) - 1)
  491.     IF vsn_numberpresent& = 0 THEN
  492.         IF vsn_zerospresent& THEN
  493.             stringmathb$ = "0"
  494.         ELSE
  495.             stringmathb$ = "invalid number"
  496.         END IF
  497.     END IF
  498.  
  499. ' Convert to commas, currency, S.N., etc.
  500. sm_converter:
  501. IF comma_display% THEN GOSUB comma_placement
  502. IF currency_display% THEN GOSUB currency_convert
  503. IF snconvert% THEN GOSUB numeric_to_scientific
  504.  
  505. ' Add in commas.
  506. comma_placement:
  507. GOSUB comma_prep
  508. sm_i& = 0: sm_j& = 0: sm_seed& = 0
  509. sm_seed& = LEN(temp_stringmathb1$) MOD 3: IF sm_seed& = 0 THEN sm_seed& = 3
  510. sm_m1& = LEN(temp_stringmathb1$)
  511. sm_m2& = (LEN(temp_stringmathb1$) - 1) \ 3
  512. sm_replace$ = SPACE$(sm_m1& + sm_m2&)
  513. DO WHILE sm_i& < sm_m1&
  514.     MID$(sm_replace$, sm_j& + 1, sm_seed& + 1) = MID$(temp_stringmathb1$, sm_i& + 1, sm_seed&) + ","
  515.     sm_i& = sm_i& + sm_seed&: sm_j& = sm_j& + sm_seed& + 1: sm_seed& = 3
  516. sm_replace$ = RTRIM$(sm_replace$)
  517. IF RIGHT$(sm_replace$, 1) = "," THEN
  518.     stringmathb$ = MID$(sm_replace$, 1, LEN(sm_replace$) - 1)
  519.     stringmathb$ = sm_replace$
  520. sm_replace$ = "": temp_stringmathb1$ = ""
  521.  
  522. ' Validate comma entry.
  523. comma_validation:
  524. GOSUB comma_prep
  525. IF INSTR(temp_stringmathb2$, ",") <> 0 OR temp_stringmathb1$ = STRING$(LEN(temp_stringmathb1$), ",") THEN
  526.     stringmathb$ = "invalid number" ' Decimal part has comma or entry is all commas.
  527.     FOR sm_i& = LEN(temp_stringmathb1$) TO 1 STEP -1
  528.         sm_j% = sm_j% + 1
  529.         IF sm_j% = 4 THEN
  530.             IF MID$(temp_stringmathb1$, sm_i&, 1) <> "," THEN stringmathb$ = "invalid number": EXIT FOR
  531.             sm_j% = 0
  532.         END IF
  533.     NEXT
  534.     IF stringmathb$ <> "invalid number" THEN
  535.         stringmathb$ = sm_sign$ + temp_stringmathb1$ + temp_stringmathb2$
  536.     END IF
  537. temp_stringmathb1$ = "": temp_stringmathb2$ = "": sm_i& = 0: sm_j% = 0: sm_sign$ = "": sm_dollar$ = ""
  538.  
  539. comma_removal:
  540. sm_i& = 0: sm_j& = 0: sm_seed& = 0
  541. sm_replace$ = SPACE$(LEN(stringmathb$))
  542.     sm_i& = INSTR(sm_seed& + 1, stringmathb$, ",")
  543.     IF sm_i& = 0 THEN EXIT DO
  544.     MID$(sm_replace$, sm_j& + 1, sm_i& - sm_seed& + 1) = MID$(stringmathb$, sm_seed& + 1, sm_i& - sm_seed& - 1)
  545.     sm_j& = sm_j& + sm_i& - sm_seed& - 1
  546.     sm_seed& = sm_i&
  547. stringmathb$ = RTRIM$(sm_replace$) + MID$(stringmathb$, sm_seed& + 1): sm_replace$ = ""
  548.  
  549. comma_prep:
  550. IF LEFT$(stringmathb$, 1) = "-" THEN stringmathb$ = MID$(stringmathb$, 2): sm_sign$ = "-"
  551. temp_stringmathb1$ = stringmathb$: stringmathb$ = ""
  552. IF INSTR(temp_stringmathb1$, ".") THEN
  553.     temp_stringmathb2$ = MID$(temp_stringmathb1$, INSTR(temp_stringmathb1$, ".")) ' Decimal part
  554.     temp_stringmathb1$ = MID$(temp_stringmathb1$, 1, INSTR(temp_stringmathb1$, ".") - 1) ' Non-decimal part
  555. IF LEFT$(temp_stringmathb1$, 1) = "$" THEN temp_stringmathb1$ = MID$(temp_stringmathb1$, 2): sm_dollar$ = "$"
  556.  
  557. currency_validate:
  558. IF LEFT$(stringmathb$, 2) = "$-" OR LEFT$(stringmathb$, 2) = "$+" THEN stringmathb$ = "invalid number": RETURN
  559. IF LEFT$(stringmathb$, 1) = "$" THEN stringmathb$ = MID$(stringmathb$, 2)
  560. IF INSTR(stringmathb$, "$") THEN stringmathb$ = "invalid number": RETURN
  561. sm_dollar$ = "$"
  562.  
  563. currency_convert:
  564. IF INSTR(UCASE$(stringmathb$), "D") <> 0 OR INSTR(UCASE$(stringmathb$), "E") <> 0 THEN GOSUB scientific_to_numeric
  565. IF INSTR(stringmathb$, ",") = 0 THEN GOSUB comma_placement
  566. IF INSTR(stringmathb$, ".") = 0 THEN stringmathb$ = stringmathb$ + ".00"
  567. IF RIGHT$(stringmathb$, 1) = "." THEN stringmathb$ = stringmathb$ + "00"
  568. IF MID$(stringmathb$, LEN(stringmathb$) - 2, 1) <> "." THEN stringmathb$ = stringmathb$ + "0"
  569. IF MID$(stringmathb$, LEN(stringmathb$) - 2, 1) <> "." THEN stringmathb$ = "invalid number": RETURN
  570. IF LEFT$(stringmathb$, 1) = "-" THEN stringmathb$ = MID$(stringmathb$, 2)
  571. stringmathb$ = sm_sign$ + "$" + stringmathb$
  572.  
  573. numeric_to_scientific:
  574. IF LEFT$(stringmathb$, 1) = "-" THEN stringmathb$ = MID$(stringmathb$, 2): n2sign$ = "-"
  575. IF INSTR(stringmathb$, ".") = 0 THEN exponentvalue&& = LEN(stringmathb$) - 1 ELSE exponentvalue&& = INSTR(stringmathb$, ".") - 2 ' Exponent is one less than number of digits for whole number an two less than the placement of the decimal point for a fraction.
  576. stringmathb$ = MID$(stringmathb$, 1, INSTR(stringmathb$, ".") - 1) + MID$(stringmathb$, INSTR(stringmathb$, ".") + 1)
  577. IF LEFT$(stringmathb$, 1) = "0" AND LEN(stringmathb$) > 1 OR exponentvalue&& = -1 THEN
  578.     DO UNTIL LEFT$(stringmathb$, 1) <> "0" ' Remove leading zeros to consider rounding.
  579.         stringmathb$ = MID$(stringmathb$, 2)
  580.         exponentvalue&& = exponentvalue&& - 1
  581.     LOOP
  582.     esign$ = "-"
  583.     esign$ = "+"
  584. DO UNTIL RIGHT$(stringmathb$, 1) <> "0" ' Remove trailing zeros.
  585.     stringmathb$ = MID$(stringmathb$, 1, LEN(stringmathb$) - 1)
  586. IF stringmathb$ = "" THEN stringmathb$ = "0": esign$ = "+": exponentvalue&& = 0
  587. stringmathb$ = LEFT$(stringmathb$, 1) + "." + MID$(stringmathb$, 2)
  588. IF stringmathb$ = "0." THEN n2sign$ = "": esign$ = "+"
  589. stringmathb$ = stringmathb$ + "e" + esign$ + LTRIM$(STR$(ABS(exponentvalue&&))) ' S.N formed here.
  590. IF stringmathb$ <> "overflow" THEN
  591.     stringmathb$ = n2sign$ + stringmathb$
  592. n2sign$ = "": esign$ = "": exponentvalue&& = 0
  593.  
  594. scientific_to_numeric:
  595. IF INSTR(UCASE$(stringmathb$), "D") THEN MID$(stringmathb$, INSTR(UCASE$(stringmathb$), "D"), 1) = "e"
  596. IF MID$(stringmathb$, INSTR(stringmathb$, "e") + 2) = "0" THEN ' The numeric value is the number without the zero exponent.
  597.     stringmathb$ = MID$(stringmathb$, 1, INSTR(stringmathb$, "e") - 1)
  598.     IF RIGHT$(stringmathb$, 1) = "." THEN stringmathb$ = MID$(stringmathb$, 1, LEN(stringmathb$) - 1)
  599.     RETURN
  600.     IF LEFT$(stringmathb$, 1) = "-" THEN stn_sign$ = "-": stringmathb$ = MID$(stringmathb$, 2)
  601.     stringmathb$ = MID$(stringmathb$, 1, INSTR(stringmathb$, ".") - 1) + MID$(stringmathb$, INSTR(stringmathb$, ".") + 1) ' Remove decimal point.
  602.     stn_i& = INSTR(stringmathb$, "e") - 1 ' Length of the numric part.
  603.     IF MID$(stringmathb$, INSTR(stringmathb$, "e") + 1, 1) = "-" THEN
  604.         stringmathb$ = "." + STRING$(VAL(MID$(stringmathb$, stn_i& + 3)) - 1, "0") + MID$(stringmathb$, 1, stn_i&) ' Decimal point followed by exponent value in zeros added in front of numeric part.
  605.     ELSE
  606.         IF stn_i& - 1 > VAL(MID$(stringmathb$, stn_i& + 3)) THEN stn_point$ = "." ' - 1 for decimal place. Ex 2.034d+2 is 2034 here where 3 places to the right . could be moved before . disappears. > so no trailing decimal results.
  607.         stringmathb$ = MID$(MID$(stringmathb$, 1, stn_i&), 1, VAL(MID$(stringmathb$, stn_i& + 3)) + 1) + stn_point$ + MID$(MID$(stringmathb$, 1, stn_i&), VAL(MID$(stringmathb$, stn_i& + 3)) + 2, stn_i& - VAL(MID$(stringmathb$, stn_i& + 3)) - 1) + STRING$(VAL(MID$(stringmathb$, stn_i& + 2)) - (stn_i& - 1), "0")
  608.     END IF
  609. IF stringmathb$ = "0" THEN stn_sign$ = ""
  610. stringmathb$ = stn_sign$ + stringmathb$
  611. stn_sign$ = "": stn_point$ = ""
  612.  
  613. limit_round_convert:
  614. ' Try SN if whole number is too large (as it may be trailing zeros) or decimal is beyond limit.
  615. IF LEFT$(stringmathb$, 2) = ".0" AND LEN(stringmathb$) > limit&& + 1 OR INSTR(stringmathb$, ".") > limit&& + 1 OR INSTR(stringmathb$, ".") = 0 AND LEN(stringmathb$) > limit&& THEN
  616.     IF limit&& > 1 THEN
  617.         GOSUB numeric_to_scientific ' Retry as S.N.
  618.         IF LEN(stringmathb$) > limit&& + 3 THEN ' Needs rounding.
  619.             snotation$ = MID$(stringmathb$, INSTR(UCASE$(stringmathb$), "E"))
  620.             exponentvalue&& = VAL(MID$(snotation$, 2)) ' Get positive or negative sign.
  621.             snexponent$ = MID$(stringmathb$, INSTR(UCASE$(stringmathb$), "E") + 2)
  622.             stringmathb$ = MID$(stringmathb$, 1, INSTR(UCASE$(stringmathb$), "E") - 1)
  623.             '''IF LEN(stringmathb$) + LEN(snexponent$) > limit&& + 1 AND exponentvalue&& >= limit&& THEN BEEP
  624.             IF exponentvalue&& >= limit&& THEN
  625.                 stringmathb$ = MID$(stringmathb$, 1, exponentvalue&& + 3)
  626.             ELSE
  627.                 stringmathb$ = MID$(stringmathb$, 1, limit&& - LEN(snexponent$) + 2)
  628.             END IF
  629.             GOSUB string_rounding_method
  630.             IF LEFT$(stringmathb$, 3) = "10." THEN
  631.                 stringmathb$ = "1." + MID$(stringmathb$, 4)
  632.                 ' Add one to the exponent.
  633.                 FOR round_i& = LEN(snexponent$) TO 1 STEP -1
  634.                     round_x$ = CHR$(ASC(MID$(snexponent$, round_i&, 1)) + 1)
  635.                     IF round_x$ <> CHR$(47) THEN ' Decimal point + 1. Ignore.
  636.                         IF round_x$ = CHR$(58) THEN
  637.                             MID$(snexponent$, round_i&, 1) = "0": carry$ = "1"
  638.                         ELSE
  639.                             MID$(snexponent$, round_i&, 1) = round_x$: carry$ = "": EXIT FOR
  640.                         END IF
  641.                     END IF
  642.                 NEXT
  643.                 snexponent$ = carry$ + snexponent$: carry$ = ""
  644.             END IF
  645.             stringmathb$ = stringmathb$ + MID$(snotation$, 1, 2) + snexponent$
  646.             IF LEN(snexponent$) + LEN(MID$(stringmathb$, 1, INSTR(UCASE$(stringmathb$), "E") - 1)) > limit&& + 1 THEN
  647.                 stringmathb$ = "overflow"
  648.             END IF
  649.             exponentvalue&& = 0
  650.         END IF
  651.     ELSE
  652.         IF INSTR(stringmathb$, ".") > 0 AND INSTR(stringmathb$, ".") <= limit&& THEN
  653.             stringmathb$ = MID$(stringmathb$, 1, limit&& + 2)
  654.             IF round_total% = -1 AND RIGHT$(stringmathb$, 1) > "4" THEN
  655.                 GOSUB string_rounding_method
  656.             ELSE
  657.                 stringmathb$ = MID$(stringmathb$, 1, limit&& + 1)
  658.                 IF show_rounding% THEN stringmathround$ = "r"
  659.             END IF
  660.         ELSE
  661.             stringmathb$ = "overflow"
  662.         END IF
  663.     END IF
  664.     RETURN
  665. IF LEN(stringmathb$) > limit&& AND INSTR(stringmathb$, ".") = 0 OR LEN(stringmathb$) > limit&& + 1 AND INSTR(stringmathb$, ".") <> 0 THEN
  666.     IF INSTR(stringmathb$, ".") = 0 THEN
  667.         stringmathb$ = MID$(stringmathb$, 1, limit&& + 1)
  668.     ELSE
  669.         stringmathb$ = MID$(stringmathb$, 1, limit&& + 2)
  670.     END IF
  671.     GOSUB string_rounding_method
  672.     IF LEN(stringmathb$) > limit&& + lrc_decimalpoint& THEN ' Ex: limit&& = 4 9999.9 1.e+4
  673.         GOSUB numeric_to_scientific
  674.     ELSE
  675.         IF LEN(stringmathb$) > limit&& + lrc_decimalpoint& THEN stringmathb$ = "overflow"
  676.     END IF
  677.  
  678. replace_decimal:
  679. IF addsubplace& THEN
  680.     addsubx1$ = STRING$(addsubplace& - LEN(addsubx1$), "0") + addsubx1$
  681.     addsubx1$ = MID$(addsubx1$, 1, LEN(addsubx1$) - addsubplace&) + "." + MID$(addsubx1$, LEN(addsubx1$) - addsubplace& + 1)
  682.     DO UNTIL RIGHT$(addsubx1$, 1) <> "0" ' Remove trailing zeros in a decimal sum.
  683.         addsubx1$ = MID$(addsubx1$, 1, LEN(addsubx1$) - 1)
  684.         addsubplace& = addsubplace& - 1
  685.     LOOP
  686.     IF RIGHT$(addsubx1$, 1) = "." THEN addsubx1$ = MID$(addsubx1$, 1, LEN(addsubx1$) - 1) ' Number is now an integer.
  687.  
  688. string_rounding_method:
  689. IF INSTR(stringmathb$, ".") THEN lrc_decimalpoint& = 1 ELSE lrc_decimalpoint& = 0
  690. IF MID$(stringmathb$, LEN(stringmathb$), 1) > "4" THEN
  691.     FOR round_i& = LEN(stringmathb$) - 1 TO 1 STEP -1
  692.         round_x$ = CHR$(ASC(MID$(stringmathb$, round_i&, 1)) + 1)
  693.         IF round_x$ <> CHR$(47) THEN ' Decimal point + 1. Ignore.
  694.             IF round_x$ = CHR$(58) THEN
  695.                 MID$(stringmathb$, round_i&, 1) = "0": carry$ = "1"
  696.             ELSE
  697.                 MID$(stringmathb$, round_i&, 1) = round_x$: carry$ = "": EXIT FOR
  698.             END IF
  699.         END IF
  700.     NEXT
  701.     stringmathb$ = carry$ + MID$(stringmathb$, 1, LEN(stringmathb$) - 1): carry$ = ""
  702.     IF show_rounding% THEN stringmathround$ = "R"
  703.     stringmathb$ = MID$(stringmathb$, 1, LEN(stringmathb$) - 1)
  704.     IF show_rounding% THEN stringmathround$ = "r"
  705.  
  706. IF lrc_decimalpoint& THEN
  707.     DO UNTIL RIGHT$(stringmathb$, 1) <> "0"
  708.         stringmathb$ = MID$(stringmathb$, 1, LEN(stringmathb$) - 1)
  709.     LOOP
  710.     IF stringmathb$ = "" OR stringmathb$ = "." THEN stringmathb$ = "0": lrc_decimalpoint& = 0
  711.     IF RIGHT$(stringmathb$, 1) = "." AND exponentvalue&& = 0 THEN
  712.         stringmathb$ = MID$(stringmathb$, 1, LEN(stringmathb$) - 1): lrc_decimalpoint& = 0
  713.     END IF

Pete
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Raven_Singularity on April 10, 2019, 01:16:43 pm
Was the rounding oddity about "round to the nearest even number" functionality?
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Pete on April 10, 2019, 02:11:43 pm
Do you mean in the screen shot I posted? If so, I was worried when my routine left the figure as all 9's. I thought the only way it wouldn't round up to 649146.5 is if the digit after that last 9 in 649146.49999999999 was an 8. So I checked it with a calculator, and it was an 8. That got rounded up as a nine, the last digit of the 17 digit limit I set for that operation. So it did round correctly. Also, I use a capital "R" at the end, to indicate it rounded up. A lowercase "r" would indicate it rounded down.

Pete
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Raven_Singularity on April 10, 2019, 04:37:58 pm
I only know that BASIC in some form rounds up or down based on even whole numbers:

... -6, -4, -2, 0, 2, 4, 6 ...

So 1.5 and 2.5 would both round to 2.  I have never liked this style of rounding, I learned 0.5 rounds up, and -0.5 rounds up or down, depending on the type of rounding.  I don't like even-number rounding, because it creates uneven data sets:

0, 0, 2, 2, 4, 4 ...

Anyhow, probably not related, but one more odd way BASIC can round things internally.
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Pete on April 10, 2019, 06:51:13 pm
I used round up for 5 and over, round down for 4 and under. That's a standard. The other way I think is called "Banker's Rounding." That's where if the rounding digit is 5, and there are no other non-zero digits trailing it, you look at the number immediately preceding the rounding number, and round up only if that preceding number is even.

I'm still on the fence if I would consider changing to that.

Pete

Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Raven_Singularity on April 10, 2019, 06:58:23 pm
I used round up for 5 and over, round down for 4 and under. That's a standard. The other way I think is called "Banker's Rounding." That's where if the rounding digit is 5, and there are no other non-zero digits trailing it, you look at the number immediately preceding the rounding number, and round up only if that preceding number is even.

I'm still on the fence if I would consider changing to that.

Pete

Banker's Rounding sounds like a more "fair" form of rounding.  0.5 is halfway, going up or down are both wrong, heh.  Dividing whether it goes up or down half the time is fair, but I didn't understand what you meant about the other digits causing the rounding.
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Pete on April 10, 2019, 07:43:03 pm
Example: 2.315 and 2.325 round to hundredths.

In Banker's Rounding, we have both digits in the thousandth ending in a 5, so both numbers are eligible to be rounded.

Next we look at the digit in front of each 5.

2.315 (The digit in front of the 5 is a 1, and odd number.)

2.325 (The digit in front of the 5 is a 2, an even number.)

In Banker's Rounding, we round up the value of an odd number in front of the trailing "5" digit to an even number and we leave even numbers alone.

So, 2.315 gets changed to 2.325 and 2.125 stays as 2.325.

Now, to get to hundredths, we drop the 5 off of each, and end up with both being...

2.32

And I'm glad I did an example this time, because I just re-read my last post and I should have ended that last sentence with odd, not even, as in...

I used round up for 5 and over, round down for 4 and under. That's a standard. The other way I think is called "Banker's Rounding." That's where if the rounding digit is 5, and there are no other non-zero digits trailing it, you look at the number immediately preceding the rounding number, and round up only if that preceding number is odd.

So both round to 2.32—instead of 2.325 rounding up to 2.33—when rounded off to the nearest 100th. The rationale for the third rule is that approximately half of the time the number will be rounded up and the other half of the time it will be rounded down.

Sorry for messing that up in my previous post.

Pete
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Jack002 on April 11, 2019, 10:50:10 am
I don't like rounding. I say if you want to round to the 3rd digit past the decimal then show me the 4th digit. I thought that rounding affects the accuracy of calculations negatively, but I can't seem to find people saying that.

My rounding pun
Farmer Brown had 290 cows but it was 300 once he rounded up
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: jack on April 11, 2019, 11:53:16 am
maybe your pun should be "Farmer Brown thought he had 290 cows but it was 300 once he rounded up"
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Raven_Singularity on April 11, 2019, 12:31:11 pm
"After losing his 290 cows, Farmer Brown later that day rounded up his 300 cows."
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: SMcNeill on April 11, 2019, 02:42:27 pm
You guys should quit picking on us farmers, when it comes to math.  We’re rather good at it.  To illustrate the point, let me tell you a tale my Grandpa told.

Back in the early 1900’s, everyone’s child was in a rush to head off to the city and get “educated” proper.  It turns out, Grandpa’s neighbor’s children all went off and headed to college.  While the oldest was getting his PHD, and the youngest was just starting, their father passed away.  All three children rushed home for the funeral and reading of the will.

In the will, their father divided up his herd of horses in the following manner.  The first born was supposed to get one half of the herd, the second child was supposed to get one third, while the youngest boy should’ve gotten one ninth.

Now, since their father had seventeen horses, they had no idea of what to do.  No mater how they proposed settling the issue, they just couldn’t make the numbers work to everyone’s satisfaction.  Luckily, my grandfather stopped by around that time to offer his condolences on their loss.  When presented with their quandary, he gave them his horse as a bereavement give.

With eighteen horses now, the oldest got nine, the second child got six, and the third child got two.  Taking the one that was left over as payment for services rendered, Grandfather left later that evening on the same horse he rode in on...

Who says farmers don’t know math?  ;D
Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Jack002 on April 11, 2019, 04:19:57 pm
Well it looks like I drug this thread off topic. I'm denying all responsibility.

Rounding tho. Anyone else think its inaccurate? I think if you divide 1 by 3 and get 0.333333 report all your digits and don't hide or round anything. Calculators are now reporting in fractional mode and preserve value when its not a repeating integer.
We use numbers in base 10 (usually) to represent an idea. They are not the idea, they are the representation.

Title: Re: Yuck String Math or What's wrong with FOO?
Post by: Pete on April 11, 2019, 04:46:02 pm
You guys should quit picking on us farmers, when it comes to math.  We’re rather good at it.  To illustrate the point, let me tell you a tale my Grandpa told.

Back in the early 1900’s, everyone’s child was in a rush to head off to the city and get “educated” proper.  It turns out, Grandpa’s neighbor’s children all went off and headed to college.  While the oldest was getting his PHD, and the youngest was just starting, their father passed away.  All three children rushed home for the funeral and reading of the will.

In the will, their father divided up his herd of horses in the following manner.  The first born was supposed to get one half of the herd, the second child was supposed to get one third, while the youngest boy should’ve gotten one ninth.

Now, since their father had seventeen horses, they had no idea of what to do.  No mater how they proposed settling the issue, they just couldn’t make the numbers work to everyone’s satisfaction.  Luckily, my grandfather stopped by around that time to offer his condolences on their loss.  When presented with their quandary, he gave them his horse as a bereavement give.

With eighteen horses now, the oldest got nine, the second child got six, and the third child got two.  Taking the one that was left over as payment for services rendered, Grandfather left later that evening on the same horse he rode in on...

Who says farmers don’t know math?  ;D

But what about that other family that had 17 donkeys, and only 2 kids? I heard the grandpa lost his ass in that deal. Too bad, he should have just told each to take 8 live ones, and sawed the one remaining down the middle. Oops, I forgot, farmers don't like half-assed solutions. :D

@Jack: I know. I used to have a repeating number output method in my code, but I took it out when I discovered calculators that don't support that these days. A lot will report 2 / 3 as: .6666666666666667