Author Topic: How to ensure accuracy of calculations  (Read 4420 times)

0 Members and 1 Guest are viewing this topic.

Offline kservice

  • Newbie
  • Posts: 25
    • View Profile
How to ensure accuracy of calculations
« on: October 18, 2021, 01:48:02 am »
Faced the problem of accuracy of calculations. I bring an elementary example in which I have errors.

  [ You are not allowed to view this attachment ]  

It seems obvious that A2 = 0.8; B2 = 0.3; C2 = 0.4; d2 = 1.5; d = 2425301.5.
Why do I have such inaccurate results?

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: How to ensure accuracy of calculations
« Reply #1 on: October 18, 2021, 02:17:14 am »
Computers don't think in Base10. Base2 is all they do. That's why I invented, wait for it, String math!

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

There are some other routines you may want to search the forum for. using _INTEGER64 for example; but for really large numbers calculated with a computer program, my position is you need to think in strings.

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

Offline jack

  • Seasoned Forum Regular
  • Posts: 408
    • View Profile
Re: How to ensure accuracy of calculations
« Reply #2 on: October 18, 2021, 02:33:25 am »
@kservice you are using single precision variables and some of the values that you assigned are more than a single can handle, a single only holds about 7.2 decimal digits, use double instead
single holds about 24 bits * log10(2) = 7.2247198959355 decimal digits
double holds about 53 bits * log10(2) = 15.954589770191 decimal digits
_Float holds about 64 bits * log10(2) = 19.265919722494 decimal digits
[edit] was wrong about single
« Last Edit: October 18, 2021, 08:06:08 am by jack »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: How to ensure accuracy of calculations
« Reply #3 on: October 18, 2021, 03:45:23 am »
What's you're seeing is the nature of floating point math.

In base 10, what's 1/3 in decimal form? 

0.333333333333333333333333333333333333...more 3333333333..... more 33333333....

It's a non-terminating decimal value.

1 / 3 * 3 should equal 0.9999999999999999...   Yet we call it 1.  Doesn't that mean if we calculate 0.3333333333333333 * 3, that we're going to get a wrong answer when we call it 1?

Absolutely.  Base-10 math has rounding points where it says, "Close enough to X value, so let's call it X."  You can't perfectly represent non-terminating values with a terminating technology (most calculators only display X number of digits after all), and we're used seeing that in base-10 math.

Computers, on the other hand, think in base-2 math.  They hold the exact same problem, but with different values that we're not used to. 

For example, take a cake and cut me EXACTLY 1/10th of it, just by cutting and adding together halves.

Cut in half = .5..  too big.
Cut in half = .25.. too big
Cut in half = .125... too big
Cut in half = ..0625.. too small!  We want .1
Cut in half and together = 0.9375... closer, but still not there!

Try as you might, you can never take those perpetually smaller halves and be able to make 1/10th of a cake with them.  At some point, you'll hit the atomic level and have .099999999871342 chunks of cake, but you'll never make 0.1 with base-2 numbers.

That's just the limit of the base system we're using! 



So how the heck do banks track money with computers then?  It's impossible, right?!!

It would be if the used floating point math! 

Instead, they keep everything as INTEGER values.  The bank doesn't count that you have 1,234.56 dollars in an account.  You've got 123,456 PENNIES in an account.  All the math is based on INTEGER calculations.

So for your examples, you would do something like:

DEFLNG A-Z
a = 11743008
a1 = (a \ 10) * 10 'since we're limiting to 1 decimal place, this is equivalent to INT stripping off the decimal values
a2 = a - a1

Instead of counting 1,174,300.8 dollars, we instead counted, and did math with, 11,743,008 dimes.

Stick with integers if you want perfect precision.  Floating point values are built so "close enough is good enough". 
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Qwerkey

  • Forum Resident
  • Posts: 755
    • View Profile
Re: How to ensure accuracy of calculations
« Reply #4 on: October 18, 2021, 05:12:29 am »
Computers don't think in Base10.

@kservice , Remember this at all times.

Offline RhoSigma

  • QB64 Developer
  • Forum Resident
  • Posts: 565
    • View Profile
Re: How to ensure accuracy of calculations
« Reply #5 on: October 18, 2021, 05:20:02 am »
This seems to be a neverending story, just in the moment you forgot about the last math precision discussion, the next new member pops up the next one. No idea how many of those discussions are already in this forum.

It shows a simple human trait, we are lazy, too lazy to even take the time to carefully search, if our problems are already discussed before, it's much easier to start the 100th new thread on it.

And you know what, to get to this conclusion I don't even need extended math precision.

Waiting for the next math precision thread.....
My Projects:   https://qb64forum.alephc.xyz/index.php?topic=809
GuiTools - A graphic UI framework (can do multiple UI forms/windows in one program)
Libraries - ImageProcess, StringBuffers (virt. files), MD5/SHA2-Hash, LZW etc.
Bonus - Blankers, QB64/Notepad++ setup pack

