Sin(x) ohne float
-
Hi, ich bin gerade auf der suche nach einer sinusfunktion die ohne floats auskommt.
z.B.
// input: [0, 6283] ? // output: [-1000, 1000] long long mysine(long long val) { return ...; }
gibts sowas?
-
nimm die normale sinusfunktion und multiplizier den wert der rauskommt mit 1000 und speicher den in deinem long long ab.. ^^
-
hab keine normale sin funktion
-
foofel schrieb:
hab keine normale sin funktion
Du wirst ja wohl einen Taschenrechner besitzen, mit dem du diese Tabelle anlegen kannst. Oder eine Tabellenkalkulation.
-
foofel schrieb:
Hi, ich bin gerade auf der suche nach einer sinusfunktion die ohne floats auskommt.
z.B.
// input: [0, 6283] ? // output: [-1000, 1000] long long mysine(long long val) { return ...; }
gibts sowas?
Bestimmt. Im Notfall selber bauen.
Welchem Winkel entspricht denn die Zahl 6283? Außerdem würde ich nicht so'ne krumme Zahl wie 1000 zum Skalieren benutzen. Eher 1024 oder so. Wenn Du dann nämlich a*sin(b1) berechnen willst, schreibst Du einfach (a*mysine(b2)+512)>>10, wobei b2 der zu b1 gehörige Winkel in Deiner Einheit ist. (Das funktioniert natürlich nur, wenn vorzeichenbehaftete Ganzzahlen im Zweierkomplement gespeichert werden.)
Wieviel Speicher (für 'nen Lookup-Table) darfst Du denn verbraten? Wenn Du Platz für 6284 Ganzzahlen im Speicher hast, dann ist das die einfachste und beste Lösung.
Wenn Du genug RAM hast, aber kaum ROM, kannst Du so eine Tabelle relativ leicht zur Laufzeit vorberechnen. Das geht zur Not auch ohne Fließkommazahlen.
Ansonnsten müsstest Du den Definitionsberich "verkleinern" (Du brauchst nur den Bereich 0...pi/2 und kannst den Rest spiegeln), in Abschnitte aufteilen und per Abschnitt zB eine lineare Approximation nutzen.
Gruß,
SP
-
#include <vector> #include <cmath> std::vector<long> dreck() { std::vector<long> ig(6283+1); for(long l = 0;l<=6283; l++) { ig[l]=static_cast<long>((sin(0.001f*l)*1000) + 0.5f); } return ig; } long mysine(long val) { static const std::vector<long> harry = dreck(); return harry[val]; }
-
harryDreckig schrieb:
#include <vector> #include <cmath> std::vector<long> dreck() { std::vector<long> ig(6283+1); for(long l = 0;l<=6283; l++) { ig[l]=static_cast<long>((sin(0.001f*l)*1000) + 0.5f); } return ig; } long mysine(long val) { static const std::vector<long> harry = dreck(); return harry[val]; }
Nix sin da! (Ausser du wolltest auch drauf hinweisen es vorher zu berechnen.)
Sebastian Pizer schrieb:
foofel schrieb:
Hi, ich bin gerade auf der suche nach einer sinusfunktion die ohne floats auskommt.
z.B.
// input: [0, 6283] ? // output: [-1000, 1000] long long mysine(long long val) { return ...; }
gibts sowas?
Bestimmt. Im Notfall selber bauen.
Welchem Winkel entspricht denn die Zahl 6283? Außerdem würde ich nicht so'ne krumme Zahl wie 1000 zum Skalieren benutzen. Eher 1024 oder so. Wenn Du dann nämlich a*sin(b1) berechnen willst, schreibst Du einfach (a*mysine(b2)+512)>>10, wobei b2 der zu b1 gehörige Winkel in Deiner Einheit ist. (Das funktioniert natürlich nur, wenn vorzeichenbehaftete Ganzzahlen im Zweierkomplement gespeichert werden.)
Wieviel Speicher (für 'nen Lookup-Table) darfst Du denn verbraten? Wenn Du Platz für 6284 Ganzzahlen im Speicher hast, dann ist das die einfachste und beste Lösung.
Wenn Du genug RAM hast, aber kaum ROM, kannst Du so eine Tabelle relativ leicht zur Laufzeit vorberechnen. Das geht zur Not auch ohne Fließkommazahlen.
Ansonnsten müsstest Du den Definitionsberich "verkleinern" (Du brauchst nur den Bereich 0...pi/2 und kannst den Rest spiegeln), in Abschnitte aufteilen und per Abschnitt zB eine lineare Approximation nutzen.
Gruß,
SP6283 ist 2PI * 1000, also einfach einmal rum. Die Werte vorher zu berechnen wäre vielleicht ne möglichkeit aber ich hab nur 20kb sram + 128kb flash, das würde schon einiges verbraten. Es muss auch garnicht soo schnell sein. *g*
-
foofel schrieb:
6283 ist 2PI * 1000, also einfach einmal rum. Die Werte vorher zu berechnen wäre vielleicht ne möglichkeit aber ich hab nur 20kb sram + 128kb flash, das würde schon einiges verbraten. Es muss auch garnicht soo schnell sein.
Wie gesagt. 6283 für 2pi und 1000 für 1.0 ist eventuell nicht die geschickteste Lösung. Ansonnsten bist Du schon startklar: Kleine Tabelle vorberechnen und linear interpolieren
// sin_table[x] = round(4096*sin(x*pi/32)) // für x=0...16 static const int sin_table[17] = { 0,401,799,1189,1567,1931,2276,2598,2896 3166,3406,3612,3784,3920,4017,4076,4096 }; // Gibt Approximation für round(4096*sin(angle*pi/4096)) // zurueck. ("8192 = 360°") int my_sine(int angle) { // Reduktion von (-oo,+oo) auf [0°,360°) angle &= 8192-1; // Reduktion von [0°,360°) auf [0°,180°) int sign = 1 - 2*((angle >> 12) & 1); angle &= 4096-1; // Reduktion von [0°,180°) auf [0°,90°] if (angle>2048) angle=4096-angle; return sign * interpolate(sin_table,angle/128.0); }
wobei Du für
interpolate(sin_table,angle/128.0)
jetzt noch selbst dein Hirn etwas anstrengen musst. Ich denke, es ist klar, was gemeint ist.Gruß,
SP
-
foofel schrieb:
6283 ist 2PI * 1000, also einfach einmal rum. Die Werte vorher zu berechnen wäre vielleicht ne möglichkeit aber ich hab nur 20kb sram + 128kb flash, das würde schon einiges verbraten. Es muss auch garnicht soo schnell sein. *g*
dann würde ich erstmal falten.
sin(x)=
if(x>3142) return -sin(x-3142);
else if(x>3142/2) return sin(3142-x);
else return kleinsin(x);kleinsin(x) kannste dann mit der tabelle machen, und die muß nur von 0 bis 1571 gehen.
oder noch besser, jetzt machst du für kleinsin(x) einfach eine näherungsformel, tum beispiel tailor-reihe.kleinsin(x)=x-x*x/2000*x/3000; //bis zu 7% abweichung
oder
kleinsin(x)=x-x*x/2000*x/3000+x*x/2000*x/3000*x/4000*x/5000; //bis zu 0.4% abweichung
-
1. warum nehmt ihr alle long (long) oder so, wenn ihr eh nur nen wertebereich braucht, den so gar short schon abdeckt?! Oo
2. sollten 20kb (ich nehm mal an byte - bit wäre ne komische größe ^^) ja wohl reichen 6283/2 shorts zu speichern... sollten ziemlich genau 6kB sein...
3. warum c++ forum, wenn du offensichtlich keine standard-lib zur verfügung hast? oder hast du nur den math header nicht oder was?
also so in etwa:
struct _sinus_detail { private: short *cache; const static short max_index = 6283; short get_index(unsigned short val) { short size = max_index+1; //gesamtlänge short limit = size<<1; //die hälfte if(val > limit) val = size-val; return val; } public: _sinus_detail() { cache = new short[max_index]; //entweder die werte hier fest reinschreiben oder nen algorithmus suchen - siehe dazu unten } short sin(unsigned short val) { short index = get_index(val); if(index == val) return cache[index]; else return -cache[index]; } ~_sinus_detail() { delete cache; } }; static _sinus_detail math; int main() { short a = math.sin(1234); short b = math.sin(4321); }
kann sein, du musst das alginment noch deaktivieren, weil er sonst pro short trotzdem 4B verbrät - musst du selbst ma gucken...
Algorithmen für Sinus-Berechnungen:
http://de.wikipedia.org/wiki/Sinus_und_Kosinus#Definition_als_Taylorreihebb
edit:
1. da es dir nicht auf die geschwindigkeit ankommt, wie mir gerade aufgefallen ist (lesen ist nich so meins^^), brauchst du eigtl gar nichts vorberechnen
2. kann man das ganze natürlich noch mal zusammenklappen