0.3.0 FastTrig

This commit is contained in:
rob tillaart 2022-12-17 15:04:42 +01:00
parent 8a2f0e7b16
commit 725673b5d9
10 changed files with 671 additions and 25 deletions

View File

@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.3.0] - 2012-12-11
- add isin256(), icos256() and isincos256() => integer math only version
- add isincos(float f, float &si, float &co) => calculate sin and cos simultaneously.
- fix rounding bug when mirroring 90° for remainder.
- add examples.
- update documentation.
- update library.properties.
----
## [0.2.1] - 2022-12-05
- add atanFast(x) => faster and less accurate than atan().
Input range (-1..1) is fastest.
@ -15,7 +25,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- update readme.md
- update keywords.txt
## [0.2.0] - 2022-12-02
- merged PR #16 for ESP_IDF support
- added <stdint.h>

View File

@ -1,7 +1,7 @@
//
// FILE: FastTrig.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.2.1
// VERSION: 0.3.0
// PURPOSE: Arduino library for a faster approximation of sin() and cos()
// DATE: 2011-08-18
// URL: https://github.com/RobTillaart/FastTrig
@ -60,6 +60,78 @@ uint8_t sinTable8[] = {
};
///////////////////////////////////////////////////////
//
// GONIO INT EXPERIMENTAL
// works with only whole degrees.
//
int isin256(uint32_t v)
{
bool negative = false;
long whole = v;
if (whole >= 360) whole %= 360;
int y = whole; // 16 bit math is faster than 32 bit
if (y >= 180)
{
y -= 180;
negative = true;
}
if (y >= 90)
{
y = 180 - y;
}
int g = sinTable16[y] >> 8;
if (negative) return -g;
return g;
}
int icos256(uint32_t v)
{
return isin256(v + 90);
}
void isincos256(uint32_t v, int &si, int &co)
{
bool sneg = false;
bool cneg = false;
long whole = v;
if (whole >= 360)
{
whole %= 360;
}
int y = whole; // 16 bit math is faster than 32 bit
if (y >= 180)
{
y -= 180;
sneg = !sneg;
cneg = !cneg;
}
if (y >= 90)
{
y = 180 - y;
cneg = !cneg;
}
si = sinTable16[y] >> 8;
co = sinTable16[90-y] >> 8;
if (sneg) si = -si;
if (cneg) co = -co;
}
///////////////////////////////////////////////////////
//
// GONIO LOOKUP
@ -76,7 +148,13 @@ float isin(float f)
long whole = f;
uint8_t remain = (f - whole) * 256;
if (whole >= 360) whole %= 360;
if (whole >= 360)
{
whole %= 360;
// possible faster for 360-720
// if (whole >= 720) whole %= 360;
// else whole -= 360;
}
int y = whole; // 16 bit math is faster than 32 bit
@ -91,7 +169,7 @@ float isin(float f)
y = 180 - y;
if (remain != 0)
{
remain = 255 - remain;
remain = 256 - remain;
y--;
}
}
@ -117,6 +195,87 @@ float icos(float x)
}
void isincos(float f, float &si, float &co)
{
bool sneg = (f < 0);
bool cneg = false;
if (sneg)
{
f = -f;
}
long whole = f;
uint8_t remain = (f - whole) * 256;
if (whole >= 360)
{
whole %= 360;
// possible faster for 360-720
// if (whole >= 720) whole %= 360;
// else whole -= 360;
}
int y = whole; // 16 bit math is faster than 32 bit
if (y >= 180)
{
y -= 180;
sneg = !sneg;
cneg = !cneg;
}
if (y >= 90)
{
y = 180 - y;
if (remain != 0)
{
remain = - remain;
y--;
}
cneg = !cneg;
}
// float value improves ~4% on avg error for ~60 bytes.
// SIN
uint16_t value = sinTable16[y];
// interpolate if needed
if (remain > 0)
{
value = value + ((sinTable16[y + 1] - value) / 8 * remain) / 32; // == * remain / 256
}
si = value * 0.0000152590219; // = / 65535.0
if (sneg) si = -si;
// COS
value = sinTable16[90-y];
if (remain > 0)
{
value = sinTable16[89-y];
remain = 256 - remain;
value = value + ((sinTable16[90-y] - value) / 8 * remain) / 32; // == * remain / 256
}
co = value * 0.0000152590219; // = / 65535.0
if (cneg) co = -co;
}
///////////////////////////////////////////////
//
// TAN
//
// tan() should be done with isincos()
// as icos() is less accurate => tan() less accurate.
/*
float itan(float f)
{
float x, y;
isincos(f,x,y);
if (y != 0) return x/y;
return NAN;
}
*/
float itan(float f)
{
// reference
@ -267,5 +426,5 @@ float atan2Fast(float y, float x)
}
// -- END OF FILE --