Offline kservice

  • Newbie
  • Posts: 25
    • View Profile
Re: How to ensure accuracy of calculations
« Reply #6 on: October 18, 2021, 05:37:36 am »
This seems to be a neverending story, just in the moment you forgot about the last math precision discussion, the next new member pops up the next one. No idea how many of those discussions are already in this forum.

It shows a simple human trait, we are lazy, too lazy to even take the time to carefully search, if our problems are already discussed before, it's much easier to start the 100th new thread on it.

And you know what, to get to this conclusion I don't even need extended math precision.

Waiting for the next math precision thread.....

I would be grateful if you just twist my nose. If I found the answer (the decision, and not theory! The causes of the problem were known to me) - I would not write here.

Offline kservice

  • Newbie
  • Posts: 25
    • View Profile
Re: How to ensure accuracy of calculations
« Reply #7 on: October 18, 2021, 05:40:03 am »
Thank you very much for prompt help. The theory is good, I am grateful for this lesson. But what to do in my constriction case? As I understood: 1) Instead of Single, use Double 2) Calculations to produce with integers, multiplying the initial number of previously, for example, by 10 or 100. I understood correctly?
I tried to multiply by 10 and 100. Received different results.

   [ You are not allowed to view this attachment ]  

In the first case, garbage is less, but it is more correct to use 100.

  [ You are not allowed to view this attachment ]  
But what to do with unnecessary numbers after the comma? Round And the actions are extra, and I am afraid of the appearance of errors. How to be?


« Last Edit: October 18, 2021, 05:47:07 am by kservice »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: How to ensure accuracy of calculations
« Reply #8 on: October 18, 2021, 07:46:01 am »
If you're going to use DOUBLE, you don't need to multiply by 10.  You'd want to do that if you were using integer variable types -- INTEGER, LONG, or _INTEGER64 -- and you'd want to use it with all variables to eliminate any decimals from your program.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline kservice

  • Newbie
  • Posts: 25
    • View Profile
Re: How to ensure accuracy of calculations
« Reply #9 on: October 18, 2021, 08:03:58 am »
OK. But what to do with unnecessary numbers after the comma?

Offline jack

  • Seasoned Forum Regular
  • Posts: 408
    • View Profile
Re: How to ensure accuracy of calculations
« Reply #10 on: October 18, 2021, 08:11:48 am »
you could use print using to display only the number of digits that make sense in your application
alternatively if the math computations are not intensive and it only involves basic arithmetic then you could try Pete's string-math
here's your code
Code: QB64: [Select]
  1. DefDbl A-D
  2.  
  3. a = 1174300.8
  4. a1 = Int(a)
  5. a2 = (a - a1)
  6. Print "a2="; a2
  7. b = 561000.3
  8. b1 = Int(b)
  9. b2 = (b - b1)
  10. Print "b2="; b2
  11. c = 690000.4
  12. c1 = Int(c)
  13. c2 = (c - c1)
  14. Print "c2="; c2
  15. d = a + b + c
  16. Print "d="; d
  17. d2 = a2 + b2 + c2
  18. Print "d2="; d2
  19.  
perhaps you could write a trim function to print your values
« Last Edit: October 18, 2021, 08:31:59 am by jack »

Offline Dimster

  • Forum Resident
  • Posts: 500
    • View Profile
Re: How to ensure accuracy of calculations
« Reply #11 on: October 18, 2021, 10:26:13 am »
There are a lot of great discussions on the same or very similar topics. This one on accuracy and use of string math is a good example. Is there no way of corralling the discussions with a descriptive topic and accessing them in either Samples or Learning??

Offline Qwerkey

  • Forum Resident
  • Posts: 755
    • View Profile
Re: How to ensure accuracy of calculations
« Reply #12 on: October 18, 2021, 11:13:19 am »
I think that @Dimster has a very good point.  There are such repeated topics which the Librarians might curate and present in "Learning" (certainly not "Samples").  Might be the sort of thing for the Junior Librarian (c'est moi) to do.  Will discuss with the Senior Librarian ( @bplus ).

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: How to ensure accuracy of calculations
« Reply #13 on: October 18, 2021, 12:49:46 pm »
I'm with Pete, write your own String Math routines, that and your own Interpreters, these are tremendous learning experiences for coders, oh also your own data base stuff and drawing routines... Isn't that the spirit of Basic? DIY! We don't need no dang competitions. We share our learning and resources.

Meanwhile for @kservice  learn lesson's on Type limits as hinted at in comments above.

Man!, I admire non English speakers taking on QB64! Hang in there :)

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: How to ensure accuracy of calculations
« Reply #14 on: October 18, 2021, 01:08:54 pm »
@Qwerkey There you go going off-topic again! :D

Definitely worth discussing, though: https://www.qb64.org/forum/index.php?topic=4305.0

Oh, and @bplus Wow Mark, you nailed it DIY. That is the spirit of BASIC.

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