QB64.org Forum

Active Forums => QB64 Discussion => Topic started by: JohnUKresults on May 26, 2021, 07:18:59 am

Title: Maths accuracy
Post by: JohnUKresults on May 26, 2021, 07:18:59 am
Hi all

Trying to do this (although with figures from an array rather than directly programmed numbers - getting the same result):
Code: QB64: [Select]
  1. finishseconds! = 39818.38
  2. startseconds! = 34200.77
  3. PRINT finishseconds! - startseconds!

I should get 5617.61 but for some reason I'm getting 5617.609.

Why and how can I be sure to get the exact result I need? Should I use a different type of number?

I'm using type ! because the numbers could possibly go up to 86400 (number of seconds in 24 hours) and I need 2 decimal point accuracy (as that's what my timing system outputs).

Is there a better option, or alternatively, how can I make sure the answer always comes back rounded up to 5617.61?

Thanks
Title: Re: Maths accuracy
Post by: OldMoses on May 26, 2021, 08:11:36 am
I'm of the opinion that QB64 does have some sort of rounding error.

In limiting things to 2 decimal places I generally use:
Code: QB64: [Select]
  1. rounded_result = INT(number * 100) / 100
  2.  

but even that doesn't work for some computations in QB64. Which I find inconceivable. How could INT not work to limit decimal places?

When it really shows its tail is when it resorts to exponential notation, at least you're just getting a third decimal place.

I hope someone figures it out and fixes it one day.
Title: Re: Maths accuracy
Post by: JohnUKresults on May 26, 2021, 08:59:23 am
Hi. Thanks for the reply.

For the moment I've had to resort to a somewhat Heath Robinson method to get this sorted, as this 3 decimal place issue could cause cumulative timing issues when fractions get added together (or subtracted)

This is what I'm currently doing. There must be a more elegant solution out there!

Firststartsec$ is a variable used to store the seconds equivalent of the first runner's time of day start time crossing the start line.

Subtracting a runner's finish time from that first start gives a gun elapsed time (as opposed to their personal time which is from when they crossed the start line until they finished)

Code: QB64: [Select]
  1.                finishseconds& = finishseconds! * 100
  2.                firststartsecs& = VAL(firststartsecs$) * 100
  3.                guntime& = finishseconds& - firststartsecs&
  4.                result$ = _TRIM$(STR$(guntime&))
  5.                length = LEN(result$)
  6.                sec1$ = LEFT$(result$, length - 2)
  7.                sec2$ = RIGHT$(result$, 2)
  8.                elapsed$ = sec1$ + "." + sec2$
Title: Re: Maths accuracy
Post by: OldMoses on May 26, 2021, 09:25:15 am
I'm wondering if it's related to a problem that was discussed here:

https://www.qb64.org/forum/index.php?topic=3852.0

In that case, I believe, it was concerning printing values, and not the values themselves.

PRINT USING "#####.##" might help with outputting results, but I'm not sure it will help with data accuracy

EDIT:
You might also try:
Code: QB64: [Select]
  1. rounded_result = _ROUND(number * 100) / 100
  2.  
Title: Re: Maths accuracy
Post by: FellippeHeitor on May 26, 2021, 10:56:50 am
I'm of the opinion that QB64 does have some sort of rounding error.

Floating point math is at play, guys. Please look it up (I'd explain it further, but I don't know it as deeply. What I do know is that we, as a whole, do not easily grasp what is going on when floating point math is at play, but the answer is always: read up on floating point math).

For reference (mandatory read, everyone... I post it often): https://0.30000000000000004.com

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

Please do not limit yourselves to the screenshot above. Do actually go to https://0.30000000000000004.com, read it up, follow the links in there, read them up.
Title: Re: Maths accuracy
Post by: JohnUKresults on May 26, 2021, 11:19:22 am
Thanks Felippe

That's all very interesting, but it doesn't answer the question as to whether anyone has a solution that they have tried and works :)

If it can't be done then I'll stick with my Heath Robinson fix. Just slows things down a bit when doing 5000 to 6000 calculations, but it's only a few seconds in the grand scheme of things!

Cheers
Title: Re: Maths accuracy
Post by: jack on May 26, 2021, 12:24:11 pm
you could use a decimal math library but it's use would be cumbersome without function/operator overloading
Title: Re: Maths accuracy
Post by: bplus on May 26, 2021, 12:30:02 pm
@JohnUKresults,

What is the Heath Robinson fix?

Is it like the @SMcNeill fix for scientific notation? Removes it!
https://www.qb64.org/forum/index.php?topic=1511.msg122690#msg122690
Title: Re: Maths accuracy
Post by: SMcNeill on May 26, 2021, 12:31:52 pm
Hi all

Trying to do this (although with figures from an array rather than directly programmed numbers - getting the same result):
Code: QB64: [Select]
  1. finishseconds! = 39818.38
  2. startseconds! = 34200.77
  3. PRINT finishseconds! - startseconds!

