Skip to content

Commit 5de298c

Browse files
committed
0.1.8 FastTrig
1 parent dcdc4b9 commit 5de298c

File tree

6 files changed

+208
-48
lines changed

6 files changed

+208
-48
lines changed

libraries/FastTrig/FastTrig.h

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// FILE: FastTrig.h
44
// AUTHOR: Rob Tillaart
5-
// VERSION: 0.1.7
5+
// VERSION: 0.1.8
66
// PURPOSE: Arduino library for a faster approximation of sin() and cos()
77
// DATE: 2011-08-18
88
// URL: https://github.com/RobTillaart/FastTrig
@@ -19,8 +19,10 @@
1919
// 0.1.3 2020-09-07 initial release.
2020
// 0.1.4 2020-09-08 rewrite itan() + cleanup + examples
2121
// 0.1.5 2020-09-11 fixed optimize, new table, added iasin() and iacos()
22-
// 0.1.6 2020-12-23 arduino-CI + unit tests
22+
// 0.1.6 2020-12-23 Arduino-CI + unit tests
2323
// 0.1.7 2021-04-23 fix for PlatformIO
24+
// 0.1.8 2021-08-10 made % 180 conditional in itan() => performance gain
25+
// added icot() cotangent.
2426

2527

2628
#include "Arduino.h"
@@ -137,13 +139,14 @@ float itan(float f)
137139
// so no divide by 65535
138140

139141
// FOLDING
140-
bool neg = (f < 0);
141142
bool mir = false;
143+
bool neg = (f < 0);
142144
if (neg) f = -f;
143145

144146
long x = f;
145147
float rem = f - x;
146-
float v = x % 180 + rem; // normalised value 0..179.9999
148+
if (x >= 180) x %= 180;
149+
float v = rem + x; // normalised value 0..179.9999
147150
if (v > 90)
148151
{
149152
v = 180 - v;
@@ -162,7 +165,7 @@ float itan(float f)
162165
if (mir) co = isinTable16[p - 1] + rem * delta;
163166
else co = isinTable16[p] - rem * delta;
164167
}
165-
if (co == 0) return 0;
168+
else if (co == 0) return 0;
166169

167170
float si = isinTable16[d];
168171
if (rem != 0) si += rem * (isinTable16[d + 1] - isinTable16[d]);
@@ -172,6 +175,21 @@ float itan(float f)
172175
return ta;
173176
}
174177

178+
// some problem at 0 but at least we have a icot(x) cotangent.
179+
float icot(float f)
180+
{
181+
float t = itan(f);
182+
if (t == 0) return NAN;
183+
return 1.0 / t;
184+
}
185+
186+
187+
// missing function...
188+
// float cot(float f)
189+
// {
190+
// return 1.0/tan(f);
191+
// }
192+
175193

176194
///////////////////////////////////////////////////////
177195
//

libraries/FastTrig/README.md

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
Arduino library with interpolated lookup for sin() and cos(). Trades speed for accuracy.
1010

11+
1112
## Description
1213

1314
**Warning: The library trades speed for accuracy so use at own risk**
@@ -22,6 +23,7 @@ due to the folding.
2223

2324
The **i** in the names stands for **int** and **interpolated** as the core is using integer math and lookuptable 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).
2425

26+
2527
#### Lookup tables
2628

2729
The lookup tables are optimized (sketch provided) to minimize the error when using the interpolation, this implies that the points in the table might not be optimal when you use only wholde degrees. A sketch that generates lookup tables is in the examples folder. This generator sketch can also generate tables with different resolution e.g. 24, 14, 12 or even 6, 5 or 4 bit lookup tables. So depending on the application these tables can be ideal, but verify they meet your requirements.
@@ -37,18 +39,20 @@ Although the tables can be written to, it is advised not to do so.
3739

3840
*OK, the optimize example does a write to improve the table to minimize errors*
3941

42+
4043
## Performance isin icos itan
4144

4245
time in us - calls 0 - 360 step 1 degree and calls 720 - 1080 (lib version 0.1.5)
46+
(clock speeds in MHz)
4347

