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

0 Members and 1 Guest are viewing this topic.

Offline jack

  • Seasoned Forum Regular
  • Posts: 408
Re: Yuck String Math or What's wrong with FOO?
« Reply #15 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.

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
Re: Yuck String Math or What's wrong with FOO?
« Reply #16 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
« Last Edit: March 01, 2019, 02:03:34 pm by Pete »
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline jack

  • Seasoned Forum Regular
  • Posts: 408
Re: Yuck String Math or What's wrong with FOO?
« Reply #17 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

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
Re: Yuck String Math or What's wrong with FOO?
« Reply #18 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
« Last Edit: March 02, 2019, 06:30:51 pm by Pete »
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
Re: Yuck String Math or What's wrong with FOO?
« Reply #19 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
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline codeguy

  • Forum Regular
  • Posts: 174
Re: Yuck String Math or What's wrong with FOO?
« Reply #20 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.  

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
Re: Yuck String Math or What's wrong with FOO?
« Reply #21 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
« Last Edit: March 05, 2019, 01:57:37 am by Pete »
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
Re: Yuck String Math or What's wrong with FOO?
« Reply #22 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
« Last Edit: March 05, 2019, 02:00:35 am by Pete »
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
Re: Yuck String Math or What's wrong with FOO?
« Reply #23 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
« Last Edit: March 05, 2019, 06:56:05 pm by Pete »
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline codeguy

  • Forum Regular
  • Posts: 174
Re: Yuck String Math or What's wrong with FOO?
« Reply #24 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.

Offline Jack002

  • Forum Regular
  • Posts: 123
  • Boss, l wanna talk about arrays
Re: Yuck String Math or What's wrong with FOO?
« Reply #25 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
QB64 is the best!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
Re: Yuck String Math or What's wrong with FOO?
« Reply #26 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.
  [ You are not allowed to view this attachment ]  

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.
« Last Edit: March 14, 2019, 11:53:59 am by bplus »

Offline Jack002

  • Forum Regular
  • Posts: 123
  • Boss, l wanna talk about arrays
Re: Yuck String Math or What's wrong with FOO?
« Reply #27 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
QB64 is the best!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
Re: Yuck String Math or What's wrong with FOO?
« Reply #28 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, ...
« Last Edit: March 14, 2019, 10:44:09 pm by bplus »

Offline Jack002

  • Forum Regular
  • Posts: 123
  • Boss, l wanna talk about arrays
Re: Yuck String Math or What's wrong with FOO?
« Reply #29 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.
QB64 is the best!