Faster Trigonometry britlion Posting Freak Posts: 805 Threads: 135 Joined: Apr 2009 Reputation: 5 02-20-2010, 03:39 AM (This post was last modified: 12-28-2020, 08:54 AM by boriel. Edit Reason: Fix code to follow the new syntax ) Sometimes we're willing to lose accuracy for speed. ZX BASIC uses the spectrum's ROM routines for many of its math functions, and as a result of them being general purpose routines that use 40 bit numbers, well, they can be a bit slow sometimes. See my article on square roots, for a good example. Here is a routine to produce SIN(angle) - where angle is in degrees. It uses a lookup table to calculate sin values, and it's a very quick and dirty method, in that it only actually knows 32 angles, and those to only 8 bit precision. It does linear interpolation between these known angles, however, so that does improve things somewhat. I was actually surprised how precise it was - it's good for at least 2 decimal places, probably 3 as a rule of thumb. The average error is 0.002 That's probably good enough for games that need to calculate angles. It's about 4-5 times faster than the SIN(FLOAT) function, and not even written in native assembler. If you need better accuracy, it would be fairly easy to change the method to use a bigger table - perhaps 2 bytes per entry, even. Remember to work out COS and TAN can also use this function - COS is SIN(90+x) and TAN is SIN(x)/COS(x). It should be easy to write COSINE and TANGENT functions to do the adjustments and call the SINE function. (And this one I didn't copy. It's all mine! Bugs and all. And now it's free for anyone to use.) Code:```FUNCTION SINE(num as FIXED) as FIXED DIM quad as byte DIM est1,dif as uByte while num>360   num=num-360 end while IF num>180 then     quad=-1     num=num-180 ELSE     quad=1 END IF IF num>90 then num=180-num: end if num=((num*31)/90) dif=num : rem Cast to byte loses decimal num=num-dif : rem so this is just the decimal bit est1=PEEK (@sinetable+dif) dif=PEEK (@sinetable+dif+1)-est1 : REM this is just the difference to the next up number. num=est1+(num*dif): REM base +interpolate to the next value. return (num/255)*quad sinetable: asm DEFB 000,013,026,038,051,064,076,088 DEFB 100,112,123,134,145,156,166,175 DEFB 184,193,201,209,216,223,229,234 DEFB 239,243,247,250,252,254,255,255 end asm END FUNCTION``` boriel Administrator Posts: 1,690 Threads: 54 Joined: Aug 2019 Reputation: 10 02-21-2010, 01:48 AM britlion Wrote:Sometimes we're willing to lose accuracy for speed. ZX BASIC uses the spectrum's ROM routines for many of its math functions, and as a result of them being general purpose routines that use 40 bit numbers, well, they can be a bit slow sometimes. See my article on square roots, for a good example. Here is a routine to produce SIN(angle) - where angle is in degrees. It uses a lookup table to calculate sin values, and it's a very quick and dirty method, in that it only actually knows 32 angles, and those to only 8 bit precision. It does linear interpolation between these known angles, however, so that does improve things somewhat. I was actually surprised how precise it was - it's good for at least 2 decimal places, probably 3 as a rule of thumb. The average error is 0.002 That's probably good enough for games that need to calculate angles. It's about 4-5 times faster than the SIN(FLOAT) function, and not even written in native assembler. If you need better accuracy, it would be fairly easy to change the method to use a bigger table - perhaps 2 bytes per entry, even. Remember to work out COS and TAN can also use this function - COS is SIN(90+x) and TAN is SIN(x)/COS(x). It should be easy to write COSINE and TANGENT functions to do the adjustments and call the SINE function. (And this one I didn't copy. It's all mine! Bugs and all. And now it's free for anyone to use.) Great :!: :!: :!: :o I think this should go in the Library/ as Fsin, fcos, ftan, for example??? What do you think? On the other hand, I think you must be included as a co-author of the ZX Basic framework. If you are reluctant, please tell me. But your contributions are really importants and you must appear as a coauthor, etc... both in documentation, Wiki, and so on. britlion Posting Freak Posts: 805 Threads: 135 Joined: Apr 2009 Reputation: 5 02-21-2010, 07:26 PM boriel Wrote:Great :!: :!: :!: :o I think this should go in the Library/ as Fsin, fcos, ftan, for example??? What do you think? On the other hand, I think you must be included as a co-author of the ZX Basic framework. If you are reluctant, please tell me. But your contributions are really importants and you must appear as a coauthor, etc... both in documentation, Wiki, and so on. I think it would work there. The thing about this sort of function though is you can write a very fast very big and very accurate one - based on a lookup table, or a really tiny but very slow one (use the ROM) - or anything in between. Right now I've thought of a way I might be able to speed it up AND make it more accurate, though. As for the credit - you have done all the hard work. I've just broken it repeatedly :twisted: But it would be nice for some thanks on there, yes. I really do appreciate the offer. Don't forget to mention LCD though :wink: Now I've got my computer running again, I should be able to look at the new version. I had to reinstall the operating system! britlion Posting Freak Posts: 805 Threads: 135 Joined: Apr 2009 Reputation: 5 02-28-2010, 12:38 AM (This post was last modified: 12-28-2020, 08:52 AM by boriel.) Here's a full set of functions, with a slightly higher accuracy (at a slightly larger cost - this one is tight to within 2 degrees, and interpolates anything in  between. As a result it needs 46 bytes for the table instead of the 33 used further up.) I figured that to use things like TAN - which divides one by the other - it might not hurt to tighten it a little; since that multiplies the error up! It's now accurate to about 0.25% on average over a full circle. Slightly less for TAN values for the reasons listed above. Code:```FUNCTION Fsin(num as FIXED) as FIXED DIM quad as byte DIM est1,dif as uByte while num>360   num=num-360 end while while num<0 num=num+360 end while IF num>180 then   quad=-1   num=num-180 ELSE   quad=1 END IF IF num>90 then num=180-num: end if num=num/2 dif=num : rem Cast to byte loses decimal num=num-dif : rem so this is just the decimal bit est1=PEEK (@sinetable+dif) dif=PEEK (@sinetable+dif+1)-est1 : REM this is just the difference to the next up number. num=est1+(num*dif): REM base +interpolate to the next value. return (num/255)*quad sinetable: asm DEFB 000,009,018,027,035,044,053,062 DEFB 070,079,087,096,104,112,120,127 DEFB 135,143,150,157,164,171,177,183 DEFB 190,195,201,206,211,216,221,225 DEFB 229,233,236,240,243,245,247,249 DEFB 251,253,254,254,255,255 end asm END FUNCTION``` Code:```FUNCTION Fcos(num as FIXED) as FIXED return Fsin(90-num) END FUNCTION``` Code:```FUNCTION Ftan(num as FIXED) as FIXED return Fsin(num)/Fsin(90-num) END FUNCTION``` « Next Oldest | Next Newest »