sin(x)

Started by stuh505, Thu 04/11/2004 02:35:54

Previous topic - Next topic

stuh505

Edit by strazer:

AGS 2.7 Beta 8 introduced the Maths.Sin function.




here is my attempt, which is not working.  the problem is somewhere in the Taylor approximation -- it seems to think I am dividing by zero.

************

function sin(int x)
{
  //add 5 decimel places (to hold decimels)
  x = 100000*x;
 
  //find equivalent value of x between -Pi to Pi (Taylor approx of 3 terms is nearly perfect here)
  while ((x-2*314159) >= -314159){
    x = x-2*314159;
  }

  //computer Taylor approximation
  //  ie. x = x - x^3/3! + x^5/5! - x^7/7!
  x = x-x*x*x/(6*100000*100000*100000)+x*x*x*x*x/(120*100000*100000*100000*100000*100000)-x*x*x*x*x*x*x/(5040*100000*100000*100000*100000*100000*100000*100000);
 
  //return integer
  return (x/100000);
}

Gilbert

#1
Didn't check the codes yet, but as integers are 4 bytes in length in AGS, the arithmetic used can also be within this accuracy. So those *100000*100000*100000... etc. can easily overflow the calculations and produce unexpected results.

One solution is probably to use less accuracy, like using 4 digit numbers instead of 6.

I may read more and see if it can be improved.

Edit:

Okay I had checked a bit, see if this works (tested, quite accurate):

Code: ags

function square(int x){
Ã,  return (x*x)/1000;
}

function sin(int x)
{
Ã,  int a,b,c,t;
Ã,  //find equivalent value of x between -Pi to Pi (Taylor approx of 3 terms is nearly perfect here)
Ã,  while ((x-2*3142) >= -3142){
Ã,  Ã,  x = x-2*3142;
Ã,  }

Ã,  //computer Taylor approximation
Ã,  //Ã,  ie. t = x - x^3/3! + x^5/5! - x^7/7! 
Ã,  a = (square(x)*x)/1000; //a=x^3Ã,  
Ã,  b = (a*square(x))/1000; //b=x^5
Ã,  c = (b*square(x))/1000; //c=x^7Ã,  Ã,  
Ã,  t = ((x - (a/6)) + (b/120)) - (c/5040);
Ã,  //return integer
Ã,  return (t);
}


For example if you want to calculate sin 2.5 rad, just call:
sin(2500)
which would return 588, so the answer ~ 588/1000 = 0.588


Some notes:
1. If you only expect an integer in radian before enlargement to include the decimals the functions will only produce 7 outcomes (with parameter = -3, -2, -1, 0, 1, 2, 3).
2. You shouldn't return t/1000 but just x, as |sin(x)|<=1, t/1000 is always 0 (or 1 in extreme cases).
3. Because of overflow problems, I changed the numbers to 4 digits instead of 6, I also break down the steps.

stuh505

well first of all...I'm only using 5 digit numbers

but your comment is probably very pertinent, because I accidentally neglected to mention that it did NOT crash when I removed all of the 10000's from the Taylor series.

...this of course did not produce the correct value for sin, though...


stuh505

ok...multiplying x by 1000 (so essentially using 3 decimal places) the game does not crash

the final values for sin are either 0, 1, -1, 2, -2, 3, -3.

I should only be getting values of 0 (and technically 1 or -1 although practically never) because I will always be getting a value with magnitude <=1 and I think that AGS does not round up

Gilbert