I should get 5617.61 but for some reason I'm getting 5617.609.

Why and how can I be sure to get the exact result I need? Should I use a different type of number?

I'm using type ! because the numbers could possibly go up to 86400 (number of seconds in 24 hours) and I need 2 decimal point accuracy (as that's what my timing system outputs).

Is there a better option, or alternatively, how can I make sure the answer always comes back rounded up to 5617.61?

Thanks

As Fellippe says, what you're seeing is the natural effect of binary math.  We see the same result with decimal math (what we normally use, which is base-10), but folks don't quite comprehend why it pops up like it does in binary math.   Let me break down what's happening for you, really simply:

In decimal-math, what is 1/3?  It's 0.333333333333333333333333333333333..... with more threes to infinity.  There is no perfect decimal representation of 1/10.  At some point, we have to have a break off point for precision -- whatever we decide we need it to be, whether that's 2 digits(.33), 6 digits (.333333), or 12 digits (.333333333333).   We can NEVER truly represent 1/10 as a decimal, so for practicality's sake, we break it off at some point.

But what happens when we take that 1/10, store it as a decimal, and then multiply it by 3?   1/3 * 3 = 1, and everyone knows that.  But if we were to write it down on paper, we'd work the math right to left and get .333333 * 3 = .999999...  The more precision we have, the closer we come to the proper answer of 1, but we'll always be just a weeee bit short from it.

Which is why computers tend to calculate figures to one place past their precision level and then round.  .333333 (if we use a 6 digit precision limit) * 3 actually becomes .9999999, with 7 digits of precision, and then it rounds up to 1, to try and reduce errors as much as possible.  Many times, it works just fine, but sometimes it doesn't and values get off by a small fraction -- especially when calculating values inside loops.  A .000001 difference looped 100000 times could make your answer be off by several points.

And this is why, when coding with real precision numbers, you shouldn't make IF checks absolute. 

IF x / y = 0.1 THEN...    This is very likely going to fail for you, as x / y might end up being 0.09999999999874 instead of 0.1.

The best way to do these type of checks with real variable types is to do them based upon a natural level of variance:

If ABS(x / y - 0.1) < 0.00001 THEN...   Here we're saying that as long as our value is *almost* 0.1, then we're going to allow it and just work with it as a rounding error from the floating point math.



Now, why do we see this type of error when we do with binary math?   Even something as simple as 1/10 can end up glitching out on us, with our PC!!

Think of binary math, which is base-2 values.  Let's count from 0 to 10 in binary:

0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010

So that's 0 to 10, represented in binary values.  But what are fractional values?

Just like with decimal math, they're represented on the right side of the period:

0000.1000 = 1/2
0000.0100 = 1/4
0000.0010 = 1/8
0000.0001 = 1/16

But the problem here is that just like how we can't represent 1/3 in decimal math, there's no perfect representation of 1/10.  The more precision we use, the closer we can come to representing 1/10, but we'll never be able to do it.  You just can't represent 1/10 with binary numbers.



So what's the work around?

If one is talking about two digit values, such as with banks and other organizations which deal with money, they simply NEVER use decimals.  The bank doesn't count the number of dollars you have with them; they count the number of pennies!!  Your cash isn't stored as $123.32; it's stored as 12332 pennies and then always processed as INTEGER values.  Integer math is limited in scope, with much smaller numbers, but it doesn't have a lack of precision.  As long as the values you're dealing with are less than 10^15th power, or whatever, you shouldn't have any type of issues calculating them.

Swapping over to integer math might be the answer you seek.

From the minor example you posted though, I'd think the easiest solution in this case is simply to use PRINT using to force a rounding to 2-digit precision.

Code: [Select]
finishseconds! = 39818.38
startseconds! = 34200.77
PRINT USING "#####.##"; finishseconds! - startseconds!

If that doesn't work for you, then you can always swap over to using string math for your calculations, and then you can set the level of precision to whatever point you need it to become, but that's a complete different topic for another day....



The overall end result of what you're seeing is just the natural state of computers and binary math.  If precision has to be absolute, use INTEGER values instead of REAL values -- count pennies instead of dollars.    If it's just a matter of, "...BUT IT LOOKS FUNKY!!", then fix that with PRINT USING and format it to a point where you find it visually pleasing and suitable for your needs.
Title: Re: Maths accuracy
Post by: JohnUKresults on May 26, 2021, 12:39:06 pm
Thanks

Appreciate all of that detailed explanation, but it's not for printing (hat would be simple), it's for storage and then manipulation, so I'll have to work with the integer method and string slicing I outlined earlier in my post:

Code: QB64: [Select]
  1.                finishseconds& = finishseconds! * 100
  2.                firststartsecs& = VAL(firststartsecs$) * 100
  3.                guntime& = finishseconds& - firststartsecs&
  4.                result$ = _TRIM$(STR$(guntime&))
  5.                length = LEN(result$)
  6.                sec1$ = LEFT$(result$, length - 2)
  7.                sec2$ = RIGHT$(result$, 2)
  8.                elapsed$ = sec1$ + "." + sec2$
Title: Re: Maths accuracy
Post by: OldMoses on May 26, 2021, 12:50:06 pm
OK, it is the 'nature of the beast' as they say.

So if I try to force a decimal place with:

INT(number * 100) / 100     ' or _ROUND, etc.

then am I just reintroducing the same floating point error again when dividing by 100?

It would seem then that PRINT USING is the only answer.

Title: Re: Maths accuracy
Post by: bplus on May 26, 2021, 01:15:22 pm
Oh Heath Robinson is UK's version of Rube Goldberg LOL!

Hey accountants don't even use decimal point everything is in pennies, don't know what Heath Robinson uses for pennies in UK but you could just do everything in Long 100th's of a Second and forget about Floating Point Math or whatever they call that in UK LOL!

What's more elegant than no bloody dots! or do you guys use commas?
Title: Re: Maths accuracy
Post by: moises1953 on May 26, 2021, 01:17:58 pm
As FellippeHeitor has brilliantly put it, the precision of binary floating point representation has its limitations.

For the precise representation of times it is better to use double precision or if it is enough milliseconds in LONG, which lasts for tens of years.

Test this:
Code: QB64: [Select]
  1. sectimer# = TIMER(0.001)
  2. milisectimer& = sectimer# * 1000
  3.  
  4. secini# = 34200.77
  5. secfin# = 39818.38
  6.  
  7. milisecini& = secini# * 1000
  8. milisecfin& = secfin# * 1000
  9.  
  10. PRINT USING " #####.###    "; secini#, secfin#, sectimer#
  11. PRINT milisecini&, milisecfin&, milisectimer&
  12.  
  13.  
Title: Re: Maths accuracy
Post by: George McGinn on May 26, 2021, 02:03:32 pm
About 3 years back (maybe more) I had this issue on mobile devices, except it was worse. Instead of accuracy to 1^308 I was getting 2^59! I was developing Apps for the space sciences, and I ran afoul of the very limitations on mathematical precision.

So a friend and I developed string math routines, that were accurate to 100 decimal places! For division, which was one of the routines I wrote, it's accurate to infinity (can be, but you can specify any number of decimal places).

I can ask my friend if it is OK to convert these routines to either QB64 or create C++ code that may be included in either a header file for use when needing precision beyond the limitations of the math hardware co-processor. Also, we did benchmark time tests, and we found that the division runs faster than the software/hardware combo developed by Intel (division is done on the system level using software and the math co-processor chip).

Since it is written in SmartBASIC on mobile devices, it will take some effort to convert it so it works here.

If interested, let me know.
Title: Re: Maths accuracy
Post by: bplus on May 26, 2021, 02:26:44 pm
Hey @George McGinn

I have String Math here:
https://www.qb64.org/forum/index.php?topic=2921.msg121886#msg121886

Somewhere in toolbox here:
https://www.qb64.org/forum/index.php?topic=1511.msg107143#msg107143

And my oh Interpreter uses it:
https://www.qb64.org/forum/index.php?topic=3723.msg130797#msg130797

Basic +-*/ and Inverse to get 1 / any Integer to how many ever places you like, default for divide is 100 digits but  mult numerator by inverse of denominator gives that precision at least.

Can work out powers and trig but no one has called for it.
Title: Re: Maths accuracy
Post by: SMcNeill on May 26, 2021, 02:33:44 pm
OK, it is the 'nature of the beast' as they say.

So if I try to force a decimal place with:

INT(number * 100) / 100     ' or _ROUND, etc.

then am I just reintroducing the same floating point error again when dividing by 100?

It would seem then that PRINT USING is the only answer.

Ir’s not the same floating point error, but a *new* floating point error.  Usually the computer can sort out when to round up/down a few decimal places (or else 1/3 * 3 would forever be 0.999999999999 instead of 1), but sometimes it still screws up.  As you mention: it’s simply the nature of the beast.

Only solution I can offer in this case is just to up your precision level as much as possible, and hope it fixes the issue.  Or write a custom function to convert to string and manually insert the decimal:

FUNCTION ex## (x##)
   ex## = INT(x## * 100)
   temp$ = STR$(ex##)
   temp1$ = LEFT$(temp$, LEN(temp$) - 2) + “.” + RIGHT$(temp$, 2)
   ex## = VAL(temp1$)
END FUNCTION

Floating point math removed — problem removed.

(You may need my routine to remove/convert scientific notation, if your values are too large.  They’re here on the forums if you need them.)
Title: Re: Maths accuracy
Post by: JohnUKresults on May 26, 2021, 02:37:34 pm
Thanks all

I just need to 2 decimal points, to take one number from the other and store in a variable (not to print)

I think I have a solution which probably does the job, if a bit untidily

Best wishes to all
Title: Re: Maths accuracy
Post by: bplus on May 26, 2021, 02:56:55 pm
Quote
(You may need my routine to remove/convert scientific notation, if your values are too large.  They’re here on the forums if you need them.)

Yeah I posted the link in reply #7

You know I have same problem with adding machine I made from QB64. It really makes you wonder what's going when the 3rd decimal shows up and all you are doing is adding and substracting up to 2 digit floats.

Well no more, I am taking my own advice from reply #11 and dumping the the bloody dot's.
Title: Re: Maths accuracy
Post by: George McGinn on May 26, 2021, 02:57:58 pm
Good, because mine would take some time to convert to QB64.

I noticed that for division, it looks like you do an inverse of the denominator then multiply.

In mine, I create a factor table based on the quotient, and do subtraction instead, carrying over the remainder until I'm subtracting from 0 or I reach the preset decimal places.

Thanks for showing me. I will add it to my programs when needed.





Hey @George McGinn

I have String Math here:
https://www.qb64.org/forum/index.php?topic=2921.msg121886#msg121886

Somewhere in toolbox here:
https://www.qb64.org/forum/index.php?topic=1511.msg107143#msg107143

And my oh Interpreter uses it:
https://www.qb64.org/forum/index.php?topic=3723.msg130797#msg130797

Basic +-*/ and Inverse to get 1 / any Integer to how many ever places you like, default for divide is 100 digits but  mult numerator by inverse of denominator gives that precision at least.

Can work out powers and trig but no one has called for it.
Title: Re: Maths accuracy
Post by: George McGinn on May 26, 2021, 03:02:18 pm
I also coded square root and root functions in mine.

I can work just on those and add them to your routines, if anyone needs them.

Hey @George McGinn

Can work out powers and trig but no one has called for it.
Title: Re: Maths accuracy
Post by: bplus on May 26, 2021, 03:08:39 pm
I also coded square root and root functions in mine.

I can work just on those and add them to your routines, if anyone needs them.

If you do root functions that means you did powers for floats? I'd be interested in that.

I found a way to do powers with Sqr(2) and base 2 math but not sure I want to try that with string math.
Title: Re: Maths accuracy
Post by: George McGinn on May 26, 2021, 08:42:30 pm
@bplus

Here is the code for the square root and n'th root of a number.  I checked my source, and for the x^y, all I did was perform a loop with the multiplication function. Your code should work the same.

I converted this code from SmartBASIC on the mobile device (allows to code BASIC for iOS apps) which is what the code was originally written for. I had to make some changes to get it to work in QB64, and I verified the results.

Code: QB64: [Select]
  1.  
  2. DECLARE FUNCTION root_2 (A)
  3. DECLARE FUNCTION root_n (A, n)
  4.  
  5. '*** square root function
  6. A = 4503599627370496: r = root_2(A): PRINT "square root of "; A; " equals "; result
  7. A = 125: r = root_2(A): PRINT "square root of "; A; " equals "; result
  8. A = 100: r = root_2(A): PRINT "square root of "; A; " equals "; result
  9.  
  10. '*** n'th root function
  11. A = 100000: n = 6: r = root_n(A, n): PRINT n; "'th root of "; A; " equals "; result
  12. PRINT: PRINT "==================================================================="
  13.  
  14.  
  15. FUNCTION root_2 (A AS DOUBLE)
  16.     x = A / 2: precision = 0.000001: max_iterations = 30
  17.     FOR i = 1 TO max_iterations
  18.         dx = (x - A / x) / 2: x = x - dx
  19.         IF ABS(dx) < precision THEN i = max_iterations
  20.     NEXT i
  21.     result = STR$(x)
  22.  
  23. ' n'th root function
  24. ' n is positive integer (x^(n-1)) by repeated multiplication)
  25. ' n as positive real number requires exponentiation function
  26. '
  27. FUNCTION root_n (A AS DOUBLE, n AS INTEGER)
  28.     x = A / 2: precision = 0.000001: max_iterations = 30
  29.     FOR i = 1 TO max_iterations
  30.         dx = (x - A / x ^ (n - 1)) / n: x = x - dx
  31.         IF ABS(dx) < precision THEN i = max_iterations
  32.     NEXT i
  33.     result = STR$(x)
  34.  

If you do root functions that means you did powers for floats? I'd be interested in that.

I found a way to do powers with Sqr(2) and base 2 math but not sure I want to try that with string math.
Title: Re: Maths accuracy
Post by: STxAxTIC on May 26, 2021, 08:51:43 pm
Speaking of accuracy...

  [ This attachment cannot be displayed inline in 'Print Page' view ]  
Title: Re: Maths accuracy
Post by: George McGinn on May 26, 2021, 11:27:42 pm
@bplus @STxAxTIC

I screwed up the code, my bad. I made changes to that by mistake and didn't change it back (I was fixing the precision issue with square roots).

Change line 34 to be:
Code: [Select]
x = A / 2: precision = 0.001: max_iterations = 100


The value for precision needs to be 0.001 and max_iterations needs to be changed from 30 to 100.

Here is the new program:

Code: Text: [Select]
  1. DIM SHARED result AS STRING
  2. DIM SHARED x AS DOUBLE
  3. DIM SHARED A AS DOUBLE
  4.  
  5. DECLARE FUNCTION root_2 (A)
  6. DECLARE FUNCTION root_n (A, n)
  7.  
  8. '*** square root function
  9. A = 4503599627370496: r = root_2(A): PRINT "square root of "; A; " equals "; result
  10. A = 125: r = root_2(A): PRINT "square root of "; A; " equals "; result
  11. A = 100: r = root_2(A): PRINT "square root of "; A; " equals "; result
  12. PRINT
  13.  
  14. '*** n'th root function
  15. A = 100000: n = 6: r = root_n(A, n): PRINT n; "'th root of "; A; " equals "; result
  16. PRINT: PRINT "==================================================================="
  17. PRINT
  18.  
  19.  
  20. FUNCTION root_2 (A AS DOUBLE)
  21.     x = A / 2: precision = 0.00001: max_iterations = 100
  22.     FOR i = 1 TO max_iterations
  23.         dx = (x - A / x) / 2: x = x - dx
  24.         IF ABS(dx) < precision THEN i = max_iterations
  25.     NEXT i
  26.     result = STR$(x)
  27. END FUNCTION
  28.  
  29. ' n'th root function
  30. ' n is positive integer (x^(n-1)) by repeated multiplication)
  31. ' n as positive real number requires exponentiation function
  32. '
  33. FUNCTION root_n (A AS DOUBLE, n AS INTEGER)
  34.     x = A / 2: precision = 0.001: max_iterations = 100
  35.     FOR i = 1 TO max_iterations
  36.         dx = (x - A / x ^ (n - 1)) / n: x = x - dx
  37.         IF ABS(dx) < precision THEN i = max_iterations
  38.     NEXT i
  39.     result = STR$(x)
  40. END FUNCTION
  41.  


Speaking of accuracy...

  [ This attachment cannot be displayed inline in 'Print Page' view ]
Title: Re: Maths accuracy
Post by: bplus on May 27, 2021, 12:36:09 am
@bplus

Here is the code for the square root and n'th root of a number.  I checked my source, and for the x^y, all I did was perform a loop with the multiplication function. Your code should work the same.

I converted this code from SmartBASIC on the mobile device (allows to code BASIC for iOS apps) which is what the code was originally written for. I had to make some changes to get it to work in QB64, and I verified the results.

Code: QB64: [Select]
  1.  
  2. DECLARE FUNCTION root_2 (A)
  3. DECLARE FUNCTION root_n (A, n)
  4.  
  5. '*** square root function
  6. A = 4503599627370496: r = root_2(A): PRINT "square root of "; A; " equals "; result
  7. A = 125: r = root_2(A): PRINT "square root of "; A; " equals "; result
  8. A = 100: r = root_2(A): PRINT "square root of "; A; " equals "; result
  9.  
  10. '*** n'th root function
  11. A = 100000: n = 6: r = root_n(A, n): PRINT n; "'th root of "; A; " equals "; result
  12. PRINT: PRINT "==================================================================="
  13.  
  14.  
  15. FUNCTION root_2 (A AS DOUBLE)
  16.     x = A / 2: precision = 0.000001: max_iterations = 30
  17.     FOR i = 1 TO max_iterations
  18.         dx = (x - A / x) / 2: x = x - dx
  19.         IF ABS(dx) < precision THEN i = max_iterations
  20.     NEXT i
  21.     result = STR$(x)
  22.  
  23. ' n'th root function
  24. ' n is positive integer (x^(n-1)) by repeated multiplication)
  25. ' n as positive real number requires exponentiation function
  26. '
  27. FUNCTION root_n (A AS DOUBLE, n AS INTEGER)
  28.     x = A / 2: precision = 0.000001: max_iterations = 30
  29.     FOR i = 1 TO max_iterations
  30.         dx = (x - A / x ^ (n - 1)) / n: x = x - dx
  31.         IF ABS(dx) < precision THEN i = max_iterations
  32.     NEXT i
  33.     result = STR$(x)
  34.  

Thanks George, I could translate Root_2 for SQR into string math, in fact we were discussing this algo not that long ago.

The algo for Root wont work because it is  for ^ that I need to create a Function. Of course we could do ^ for integers by simple multiplying that many times but that won't help for any real number. So I need a power function done using only +-*/ or inverse functions. I know one way by converting to binary but maybe too Heath Robinson :)
Title: Re: Maths accuracy
Post by: George McGinn on May 27, 2021, 09:15:33 am
@bplus, you should be able to create a function for the power of by doing the following:


You should have all the code already written to do this.
Title: Re: Maths accuracy
Post by: George McGinn on May 27, 2021, 10:44:45 am
@bplus

NOTE: While you can raise a number to a fractional power (ie: 5^5.5), this code will not do this, as the FOR/NEXT loop must be done as an integer. Once I solve this, I will post the new routine. For now, as long as the power of is an integer, this will work fine.

Here is the code for your program, Math Regulator, to add the power of functionality:

Code for the ELSE IF statements checking for the operator (Note: add ix1 to the DIM AS INTEGER in function mr$:
Code: QB64: [Select]
  1.     DIM adp AS INTEGER, bdp AS INTEGER, dp AS INTEGER, lpop AS INTEGER, ix1 AS INTEGER, cbhold as LONG
  2.  
  3. ...
  4.  
  5.  
  6. '*** Add in Power Of Function
  7.     ELSEIF op$ = "^" THEN
  8.        cbhold = VAL(cb$)
  9.         cb$ = ca$
  10.         FOR ix1 = 1 TO cbhold
  11.             IF ix1 = 1 THEN
  12.                 postOp$ = sgn$ + mult$(ca$, "1")
  13.             ELSE
  14.                 ca$ = postOp$
  15.                 postOp$ = sgn$ + mult$(ca$, cb$)
  16.             END IF
  17.         NEXT ix1

How to test this code:
Code: QB64: [Select]
  1. PRINT "3125 ? "
  2. PRINT mr$("5", "^", "5") ' OK
  3. PRINT "25 ? "
  4. PRINT mr$("5", "^", "2") ' OK

This should work for your power of needs.

George
Title: Re: Maths accuracy
Post by: George McGinn on May 27, 2021, 11:12:25 am
Here is another way you can use 'power of' which I use in the iOS app.

Supposed you wanted to do, say 999999 * 2^53?

The Math Regulator with my changes can do this by using the following code (Put at the top of the MR source):
Code: QB64: [Select]
  1. DIM result$
  2.  
  3.  
  4. ' debug tests
  5.  
  6. '*** Test 999999 * 2^53
  7. result$ = mr$("2", "^", "53")
  8. result$ = mr$("999999", "*", result$)
  9. PRINT "The result of 999999 * 2^53 = "; result$

The results should be: 9,007,190,247,541,737,259,008
Title: Re: Maths accuracy
Post by: bplus on May 27, 2021, 12:03:10 pm
Hmm...

Like I said a ^ b where b is integer, I get that though god help us if that integer is huge ;-))