View File

@ -2,7 +2,7 @@
//
// FILE: FastTrig.h
// AUTHOR: Rob Tillaart
// VERSION: 0.2.1
// VERSION: 0.3.0
// PURPOSE: Arduino library for a faster approximation of sin() and cos()
// DATE: 2011-08-18
// URL: https://github.com/RobTillaart/FastTrig
@ -18,7 +18,7 @@
#endif
#define FAST_TRIG_LIB_VERSION (F("0.2.1"))
#define FAST_TRIG_LIB_VERSION (F("0.3.0"))
#ifdef __cplusplus
extern "C"
@ -29,6 +29,17 @@ extern uint16_t sinTable16[];
extern uint8_t sinTable8[];
///////////////////////////////////////////////////////
//
// GONIO INT EXPERIMENTAL
//
int isin256(uint32_t v);
int icos256(uint32_t v);
// calculate both in one call.
void isincos256(uint32_t v, int &si, int &co);
///////////////////////////////////////////////////////
//
// GONIO LOOKUP
@ -37,6 +48,10 @@ float isin(float f);
float icos(float x);
// calculate both in one call.
void isincos(float v, float &si, float &co);
float itan(float f);
// 0 returns NAN but we have a icot(x) cotangent.

View File

@ -15,6 +15,11 @@ Arduino library with interpolated lookup for sin() and cos(). Trades speed for a
**Warning: The library trades speed for accuracy so use at own risk**
So please, verify the performance and accuracy to see if they meet
the requirements of your project.
----
The library provides one lookup table that is used for
**isin(degrees)** and **icos(degrees)** and **itan(degrees)**.
This lookup table is optimized for interpolation so the values for whole degrees are not optimal.
@ -28,11 +33,15 @@ Similar to ```cos(x) == sin(x + PI)``` it is also true that ```icos(x) == isin(x
so **icos()** can use the very same lookup table at the cost of a single addition.
In fact it uses ```icos(x) == isin(x - 270)``` as that performs better, due to the folding.
The **i** in the names stands for **int** and **interpolated** as the core is using integer math and lookup table of 91 uint16_t = 182 bytes.
The **i** in the names stands for **int** and **interpolated** as the core is
using integer math and lookup table of 91 uint16_t = 182 bytes.
By folding and mirroring the whole 360 degrees and beyond can be handled.
When **isin(x)** is called and ```x == int(x)``` then the library will not interpolate and this will improve performance.
When x is not a whole number the library will linear interpolate between **isin(int(x))** and **isin(int(x+1))**.
Of course this introduces an error but it is quite fast (which was the goal).
When **isin(float x)** is called and ```x == int(x)``` then the library will not interpolate.
This will improve performance even more.
When x is not a whole number the library will linear interpolate
between **isin(int(x))** and **isin(int(x+1))**.
Of course this introduces an error but the error is small and performance is still
quite fast (which was the goal).
#### Lookup tables
@ -70,6 +79,30 @@ Use **fastTrig_atan_performance.ino** to check the gain on your board.
Price is that the values are less accurate, but the difference is < 0.001.
#### isin256, icos256, isincos256
Version 0.3.0 added these experimental functions:
- **int isin256(uint32_t v)** accepts only positive angles in degrees.
Returns the sin(v)\*256 to keep all math integer (shift 8 later to correct value)
- **int icos256(uint32_t v)** accepts only positive angles in degrees.
Returns the cos(v)\*256 to keep all math integer (shift 8 later to correct value)
- **int isincos256(uint32_t v, int &si, int &co)** accepts only positive angles in degrees.
returns both the sin(v)\*256 and the cos(v)\*256 of the same angle.
Faster than both individual calls together.
#### isincos
Version 0.3.0 added this experimental function:
- **float isincos(float f, float &si, float &co)** accepts any angle in degrees.
returns both the sin(v) and the cos(v) of the same angle.
Faster than both individual calls, see example.
There is a minor difference between the value of the **float co** compared to **icos()**.
This need some investigation ( truncating ?)
## Performance isin icos itan
time in us - calls 0 - 360 step 1 degree and calls 720 - 1080 (lib version 0.1.5)
@ -98,7 +131,8 @@ and needs to be normalized ( expensive modulo on AVR ). It is worth noting that
original **sin()** **cos()** and **tan()** only have a small overhead for
values outside the 0..360 range.
Please, verify the performance to see if it meets your requirements.
Please, verify the performance and accuracy to see if they meet
the requirements of your project.
## Accuracy isin icos itan
@ -166,7 +200,6 @@ ESP32 calls -1 ..+1 step 0.001 degree
- **iatan()** is **Not** Implemented
UNO calls -1 ..+1 step 0.001 degree
| function | max abs error | avg abs error | max rel error | avg rel error |
@ -185,7 +218,7 @@ Please, verify the accuracy to see if it meets your requirements.
## Performance atanFast, atan2Fast
Times in microseconds (first measurement)
Indicative times in microseconds (first measurements)
| function | atan | atanF | atan2 | atan2F | factor | notes |
|:----------:|:------:|:-------:|:-------:|:--------:|:--------:|:--------|
@ -193,18 +226,80 @@ Times in microseconds (first measurement)
| UNO | 220 | 124 | 212 | 128 | ~1.6 |
| ESP32 | 50 | 15 | 44 | 13 | ~3.3 |
The range
The range of the second UNO is beyond the -1..1 range
Additional measurements are welcome.
(use performance sketch)
To be elaborated.
## Accuracy atanFast, atan2Fast
The atan2Fast() uses atanFast() so the accuracy for both is the same.
The test sketch indicates a maximum error is smaller than 0.001.
To be elaborated
To be elaborated.
## Performance isincos()
**isincos()** calculates sin(f) and cos(f) in one call.
1000 calls in microseconds.
| function | UNO 16 | ESP32 240 |
|:------------|:--------:|:-----------:|
| sin | 122872 | 10926 |
| isin | 70704 | 1086 |
| cos | 122636 | 10853 |
| icos | 66588 | 1151 |
| isin + icos | 148368 | 2248 |
| isincos | 103788 | 1909 |
Note the isincos() is faster than the original
sin() or cos() while being pretty accurate.
## Accuracy isincos()
As the basic algorithm is very similar to isin() the accuracy is the same.
## Performance isin256 icos256 isincos256
**isin256()**, **icos256()** and **isincos256()** calculates the sin\*256 etc.
These functions all return an integer value.
There is no floating point math in there so it performs a bit better.
At some moment you must correct this factor of 256 with a division or a shift 8.
1000 calls in microseconds. Based upon **fastTrig_isincos256.ino**
Note to test and compare, the values were multiplied by 100 and shifted by 8.
| function | UNO 16 | ESP32 240 |
|:------------------|:--------:|:-----------:|
| sin | 131260 | 11364 |
| isin | 79044 | 1119 |
| isin256 | 28284 | 255 |
| cos | 131008 | 11298 |
| icos | 74928 | 1190 |
| icos256 | 31704 | 289 |
| isin256 + icos256 | 58352 | 478 |
| isincos256 | 32300 | 339 |
Note the **Ixxx256()** series functions are Fast.
The price is accuracy but might still be OK for many projects.
## Accuracy isin256 icos256 isincos256
The **Ixxx256()** only accept whole degrees.
Therefore the values come directly from the lookup tables. no interpolation.
The error is less than 2% (first measurements.
**To be quantified**
## versions
@ -219,5 +314,23 @@ See examples
## Future
- How to improve the accuracy of the whole degrees, as now the table is optimized for interpolation.
- sinc(x) = sin(x)/x function.?
#### Must
- improve documentation
- verify math (tables etc) again.
- write test sketches that output the tables for documentation :)
#### Should
- write more tests to verify values.
- test performance on more platforms.
- investigate the difference between **isincos()** and **icos()**.
- investigate **itan256()**
- itan256(0) = 0 itan256(1) = 4 itan256(2) = 9 so there will be big steps...
- max abs error should be 0.5 or less, it might have its uses.
#### Could
- How to improve the accuracy of the whole degrees,
- now the table is optimized for interpolation.
- add **sinc(x)** = **sin(x)/x** function.?
- **ixxx256()** functions need another lookup table?
- separate .h file?

View File

@ -0,0 +1,136 @@
// FILE: isincos.ino
// AUTHOR: Rob Tillaart
// DATE: 2022-12-09
// PURPOSE: R&D
#include "Arduino.h"
#include "FastTrig.h"
uint32_t start, stop;
volatile float x;
volatile int y;
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println(__FILE__);
delay(10);
start = micros();
for (uint32_t r = 0; r < 1000; r++)
{
x = sin(r);
}
stop = micros();
Serial.print("SIN: \t\t");
Serial.println(stop - start);
delay(10);
start = micros();
for (uint32_t r = 0; r < 1000; r++)
{
x = isin(r);
}
stop = micros();
Serial.print("ISIN: \t\t");
Serial.println(stop - start);
delay(10);
start = micros();
for (uint32_t r = 0; r < 1000; r++)
{
x = cos(r);
}
stop = micros();
Serial.print("COS: \t\t");
Serial.println(stop - start);
delay(10);
start = micros();
for (uint32_t r = 0; r < 1000; r++)
{
x = icos(r);
}
stop = micros();
Serial.print("ICOS: \t\t");
Serial.println(stop - start);
delay(10);
for (uint32_t r = 0; r <= 360; r++)
{
x = cos(r * PI / 180.0);
y = icos(r);
if (abs(x - y) >= 1)
{
Serial.print(r);
Serial.print('\t');
Serial.print(x);
Serial.print('\t');
Serial.print(float(y));
Serial.println();
}
}
Serial.println();
delay(100);
volatile float sum = 0;
start = micros();
for (uint32_t r = 0; r < 1000; r++)
{
sum += isin(r) + icos(r);
}
stop = micros();
Serial.print("ISIN + ICOS: \t");
Serial.println(stop - start);
Serial.println(sum);
delay(10);
sum = 0;
start = micros();
for (uint32_t r = 0; r < 1000; r++)
{
float p, q;
isincos(r, p, q);
sum += p + q;
}
stop = micros();
Serial.print("ISINCOS: \t");
Serial.println(stop - start);
Serial.println(sum);
delay(10);
for (uint32_t r = 0; r < 100; r++)
{
float p, q, s, t;
isincos(r*0.1, p, q);
s = isin(r*0.1);
t = icos(r*0.1);
if ((abs(p-s) > 0.0001) || (abs(q-t) > 0.0001))
{
Serial.print(r);
Serial.print("\t");
Serial.print(s, 6);
Serial.print("\t");
Serial.print(p, 6);
Serial.print("\t");
Serial.print(t, 6);
Serial.print("\t");
Serial.print(q, 6);
Serial.println();
}
}
delay(10);
Serial.println("\ndone...");
}
void loop()
{
}
// -- END OF FILE --

View File

@ -0,0 +1,51 @@
start
test_hw_support
172
40
0.21
test_sin_cos_tan
SIN COS TAN 360 calls - offset: 0
120.42 120.29 147.66
test_isin_icos_itan
ISIN ICOS ITAN 360 calls - offset: 0
44.21 51.33 112.71
test_sin_cos_tan
SIN COS TAN 360 calls - offset: 720
124.19 123.98 151.39
test_isin_icos_itan
ISIN ICOS ITAN 360 calls - offset: 720
84.93 91.36 134.70
test_isin_error_1
ISIN 0-3600 calls:
max abs error: 0.00010270
avg abs error: 0.00002059
max rel error: 0.02955145
avg rel error: 0.00035171
test_icos_error_1
ICOS 0-3600 calls:
max abs error: 0.00010264
avg abs error: 0.00002032
max rel error: 0.02949960
avg rel error: 0.00034869
test_itan_error_1
ITAN 0-3600 calls:
max abs error: 0.72760009
avg abs error: 0.00641527
max rel error: 0.00144703
avg rel error: 0.00037889
done...

View File

@ -0,0 +1,157 @@
// FILE: fastTrig_isincos256.ino
// AUTHOR: Rob Tillaart
// DATE: 2022-12-09
// PURPOSE: R&D
#include "Arduino.h"
#include "FastTrig.h"
uint32_t start, stop;
volatile float x;
volatile int y;
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println(__FILE__);
delay(10);
start = micros();
for (uint32_t r = 0; r < 1000; r++)
{
x = (100 * sin(r));
}
stop = micros();
Serial.print("SIN: \t\t");
Serial.println(stop - start);
delay(10);
start = micros();
for (uint32_t r = 0; r < 1000; r++)
{
x = (100 * isin(r));
}
stop = micros();
Serial.print("ISIN: \t\t");
Serial.println(stop - start);
delay(10);
start = micros();
for (uint32_t r = 0; r < 1000; r++)
{
y = (100 * isin256(r)) >> 8;
}
stop = micros();
Serial.print("ISIN256: \t");
Serial.println(stop - start);
Serial.println();
delay(10);
start = micros();
for (uint32_t r = 0; r < 1000; r++)
{
x = (100 * cos(r));
}
stop = micros();
Serial.print("COS: \t\t");
Serial.println(stop - start);
delay(10);
start = micros();
for (uint32_t r = 0; r < 1000; r++)
{
x = (100 * icos(r));
}
stop = micros();
Serial.print("ICOS: \t\t");
Serial.println(stop - start);
delay(10);
start = micros();
for (uint32_t r = 0; r < 1000; r++)
{
y = (100 * icos256(r)) >> 8;
}
stop = micros();
Serial.print("ICOS256: \t");
Serial.println(stop - start);
Serial.println();
delay(10);
for (uint32_t r = 0; r <= 360; r++)
{
x = (100 * cos(r * PI / 180.0));
y = (100 * icos256(r)) >> 8;
if (abs(x - y) >= 1)
{
Serial.print(r);
Serial.print('\t');
Serial.print(x);
Serial.print('\t');
Serial.print(float(y));
Serial.println();
}
}
Serial.println();
delay(100);
volatile int sum = 0;
start = micros();
for (uint32_t r = 0; r < 1000; r++)
{
sum += isin256(r) + icos256(r);
}
stop = micros();
Serial.print("S C 256: \t");
Serial.println(stop - start);
delay(10);
start = micros();
for (uint32_t r = 0; r < 1000; r++)
{
int p, q;
isincos256(r, p, q);
sum += p + q;
}
stop = micros();
Serial.print("ISINCOS256: \t");
Serial.println(stop - start);
delay(10);
for (uint32_t r = 0; r < 1000; r++)
{
int p, q, s, t;
isincos256(r, p, q);
s = isin256(r);
t = icos256(r);
if (p != s || q != t)
{
Serial.print(r);
Serial.print("\t");
Serial.print(s);
Serial.print("\t");
Serial.print(p);
Serial.print("\t");
Serial.print(t);
Serial.print("\t");
Serial.print(q);
Serial.println();
}
}
delay(10);
Serial.println("\ndone...");
}
void loop()
{
}
// -- END OF FILE --

View File

@ -5,6 +5,8 @@
# Methods and Functions (KEYWORD2)
isin KEYWORD2
icos KEYWORD2
isincos KEYWORD2
itan KEYWORD2
icot KEYWORD2
@ -14,6 +16,10 @@ iacos KEYWORD2
atanFast KEYWORD2
atan2Fast KEYWORD2
isin256 KEYWORD2
icos256 KEYWORD2
isincos256 KEYWORD2
# Instances (KEYWORD2)

View File

@ -1,7 +1,7 @@
{
"name": "FastTrig",
"keywords": "sin, cos, tan, isin, icos, itan, icot, fast",
"description": "Arduino library with interpolated lookup for sin() cos() tan(). Trades speed for accuracy. Check readme for details.",
"keywords": "sin, cos, tan, isin, icos, itan, icot, fast, isincos",
"description": "Arduino library with interpolated lookup for sin(), cos(), tan(), atan2() and more. Trades speed for accuracy. Check readme.md for details.",
"authors":
[
{
@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/FastTrig"
},
"version": "0.2.1",
"version": "0.3.0",
"license": "MIT",
"frameworks": "*",
"platforms": "*",

View File

@ -1,9 +1,9 @@
name=FastTrig
version=0.2.1
author=Rob Tillaart <rob.tillaart@gmail.com><pete.thompson@yahoo.com>
version=0.3.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library with interpolated lookup for sin() and cos()
paragraph=Trades speed for accuracy. Check readme for details.
sentence=Arduino library with interpolated lookup for sin(), cos(), tan(), atan2() and more.
paragraph=Trades speed for accuracy. Check readme.md for details.
category=Data Processing
url=https://github.com/RobTillaart/FastTrig
architectures=*