44-
| function | UNO 16MHz | ESP32 240 MHz | UNO (720-1080) | ESP (720-1080) |
45-
|:----:|:----:|:----:|:----:|:----:|
46-
| sin | 120.43 | 10.90 | 124.19 | 10.91 |
47-
| isin | 44.24 | 1.09 | 85.00 | 1.11 |
48-
| cos | 120.27 | 10.81 | 123.98 | 10.83 |
49-
| icos | 51.40 | 1.16 | 91.42 | 1.18 |
50-
| tan | 147.59 | 18.07 | 151.39 | 18.07 |
51-
| itan | 126.73 | 1.31 | 129.93 | 1.29 |
48+
| function | UNO 16 | ESP32 240 | UNO (720-1080) | ESP (720-1080) |
49+
|:--------:|:------:|:---------:|:--------------:|:--------------:|
50+
| sin | 120.43 | 10.90 | 124.19 | 10.91 |
51+
| isin | 44.24 | 1.09 | 85.00 | 1.11 |
52+
| cos | 120.27 | 10.81 | 123.98 | 10.83 |
53+
| icos | 51.40 | 1.16 | 91.42 | 1.18 |
54+
| tan | 147.59 | 18.07 | 151.39 | 18.07 |
55+
| itan | 126.73 | 1.31 | 129.93 | 1.29 |
5256

5357
*Note: itan() 0.1.3 was ( 131.23, 3.05 ) so it improved quite a bit on ESP32. *
5458

@@ -72,19 +76,19 @@ errors - based upon example sketch - lib version 0.1.5
7276

7377
ESP32 calls 0.0 - 360.0 step 0.1 degree
7478

75-
| function | max abs error | avg abs error | max rel error | avg rel error |
76-
|:----:|----:|----:|----:|----:|
77-
| isin | 0.00010264 | 0.00002059 | 0.02955145 | 0.00035180 |
78-
| icos | 0.00010264 | 0.00002031 | 0.02955145 | 0.00034868 |
79-
| itan | 0.69696045 | 0.00640957 | 0.00144703 | 0.00010100 |
79+
| function | max abs error | avg abs error | max rel error | avg rel error |
80+
|:--------:|--------------:|--------------:|--------------:|--------------:|
81+
| isin | 0.00010264 | 0.00002059 | 0.02955145 | 0.00035180 |
82+
| icos | 0.00010264 | 0.00002031 | 0.02955145 | 0.00034868 |
83+
| itan | 0.69696045 | 0.00640957 | 0.00144703 | 0.00010100 |
8084

8185
UNO calls 0.0 - 360.0 step 0.1 degree
8286

83-
| function | max abs error | avg abs error | max rel error | avg rel error |
84-
|:----:|----:|----:|----:|----:|
85-
| isin | 0.00010270 | 0.00002059 | 0.02955145 | 0.00035171 |
86-
| icos | 0.00010264 | 0.00002032 | 0.02949960 | 0.00034869 |
87-
| itan | 0.72760009 | 0.00641527 | 0.00144703 | 0.00037889 |
87+
| function | max abs error | avg abs error | max rel error | avg rel error |
88+
|:--------:|--------------:|--------------:|--------------:|--------------:|
89+
| isin | 0.00010270 | 0.00002059 | 0.02955145 | 0.00035171 |
90+
| icos | 0.00010264 | 0.00002032 | 0.02949960 | 0.00034869 |
91+
| itan | 0.72760009 | 0.00641527 | 0.00144703 | 0.00037889 |
8892

8993
*Note: 0.1.3 for AVR was bad: 17.41900634 , 0.02249339 , 0.02953807 for itan() *
9094

@@ -99,14 +103,14 @@ Please, verify the performance to see if it meets your requirements.
99103

100104
time in us - calls -1 ..+1 step 0.001 degree
101105

102-
| function | UNO 16MHz | ESP32 240 MHz |
103-
|:----:|:----:|:----:|
104-
| asin | 149.76 | 16.71 |
105-
| iasin | 107.70 | 2.58 |
106-
| acos | 169.50 | 15.44 |
107-
| iacos | 114.65 | 2.67 |
108-
| atan | 155.75 | 11.68 |
109-
| iatan | NI | NI |
106+
| function | UNO 16 -| ESP32 240 |
107+
|:--------:|:-------:|:---------:|
108+
| asin | 149.76 | 16.71 |
109+
| iasin | 107.70 | 2.58 |
110+
| acos | 169.50 | 15.44 |
111+
| iacos | 114.65 | 2.67 |
112+
| atan | 155.75 | 11.68 |
113+
| iatan | NI | NI |
110114