But if b were float?

What if I mult b by 10 until it is all integer? mult a (b  * 10 ^ Integer times) then what? divide by some log of 10 Integer times? Yeah, nah... ;-))

1.2 ^ 3.5 = 1.2 ^ (35/10) does this get us closer?


Title: Re: Maths accuracy
Post by: jack on May 27, 2021, 12:15:02 pm
for integer powers you can do something like this
Code: QB64: [Select]
  1.     'take x to an integer power
  2.     Dim As _Integer64 z, y
  3.     Dim As Integer n
  4.     y = x
  5.     n = Abs(e)
  6.     z = 1
  7.     While n > 0
  8.         While (n Mod 2) = 0 ' while n is even
  9.             n = n \ 2
  10.             y = y * y
  11.         Wend
  12.         n = n - 1
  13.         z = y * z
  14.     Wend
  15.     ipow = z
  16.  
Title: Re: Maths accuracy
Post by: jack on May 27, 2021, 12:27:40 pm
1.2 ^ 3.5 = 1.2 ^ (35/10) does this get us closer?
1.2^3.5=1.2^3*sqr(1.2)
Title: Re: Maths accuracy
Post by: bplus on May 27, 2021, 12:40:52 pm
1.2^3.5=1.2^3*sqr(1.2)

Ah yes, that is a hint as to how I do powers (of real numbers not just integers) by converting to binary and then using sqr(2) and half of that and half of that...