#4
See my edited post, it's tested to work.
Quote from: stuh505 on Thu 04/11/2004 03:08:33
well first of all...I'm only using 5 digit numbers,
if you count also the whole number part it's 6, but yeah, your's 5 decimal digits, mine is 3 decimal digits.
Quote
but your comment is probably very pertinent, because I accidentally neglected to mention that it did NOT crash when I removed all of the 10000's from the Taylor series.
As the engine provides no cause of checking of this, overflowing in calculation will produce strange results (I'd tested, like negative numbers, etc.), so we should be careful ourself while scripting, else the errors can be very difficult to fix.

Gilbert

#5
Note I just checked again, the result becomes less accurate when x is close to pi (like 3000,3152, etc.). You may need to add more terms to get more accurate results (and also due to the low accuracy of using only 2 decimals, I'm investing on ways to enhance it).

Kweepa

Two things:

Use brackets when putting multiple terms in one calculation because AGS evaluates right to left! This is a historical thing that CJ refuses to change in the face of reason :=
I see Gilbot did this, but forgot to explain it.

Also, the expansion will be more accurate between -pi/4 and pi/4 - everything else can be refactored to use this range.

For example,

function taylor_sin(int x)
{
   // taylor series approximation of sin x
   // t = x - x^3/3! + x^5/5! - x^7/7!
   // as Gilbot
   return t;
}

function taylor_cos(int x)
{
   // taylor series approximation of cos x
   // t = 1 - x^2/2! + x^4/4! - x^6/6!
   // left as an exercise to the reader
   return t;
}

function sin(int x)
{
   // two steps to try to reduce the drift
   while (x > 31416) x = x - 31416;
   while (x > 3142) x = x - 2*3142;

   if (x < -3*3142/4) return -taylor_sin(x+3142);
   if (x < -3142/4) return -taylor_cos(x+3142/2);
   if (x < 3142/4) return taylor_sin(x);
   if (x < 3*3142/4) return taylor_cos(x-3142/2);
   return taylor_sin(x-3142);
}

Or you could use my maths plugin. This is the tech forum so I think I can safely mention that.
Still waiting for Purity of the Surf II

Gilbert

#7
Yes, I had something similar, but I narrowed the range to -pi/2 through pi/2. New code, and it uses 4 decimal places now (so, pass 25000 for sin 2.5):

Code: ags

function sin(int x) {Ã,  
Ã,  int term=1; // for holding the next term
Ã,  int frac=1; // for holding fractorial value
Ã,  int ind=1; // index for fractorial
Ã,  int sign=-1; // alternating sign
Ã,  x=x%62832; //narrow x to -2pi through 2pi
Ã,  if (x>31416) x=x-62832; //narrow x to -pi through pi
Ã,  Ã,  else if (x<-31416) x=x+62832;
Ã,  if (x>15708) x=31416-x; //narrow x to -pi/2 through pi/2
Ã,  Ã,  else if (x<-15708) x=31416+x;
Ã,  int sqx= (x*x)/10000; //x^2, used a lot
Ã,  int t=x;
Ã,  int a=x;Ã,  
Ã,  while (term){
Ã,  Ã,  a=(a*sqx)/10000; //a=x^ind
Ã,  Ã,  ind++;
Ã,  Ã,  frac=frac*ind;
Ã,  Ã,  ind++;
Ã,  Ã,  frac=frac*ind; //frac=ind!
Ã,  Ã,  term=a/frac;Ã,  Ã,  //term=(x^ind)/(ind!)
Ã,  Ã,  t=t+(term*sign);
Ã,  Ã,  if (sign==1) sign=-1;
Ã,  Ã,  else sign=1;
Ã,  Ã,  }
Ã,  return (t);
}


It seems to be quite accurate now, just that when it's close to pi/2 (x=15708) the result is 10083 (as opposed to correct value 10000), I think this just a small accuracy problem that is not easy to avoid.

BTW, for cosine, you can just do:
Code: ags

function cos(x){
Ã,  return sin(15708-x);
}


;)

Kweepa

Quote from: Gilbot V7000a on Thu 04/11/2004 05:16:31
It seems to be quite accurate now, just that when it's close to pi/2 (x=15708) the result is 10083 (as opposed to correct value 10000), I think this just a small accuracy problem that is not easy to avoid.

That's why I use taylor_cos around pi/2 and 3*pi/2.

Quote
BTW, for cosine, you can just do:
Code: ags

function cos(x){
Ã,  return sin(15708-x);
}


Ah, but then it's not accurate for sin in the range -pi/4 to pi/4.
Still waiting for Purity of the Surf II

Gilbert

Yeah I understand that, because if the magnitude of x is smaller it causes less troubles (especially in those exponential calculations).
It all depends on how accurate we need for the function, if more accuracy is required, yours is better, otherwise mine can be use as it doesn't need two functions.

stuh505

Honestly, I don't understand why we can't just have floating points and math built into this game.  It is certainly the limiting factor.  Thanks for showing another way.  I'll have to keep struggling with it tommorrow because getting my function that uses the trig functions to work is just about as difficult as writing the trig functions and I didn't have time to get it working last night.  Arrgh.

Does your math plugin provide support for floating points????  I just gets so confusing when I have so many terms that need to be multiplie and divided inside and outside of these functions..

Kweepa

Yup!
It's not perfect though because you have to use functions to convert from int to float, multiply, divide, convert from float to int, etc.
It's available from the download AGS/user written plugins page.
Let me know if the documentation isn't clear enough.
Cheers,
Steve
Still waiting for Purity of the Surf II

stuh505

looked over the documentation, looks simple enough to use

i have a few suggestions

* add function for e
* rename log to do log base 10
* add ln to do log base e
* add function for log(a,b) for log base b
* add functions to do sin/cos for degree inputs, or make rad/deg mode a parameter
* functions to round, round down, or round up, without making it into an int

my biggest suggestion is in the naming of your functions.  as some of your examples show, these math expressions can get extremely long and confusing in a hurry especially due to AGS' improper order of operations handling.  if you copy the naming scheme used by the Ti calculators, and in general try to make them less than 5 characters, these math expressions could be made much less confusing to look at

aka "rad(x)" instead of "angle_from_degrees(x)"
"deg(x) instead of "degrees_from_angle(x)"
"float(x)" instead of "int_to_float(x)"
"int(x)" instead of "float_to_int(x)"
"int_up(x)"
"int_down(x)"

but....I will definitely be using this plugin a lot (I presume)

Kweepa

Some of the naming convention is to match Fuzzpilz' earlier math plugin.

I agree that it's a bit cumbersome, but on the other hand it's absolutely clear what the functions do (I hope).
I guess I could add short forms for the longer function names.
int(x) is out as a cast, since int is a keyword in AGS already. But perhaps toint and tofloat are better.

AGS' order of evaluation is irrelevant for floating point operations because all operations need to be explicitly bracketed as function calls.

For 'e' you can just write exp(1). A bit ugly I admit. But I didn't want to make 'e' a function - it seemed a bit too likely to cause problems.
You're right about log and ln. I can't remember what I was thinking.
I didn't want to add log_base(a, b) because it's not clear what the order of parameters is. You can of course write fdiv(log(a), log(b)); but that's ugly too.

I really don't want to complicate things by having a whole new set of functions taking degrees and returning degrees. Perhaps I could make it a global setting, like a calculator.

I can definitely put in round, round_down and round_up functions.

So, in conclusion, I agree with pretty much everything you said. When I have a compiler going on this machine again I'll add the suggested functionality.

Cheers,
Steve
Still waiting for Purity of the Surf II

Gilbert

Well, depends on the context where the log functions are to be used, in many situations writting just log is known to be of base e (and write log_10 for base 10). I agree that in everyday life we mostly write log for base 10 and ln for base e though (so I agree this renaming is good).

fovmester

Quote from: stuh505 on Thu 04/11/2004 13:27:06
Honestly, I don't understand why we can't just have floating points and math built into this game. It is certainly the limiting factor.

I think Chris has put a float data-type in AGS 2.7. Having floats would really make all of this much easier.

strazer

That's why I have edited the first post. I will move these threads back to the technical forum once v2.7 is final.

Orieac

Where can the sin function be usefull on an adventure game?

DoorKnobHandle

There are many ways how a sin function is useful in an adventure game.
Just think about those awesome new custom made transition/fade effects, that'd be possible using this sin function in combination with the RawDraw functions...

Gilbert

Quote from: [ ... ] on Mon 11/04/2005 20:37:45
There are many ways how a sin function is useful in an adventure game.

Like this? :=

SMF spam blocked by CleanTalk