111115
- the interpolated reverse lookup is around 30% faster on UNO an 80+% on ESP32
112116
- iatan is Not Implemented.
@@ -120,11 +124,11 @@ Please, verify the accuracy to see if it meets your requirements.
120124

121125
ESP32 calls -1 ..+1 step 0.001 degree
122126

123-
| function | max abs error | avg abs error | max rel error | avg rel error |
124-
|:----:|----:|----:|----:|----:|
125-
| iasin | 0.22498322 | 0.00195790 | 0.00456106 | 0.00005727 |
126-
| iacos | 0.22498587 | 0.00195794 | 0.64284271 | 0.00021902 |
127-
| iatan | NI | NI | NI | NI |
127+
| function | max abs error | avg abs error | max rel error | avg rel error |
128+
|:--------:|--------------:|--------------:|--------------:|--------------:|
129+
| iasin | 0.22498322 | 0.00195790 | 0.00456106 | 0.00005727 |
130+
| iacos | 0.22498587 | 0.00195794 | 0.64284271 | 0.00021902 |
131+
| iatan | NI | NI | NI | NI |
128132

129133
- largest error at 0.999981 - second largest error 0.052841 at -0.999000
130134
- iatan is Not Implemented
@@ -133,26 +137,27 @@ ESP32 calls -1 ..+1 step 0.001 degree
133137

134138
UNO calls -1 ..+1 step 0.001 degree
135139

136-
| function | max abs error | avg abs error | max rel error | avg rel error |
137-
|:----:|----:|----:|----:|----:|
138-
| iasin | 0.22499084 | 0.00195719 | 0.00456125 | 0.00005725 |
139-
| iacos | 0.22498588 | 0.00195740 | 0.64284276 | 0.00021901 |
140-
| iatan | NI | NI | NI | NI |
140+
| function | max abs error | avg abs error | max rel error | avg rel error |
141+
|:--------:|--------------:|--------------:|--------------:|--------------:|
142+
| iasin | 0.22499084 | 0.00195719 | 0.00456125 | 0.00005725 |
143+
| iacos | 0.22498588 | 0.00195740 | 0.64284276 | 0.00021901 |
144+
| iatan | NI | NI | NI | NI |
141145

142146
- largest error at 0.999981 - second largest error 0.052841 at -0.999000
143147
- max rel error is high as it occured near zero.
144148
- iatan is Not Implemented
145149

146150

147-
148151
Please, verify the accuracy to see if it meets your requirements.
149152

153+
150154
## 0.1.4
151155

152156
The library (0.1.4) provides an **itan()** which improved accuracy
153157
upon the (0.1.3) version and performance for the ESP32.
154158
Performance on AVR (UNO) is still an issue, accuracy is OK.
155159

160+
156161
## 0.1.5
157162

158163
In (0.1.4) an error was found in the optimize algorithm, so for 0.1.5
@@ -168,11 +173,25 @@ is added, as it uses the same **isintable16\[\]** interpolation table.
168173
There is no **atan()** or **atan2()** replacement.
169174

170175

176+
## 0.1.6
177+
178+
- Arduino-CI + unit tests
179+
180+
## 0.1.7
181+
182+
- fix for PlatformIO
183+
184+
## 0.1.8
185+
186+
- Made the % 180 in the **itan()** conditional.
187+
- added **icot(f)**
188+
189+
190+
171191
## TODO
172192