Oh it's Sqr of Sqr(2) and on and on... (not half of half of half ...  but sqr(sqr(Sqr(2)))...)
Code: QB64: [Select]
  1. _Title "Power Function by bplus"
  2. 'QB64 X 64 version 1.2 20180228/86  from git b301f92
  3.  
  4. ' started 2018-07-23  Naalaa has no power function (or operator), so I wrote a power function for it.
  5. ' ''Power pack the short version.txt
  6. ' ''written for Naalaa 6 by bplus posted 2018-07-23
  7. ' ''extracted from large test of fractions, random number functions, string... in Power test.txt
  8.  
  9. ' OMG the crazy thing worked! It produced decent estimates of roots given the limitations of precision...
  10. ' Now I want to see how well it works with far greater precision available. So here we are, looking to see
  11. ' how this function compares to the regualar ^ operator in QB64.
  12.  
  13. 'from Naalaa comments:
  14. ' The main purpose of this code: to demo a power function for real numbers,
  15.  
  16. ' I had an idea for how real numbers to the power of real numbers might be done ie x ^ y = ?
  17. ' This means that not only can you take SQR of a number, you can get cube or cube root, quartic, 5th 6th... roots and any multiple
  18.  
  19. ' It came from this simple idea
  20. ' 2 ^ 3.5 = 2 ^ 3 * 2 ^ .5 = 8 * Sqr(2)
  21. ' 3 ^ 3.5 = 3 ^ 3 * 3 ^ .5 = 27 * Sqr(3)
  22.  
  23. ' so 2 ^ 3.25 = 2 ^ 3 * 2 ^ .25
  24. ' what is 2 ^ .25 ?  It is sqr(sqr(2)) !
  25.  
  26. ' likewise 2 ^ 3.125 = 2 ^ 3 * 2 ^ 1/8
  27. ' what is 2 ^ 1/8 ? It is sqr(sqr(sqr(2))) !
  28.  
  29. ' any decimal can be written as a sum of fraction powers of 2 ie 1/2^n, as any integer can be written in powers of 2.
  30. ' in binary expansions
  31. ' 1/2 = .1       or .5 base 10
  32. ' 1/4 = .01      or .25 base 10
  33. ' 1/8 = .001     or .125 base 10
  34. ' 1/16 = .0001   or .0625 base 10
  35.  
  36. ' So with binary expansion of decimal, we can SQR and multiply our way to an estimate
  37. ' of any real number to the power of another real number using binary expansion of
  38. ' the decimal parts as long as we stay in Naalaa integer limits and are mindful of precision.
  39.  
  40. Const wW = 800
  41. Const wH = 600
  42. Screen _NewImage(wW, wH, 32)
  43. _ScreenMove 360, 60
  44.  
  45.     Print "Testing the power(x, pow) function:"
  46.     Input "(nothing) quits, Please enter a real number to raise to some power. x = "; x
  47.     If x = 0 Then Exit Do
  48.     Input "(nothing) quits, Please enter a real number for the power. pow = ", pw
  49.     If pw = 0 Then Exit Do
  50.     result = power(x, pw)
  51.     Print result; " is what we estimate for"; x; " raised to power of"; pw
  52.     Print x ^ pw; " is what the ^ operator gives us."
  53.     Print
  54. Print "Far from precise of course, but this code is clear proof of concept!"
  55. Print " OMG, it worked!!!"
  56.  
  57.  
  58. ' A power function for real numbers (small ones but still!)
  59. ' x to the power of pow
  60. Function power## (x As _Float, pow As _Float)
  61.     'this sub needs 2 other subs
  62.     'bExpand20$
  63.     'split though for this split is overkill
  64.     Dim build As _Float
  65.     ReDim s(0) As String
  66.     r$ = "0" + Str$(pow) 'in case pow starts with decimal
  67.     Split s$(), r$, "."
  68.     integer$ = s$(0)
  69.     build = 1.0
  70.     If integer$ <> "0" Then
  71.         p = Val(integer$)
  72.         For i = 1 To p
  73.             build = build * x
  74.         Next
  75.     End If
  76.     If UBound(s$) = 0 Then power = build: Exit Function
  77.     'that takes care of integer part,
  78.     'now for the fraction part convert decimal to fraction
  79.     n$ = s$(1)
  80.     ld = Len(n$)
  81.     While Right$(n$, 1) = "0"
  82.         n$ = Left$(n$, ld - 1)
  83.         ld = Len(n$)
  84.     Wend
  85.     denom& = 10
  86.     For i = 2 To ld
  87.         denom& = denom& * 10
  88.     Next
  89.     numer& = Val(n$)
  90.     'OK for bExpand20$ don't have to simplify and that saves us having to extract n and d again from n/d
  91.     bs$ = bExpand60$(numer&, denom&)
  92.     'at moment we haven't taken any sqr of x
  93.     runningXSQR = x
  94.     'run through all the 0's and 1's in the bianry expansion of the fraction part of the power float
  95.     For i = 1 To Len(bs$)
  96.         'this is the matching sqr of the sqr of the sqr... of x
  97.         runningXSQR = Sqr(runningXSQR)
  98.         'for every 1 in the expansion, multiple our build with the running sqr of ... sqr of x
  99.         If Mid$(bs$, i, 1) = "1" Then build = build * runningXSQR
  100.     Next
  101.     'our build should be a estimate or x to power of pow
  102.     power = build
  103.  
  104. 'write a series of 1s and 0s that represent the decimal fraction n/d in binary 60 places long
  105. Function bExpand60$ (nOver&, d&)
  106.     Dim b As _Float, r As _Float
  107.     ' b for base
  108.     b = 0.5
  109.     ' r for remainder
  110.     r = nOver& / d&
  111.     ' s for string$ 0's and 1's that we will build and return for function value
  112.     s$ = ""
  113.     ' f for flag to stop
  114.     f% = 0
  115.     ' c for count to track how far we are, don't want to go past 20
  116.     c% = 0
  117.     While f% = 0
  118.         If r < b Then
  119.             s$ = s$ + "0"
  120.         Else
  121.             s$ = s$ + "1"
  122.             If r > b Then
  123.                 r = r - b
  124.             Else
  125.                 f% = 1
  126.             End If
  127.         End If
  128.         c% = c% + 1
  129.         If c% >= 60 Then f% = 1
  130.         b = b * 0.5
  131.     Wend
  132.     bExpand60$ = s$
  133.  
  134. Sub Split (arr() As String, mystr As String, delim As String)
  135.     Dim c As String * 1, curpos As Long, arrpos As Long, le As Long
  136.     curpos = 1: arrpos = 0
  137.     le = Len(mystr)
  138.     Do Until (curpos > le)
  139.         c = Mid$(mystr, curpos, 1) ' Get 1 ctr
  140.         If (c <> delim) Then
  141.             arr(arrpos) = arr(arrpos) + c
  142.         Else
  143.             arrpos = arrpos + 1
  144.             ReDim _Preserve arr(arrpos + 1) As String 'AS STRING required or it assumes the default type which is SINGLE
  145.         End If
  146.         curpos = curpos + 1
  147.     Loop
  148.     ReDim _Preserve arr(arrpos) As String
  149.  
  150.  
  151.  
  152.  

Now all I need to do is covert it to string math functions ;-))
Title: Re: Maths accuracy
Post by: George McGinn on May 27, 2021, 01:12:48 pm
Passing power to root can be used to get your result or if you don’t have a whole solution, to express the result as a root.

For example, using my n'th root function, you could solve 1,2^3.5 as:

X = 10th root of 1.2^35, which = 1.2^3.5. (Answer is 1.892929159)

1.2 becomes the radicando (root content), 10 becomes the root index and 35 remains as exponent of 1.2

Passing power to root can be used to get your result.

Just spitballing, but if you Check for the y in x^y not = to its modulus will tell the function it needs to calculate the power using the root.

I will try and code that for your Math Regulator program.

George


Hmm...

Like I said a ^ b where b is integer, I get that though god help us if that integer is huge ;-))

But if b were float?

What if I mult b by 10 until it is all integer? mult a (b  * 10 ^ Integer times) then what? divide by some log of 10 Integer times? Yeah, nah... ;-))

1.2 ^ 3.5 = 1.2 ^ (35/10) does this get us closer?
Title: Re: Maths accuracy
Post by: Dav on June 12, 2021, 10:29:53 pm
hi @JohnUKresults.  I'm very late to the party with this, but here's another workaround if you're interested (you asked if there is another).  Since PRINT USING returns correct results you want, here's a way to convert/return a PRINT USING output as a variable, by reading a second screen output.  It's no better, but just shows another way, mostly that it’s possible to get PRINT USING output as a variable.