173193
- How to improve the accuracy of the whole degrees, as now the table is optimized for interpolation.
174-
- investigate **itan()** lookup table with interpolation (see notes.txt). Interpolation is not trivial for angles between say 60-90 degrees
175-
- investigate if **atan() / atan(()** can be made.
194+
176195

177196
## Operation
178197

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
//
2+
// FILE: fastTrig_test2.ino
3+
// AUTHOR: Rob Tillaart
4+
// PURPOSE: testing the itan functions
5+
// DATE: 2021-08-10
6+
// (c) : MIT
7+
//
8+
9+
10+
#include "FastTrig.h"
11+
12+
uint32_t start, d1, d2;
13+
14+
volatile float x;
15+
int i;
16+
17+
void setup()
18+
{
19+
Serial.begin(115200);
20+
Serial.println("start");
21+
22+
test_tan(0);
23+
test_itan_error_1(false);
24+
25+
Serial.println("done...\n");
26+
}
27+
28+
29+
30+
void test_tan(int n)
31+
{
32+
Serial.println(__FUNCTION__);
33+
Serial.print("TAN\titan\t360 calls - offset: " );
34+
Serial.println(n);
35+
delay(10);
36+
37+
start = micros();
38+
for ( i = n; i < n + 360; i++)
39+
{
40+
x = tan(i);
41+
}
42+
float t1 = (micros() - start) / 360.0;
43+
Serial.println(t1);
44+
delay(10);
45+
46+
start = micros();
47+
for ( i = n; i < n + 360; i++)
48+
{
49+
x = itan(i);
50+
}
51+
float t2 = (micros() - start) / 360.0;
52+
Serial.println(t2);
53+
Serial.print(t2 / t1, 3);
54+
Serial.println("%");
55+
Serial.println();
56+
Serial.println();
57+
delay(10);
58+
}
59+
60+
void test_itan_error_1(bool show)
61+
{
62+
Serial.println(__FUNCTION__);
63+
Serial.println("ITAN 0-3600 calls: \t");
64+
delay(10);
65+
66+
float mx = 0;
67+
float mxr = 0;
68+
float z = 0;
69+
float zz = 0;
70+
for (int i = 0; i < 3600; i++)
71+
{
72+
if (((i + 900 ) % 1800) == 0) continue;
73+
float a = tan(i * 0.1 * PI / 180);
74+
float b = itan(i * 0.1);
75+
float y = abs(a - b); // abs error - rel error ~ 1%
76+
z += y;
77+
if (a > 0) zz += y / a; // not 100% correct but almost.
78+
if (mx < y) mx = y;
79+
if (show)
80+
{
81+
Serial.print(i);
82+
Serial.print("\t");
83+
Serial.print(a, 6);
84+
Serial.print("\t");
85+
Serial.print(b, 6);
86+
Serial.print("\t");
87+
Serial.print(a - b, 6);
88+
Serial.print("\t");
89+
Serial.println((a - b) / a, 6);
90+
}
91+
if (abs(a) > 0.00001 && mxr < y / a)
92+
{
93+
// Serial.print(i);
94+
// Serial.print('\t');
95+
// Serial.print(a, 6);
96+
// Serial.print('\t');
97+
// Serial.print(b, 6);
98+
// Serial.print('\t');
99+
// Serial.print(y/a, 6);
100+
// Serial.println();
101+
mxr = y / a;
102+
}
103+
}
104+
Serial.print("max abs error: ");
105+
Serial.println(mx, 8);
106+
Serial.print("avg abs error: ");
107+
Serial.println(z / 3600, 8);
108+
Serial.print("max rel error: ");
109+
Serial.println(mxr, 8);
110+
Serial.print("avg rel error: ");
111+
Serial.println(zz / 3600, 8);
112+
Serial.println();
113+
Serial.println();
114+
delay(10);
115+
}
116+
117+
118+
void loop()
119+
{
120+
}
121+
122+
// -- END OF FILE --

libraries/FastTrig/keywords.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
isin KEYWORD2
77
icos KEYWORD2
88
itan KEYWORD2
9+
icot KEYWORD2
910
iasin KEYWORD2
1011
iacos KEYWORD2
1112

libraries/FastTrig/library.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "FastTrig",
3-
"keywords": "sin, cos, tan, isin, icos, itan, fast",
4-
"description": "Arduino library with interpolated lookup for sin() and cos(). Trades speed for accuracy. Check readme for details.",
3+
"keywords": "sin, cos, tan, isin, icos, itan, icot, fast",
4+
"description": "Arduino library with interpolated lookup for sin() cos() tan(). Trades speed for accuracy. Check readme for details.",
55
"authors":
66
[
77
{
@@ -15,7 +15,7 @@
1515
"type": "git",
1616
"url": "https://github.com/RobTillaart/FastTrig"
1717
},
18-
"version":"0.1.7",
18+
"version": "0.1.8",
1919
"license": "MIT",
2020
"frameworks": "*",
2121
"platforms": "*"

0 commit comments

Comments
 (0)