- Dav

Code: QB64: [Select]
  1. finishseconds! = 39818.38
  2. startseconds! = 34200.77
  3.  
  4. PRINT finishseconds! - startseconds!
  5. PRINT USING "#####.##"; finishseconds! - startseconds!
  6. r = PU2(finishseconds! - startseconds!)
  7.  
  8. FUNCTION PU2 (math)
  9.     'returns a print using readout as a variable
  10.     org& = _DEST 'save current screen
  11.     pun& = _NEWIMAGE(600, 100, 256) 'make new one
  12.     _DEST pun& 'switch to it
  13.     _SOURCE pun&
  14.     PRINT USING "#############.##"; math 'do the deed
  15.     FOR x = 1 TO 30 'read chracters off screen
  16.         n$ = n$ + CHR$(SCREEN(1, x, 0))
  17.     NEXT
  18.     n$ = LTRIM$(RTRIM$(n$)) 'remove any spaces
  19.     _DEST org& 'switch back to original screen
  20.     _SOURCE org&
  21.     PU2 = VAL(n$) 'return variable
  22.  

 
Title: Re: Maths accuracy
Post by: JohnUKresults on June 14, 2021, 09:25:05 am
Hi Dav

Thanks for that suggestion.

However, it's the calculation which is important, not the display of the result, so I will have to stick with multiplying the time to make an integer, doing the subtraction, and then putting the decimal point back into the right place for the results.

Like your idea though - might be useful for something else!