Ich bin (leider) auf einen Controller mit nur wenig Flash-Speicher
festgelegt, muss aber immer wieder einmal auf diesem auch rechnen (nein,
das kann nicht auf den PC ausgelagert werden, weil die Arbeiten mit
einem Controller in aller Regel "stand-alone Geräte" sind).
Im jetzigen Falle benötige ich eine pow-Funktion.
Das, was ich habe, ist im Anhang.
Sobald Basis und / oder Exponent mit 2 Nachkommastellen verwendet werden
kalkuliert meine Funktion mit relativ großen Fehlern !!! (leider).
Ausgabe meines Testprogramms:
1
Ergebnis von my_pow:
2
2.900 ^ 6.240= 776.225
3
4
Ergebnis von pow aus math.h:
5
2.900 ^ 6.240= 768.004
6
7
Fehler in %: 1.0704
Hat von euch jemand etwas besseres ?
(nein, die Verwendung von double-Variablen bringt nichts und in einigen
Zahlenfällen ist der Fehler sogar noch größer).
Ist meine Näherung in der Reihenentwicklung falsch?
Float benötigt immer sehr viel Hauptspeicher.
Ich würde auf Fixedpoint umsteigen.
Wie genau muss den deine Berechnung sein?
float: 768.004028
fixpoint (Q16): 768.015625
(0.0015% Fehler in diesem Fall)
... ich weiß dass float immer viel Speicher benoetigt. Festkomma wäre
das naechste, was ich angegangen wäre.
Prinzipiell erstelle ich mir Softwaremodule mit Fix- und mit
Gleitkommaroutinen, so auch gemacht für Sinus und Wurzel.
Bei der momentanen Aufgabenstellung benötige ich eben pow und füge die
meiner eigenen abgespeckten my_math hinzu.
Ich werde deine Funktionen gleich einmal ausprobieren und sag schon mal
vielen Dank.
So, jetzt hab ich deine Routinen ausprobiert und vielen Dank dafür.
Leider muss ich sagen, dass die Genauigkeit deiner Funktion nur bis
32768 ( 2^15) gegeben ist, an deinem Programmbeispiel zu sehen.
Das wird erreicht, in dem du 16 Bit für den Vorkommateil und 16 Bit für
den Nachkommateil verwendest.
Will man 24 Bit Vorkomma- und 8 Bit Nachkomma verwenden, ist die
Abweichung deutlich mehr als 1 % !!! (leider).
Aaaaaaber, smile, ich werde mir die Routine behalten als Vorlage für
Festkommateil oder wenn die Anwendung keine größeren Ergebnisse als
32768 erreichen kann...
Für heute habe ich genug...
Danke nochmal
Ralph S. schrieb:> So, jetzt hab ich deine Routinen ausprobiert
Zu viel der Ehre. Die Autoren stehen im Quelltext, ich habe die
Funktionen nur ergoogelt.
Ralph S. schrieb:> Will man 24 Bit Vorkomma- und 8 Bit Nachkomma verwenden, ist die> Abweichung deutlich mehr als 1 % !!! (leider).
So ist das bei Fixpoint.
Bei 32-bit float übrigens auch. Nur ist die Ungenauigkeit eben nicht
fix, sondern gleitet mit. Das kann auch unangenehm enden...
Die Frage ist halt, braucht man es wirklich genauer.
Wenn man ein Raumschiff 2x um den Mars kreisen lassen will, um Schwung
für den Trip zum Jupiter zu bekommen vermutlich ja. Ansonsten: ich habe
in der Praxis noch keinen Fall gehabt, bei dem 32-bittige Präzision
nicht ausgereicht hat.
na ja, es geht halt darum, zu wissen, wo die Grenzen von bestimmten
Algorithmen liegen.
Wie gesagt, ich werde mir die Funktion aufbewahren.
Im "normalen" Gebrauch ist 16 Bit "vorne" und 16 Bit nach dem Komma oft
ausreichend, auch Abweichungen bis 1% sind noch hinnehmbar.
Mich erstaunt hierbei jedoch, wie "genau" die libm auch mit 32 Bit ist.
Ralph S. schrieb:> es geht halt darum, zu wissen, wo die Grenzen von bestimmten> Algorithmen liegen.
Wenn es um ein KONKRETES PROBLEM geht, kommt man eventuell auch durch
Nachdenken, Formelsammlung oder Google weiter. Pow() benötigt man ja
sehr selten, und womöglich nur für einen bestimmten Bereich der
Argumente. Da sich pow() auf exp() und ln() zurückführen lässt, kann man
deren Konvergenzbereich betrachten und eventuell durch Umformung
verbessern, siehe etwa
http://math.stackexchange.com/questions/1124242/how-to-calculate-exp-x-using-taylor-series
als ein willkürliches Beispiel. Aber ein grösserer uC erspart einem den
Aufwand.
Das konkrete Problem war, dass ich einen 3-stufigen Verstärker habe, bei
dem ich an jeder Stufe die Verstärkung digital einstellen kann.
Die Gesamtverstärkung beträgt max. 100000 (was reicht um 100µV auf 10V
zu verstärken).
Das "Problem" hierbei war, dass über ein Tastenfeld die Verstärkung in
db eingebbar sein soll und der Verstärker eben genau diese Verstärkung
einstellt.
Hierbei kommt dann für die Verstärkungsberechnung:
zum Einsatz.
exp und ln hab ich noch nicht in meiner eigenen "mini_math"
implementiert, aber log10.
Ich habe mir beholfen, dass ich über log10 das Ergebnis angenähert habe
und komme auf einen Fehler auf kleiner 1%.
Aber vielen Dank an Stefan und Joe, smile, mir wäre eine größere CPU
auch lieber weil ich dann schlicht math.h verwenden könnte (andererseits
ist da "lustig", die alten Mathegrundsachen der Schule wieder
rauszukramen).
Schönen Start in die Woche,
Ralph
Und die Verstärker haben im für die Verstärkung relevanten Signalpfad
überall 0.1% Widerstände drin und sind auf 25Grad temperiert?
Wenn nicht, dann ist deine Berechnung sicher genauer als die gesteuerte
Hardware.
Ralph S. schrieb:> Prinzipiell erstelle ich mir Softwaremodule mit Fix- und mit> Gleitkommaroutinen, so auch gemacht für Sinus und Wurzel.
Meiner Erfahrung nach ist es wirklich schwer eine eigene math.h zu
schreiben, die weniger Flash-Speicher benötigt als die standard math.h
und gleichzeitig die selbe Genauigkeit aufweist. Deine Funktionen für
Sinus und Wurzel würde mich da mal interessieren. Welchen Algorithmus
hast du z.B. für die Wurzel verwendet? Das Heron-Verfahren?
Ach herjeh...
ich habe nicht gesagt, dass ich "genauer" als die math.h bin oder sein
möchte.
Mein Respekt gilt all denen, die die math-library entwickelt haben.
Mein Ansatz ist, auf leistungsschwachen Controllern eben dann rechnen zu
können, wenn ich rechnen muss. Von daher möchte ich natürlich auch
wissen, in welchem Zahlenbereich Funktionen welche Genauigkeiten haben.
Und ja: ich erfinde sicherlich das Rad nicht neu, Wurzel wird nach dem
Heron - Verfahren gezogen!
Carl D. schrieb:> Und die Verstärker haben im für die Verstärkung relevanten Signalpfad> überall 0.1% Widerstände drin und sind auf 25Grad temperiert?>> Wenn nicht, dann ist deine Berechnung sicher genauer als die gesteuerte> Hardware.
Die Verstärker sind kommerzielle Module. Sie sind es deshalb, weil ich
schlicht diese Verstärkung (zudem mit einem Rausch-Signalabstand von 95
db) nicht selbst bauen kann (bzw. ich weiß welche Schwierigkeiten es
dort gibt und von daher sie nicht selbst bauen will).
Sie werden in einer Klimakammer zwecks kalorimetrischer Messungen
betrieben und nein, ich habe keine Ahnung, wie genau dort die Temperatur
ist, das ist dann allerdings auch nicht mein Problem, weil ich den
Versuchsaufbau nicht konzipiert habe, das hat ein Wissenschaftler
gemacht.
Dieser hat mir auch die Spezifikation genannt, was er alles haben mag.
Mein Job ist erledigt, wenn die Verstärker eben einstellbar sind.
Ralph S. schrieb:> Das konkrete Problem war,
Wenn ich mich nicht täusche, willst Du also lediglich
10^(a/20) berechnen mit a als Variable? Das wäre doch
exp((a/20) * ln(10))
ln(10) ist eine Konstante, bleibt also nur die Division von a durch 20
und die Anwendung von exp(). Also brauchst Du nur eine
Taylor-Approximation von exp().
Weil das Gerät in dem der µC eingebaut ist und der das ganze bedienen
soll vorgegeben ist.
Aber es hat sich für das "eigentliche" Problem sowieso schon erledigt,
weil es auch mit dem kleinen µC funktioniert.
Für mich lerne ich aus jeder Aufgabe neues und leite mir Softwaremodule
für den Fall ab, sollte ich so etwas noch einmal benötigen.
In früheren Jahren (zu DOS und MCS-51 Zeiten) hatte ich viele solcher
Routinen und die Datensicherung vernachlässigt, weil ich dachte ich
benötige so etwas nie mehr.
Siehe da, das war eine Fehlannahme. Leider sind meine alten
Datensicherungen nicht mehr alle lesbar und von daher mache ich bestimmt
Dinge noch einmal, die ich in meinem Leben schon einmal gemacht habe.
Gut ist, dass es das Internet gibt, denn viele andere haben ähnliche
Probleme und Grundlagen sollte man (ich schäme mich auch) immer behalten
und aufbewahren.
Ralph S. schrieb:> Ich bin (leider) auf einen Controller mit nur wenig Flash-Speicher> festgelegt, muss aber immer wieder einmal auf diesem auch rechnen
Geht es dir hauptsächlich um einen geringen Flash-Verbrauch oder auch um
die Rechenzeit? Da die dB-Werte per Tastatur eingegeben werden, dürfte
die Rechenzeit wohl nicht die eintscheidende Rolle spielen, oder?
Hast du mal nachgeschaut, um wieviel kürzer deine Routine gegenüber der
pow-Funktion aus libm (inkl. alle Anhängsel wie exp und log) ist? Ist
der Unterschied wirklich so groß?
Ich glaube auch nicht, dass deine Routine schneller ist.
> else> // Integer Exponent
Wie kommst du darauf, dass nach den vorangegegangenen Abfragen hier nur
noch Integer-Exponenten übrigbleiben? Ein Exponent von bspw. 1.5 landet
ebenfalls hier.
> else if (expo > 0.0f && expo < 1.0f)> {> return nth_root(basis, 1/expo);> }
Das zweite Argument von nth_root ist vom Typ int. Wenn 1/expo nicht
ganzzahlig ist, entsteht hier ein Fehler.
> float nth_root(float a, int n)> {> int k;> float x[6] = {1};>> for (k = 0; k < itanz - 1; k++)> x[k + 1]= (1.0 / n) * ((n - 1) * x[k] + a / my_pow2(x[k], n - 1));> return x[itanz-1];> }
Das sieht nach einer argen Mehrfachrekursion aus. Hast du mal
abgeschätzt, wie oft my_pow2 maximal (rekursiv) aufgerufen wird?
> float x[6] = {1};
Warum brauchst du hier ein Array? Da die Funktion rekursiv aufgerufen
wird, ist der Stackverbrauch recht hoch. Und RAM ist auf diesen
mickrigen Controllern meist noch knapper bemessen als der Flash. Würde
nicht eine einzelne Variable genügen, die den jeweils zuletzt
berechneten Zwischenwert speichert?
Ralph S. schrieb:> Das "Problem" hierbei war, dass über ein Tastenfeld die Verstärkung in> db eingebbar sein soll
Ist das ein kompletter Zehnerblock, über den die Werte numerisch
eingegeben werden oder nur Plus-/Minustasten, über die der Wert
inkrementell eingestellt wird? In letzterem Fall würde eine einfache
Multiplikation pro Tastendruck genügen.
Ist die eingegebene dB-Zahl ganzzahlig, oder sind bspw. auch 34,5 dB
möglich? Wenn nur ganzzahlige Werte möglich sind, vereinfacht das die
Sache.
> und der Verstärker eben genau diese Verstärkung einstellt.
Wie genau geschieht die Einstellung auf Verstärkerseite? Vermutlich
nimmt der Verstärker die Werte nicht im Float-, sondern in einem
einfacheren Format entgegen. Damit würde evtl. auch die Berechnung
vereinfacht.
Wie du siehst, lässt sich an dem Algorithmus bezogen auf die konkrete
Anwendung wahrscheinlich noch viel optimieren, zumal deine aktuelle
Implementation fehlerhaft zu sein scheint. Du solltest nur ein paar mehr
Details deiner Anwendung beschreiben.
Einstellbereich wäre also 100dB
Wenn man mit einer Tabelle von nur 200 fest errechneten Werten und
linearer Interpolation zwischen diesen Werten arbeitet, käme man schon
auf eine Genauigkeit von besser als 0,04%.
Ralph S. schrieb:> Ach herjeh...>> ich habe nicht gesagt, dass ich "genauer" als die math.h bin oder sein> möchte.>> Mein Respekt gilt all denen, die die math-library entwickelt haben.>> Mein Ansatz ist, auf leistungsschwachen Controllern eben dann rechnen zu> können, wenn ich rechnen muss. Von daher möchte ich natürlich auch> wissen, in welchem Zahlenbereich Funktionen welche Genauigkeiten haben.>> Und ja: ich erfinde sicherlich das Rad nicht neu, Wurzel wird nach dem> Heron - Verfahren gezogen!
Deine Lösung fände ich dennoch interessant. Mir ist es bisher nicht
gelungen nennenswert weniger Flash zu benötigen als die
math.h...zumindest nicht mit brauchbaren Lösungen. Die waren stets
größer als die Includierung der math.h. Daher fände ich deine Lösung
echt interessant.
Das hier ... habe ich dann wirklich in der Anwendung implementiert (hier
die Testfunktion für einen PC ... genau in der Überlegung von Stefan
Salewski).
@Yalu
Die Eingabe erfolgt über eine numerische Tastatur.
Mein erster Ansatz erfolgte tatsächlich über rekursive Funktionen, die -
getestet - Fehler deutlich kleiner 1% hatten. Zugegeben war mir aufgrund
der Rekursion und eines eventuellen Stackoverflows nicht so wirklich
wohl bei der Sache.
Prinzipiell spielte die Verarbeitungsgeschwindigkeit überhaupt keine
Rolle - weil wie du bemerkt hattest - die Eingabe über eine
gemultiplexte Tastatur erfolgt ist, einzig der Speicherbedarf spielte
eine Rolle.
Das ganze ist ein Steuerungsgerät (nicht von uns) das eben vorgegeben
ist und in dem ein kleiner STM32F030K6T6 (mit insgesamt 32kByte)
werkelt. Diesem Controllergerät wurde nun die - im Source vorhandene -
Firmware um eben eine Verstärkerkette erweitert und das Originalprogramm
wurde dahingehend modifiziert, dass die Verstärker eben einstellbar
sind. Von daher war auch der zur Verfügung stehende Speicherplatz eben
eng.
Hätte ich das ganze komplett neu aufsetzen müssen, hätte ich sicherlich
einfach einen größeren Controller (F103 oder ähnlich) genommen...
Joe F. schrieb:> Einstellbereich wäre also 100dB> Wenn man mit einer Tabelle von nur 200 fest errechneten Werten und> linearer Interpolation zwischen diesen Werten arbeitet, käme man schon> auf eine Genauigkeit von besser als 0,04%.
Da sollten deutlich weniger Stützstellen reichen. 20dB entsprechen ja
einer Multiplikation mit 10. Die kann man vorher rausziehen. Dann
braucht man nur noch Interpolation zwischen 0 und 20dB.
Alternativ wie weiter oben von Stefan Salewski vorgeschlagen rechnen:
10^(x/20) = exp(x * ln(10)/20)
ln(10)/20 ist eine Konstante. Und mit x \in (0..20) hat man auch einen
einbeschränkten Bereich, in dem die Exponentiation konvergieren muß.
Vermutlich kann man nochmal mehr Geschwindigkeit rausholen, indem man
Logarithmus und Exponentiation nicht zur Basis e, sondern zur Basis 2
benutzt.
Axel S. schrieb:> Da sollten deutlich weniger Stützstellen reichen. 20dB entsprechen ja> einer Multiplikation mit 10. Die kann man vorher rausziehen. Dann> braucht man nur noch Interpolation zwischen 0 und 20dB.
Das ist gut!
Umgekehrt kommt man dann mit einer Tabelle von 200 Werten aus, und hat
bei einer Einstellmöglichkeit von 0.1dB so gut wie gar keinen Fehler.
Die 0.01dB Stufen (falls nötig) könnte man dann (mit entsprechend
geringem Fehler) interpolieren.
Das ganze geht locker in Fixpoint, und die Tabelle wäre 800 Bytes groß.
Joe F. schrieb:> Das ganze geht locker in Fixpoint, und die Tabelle wäre 800 Bytes groß.
Die Frage ist, wieviel Bytes die pow-Funktion der vom TE verwendeten
FP-Bibliothek benötigt. Hier gibt es bspw. eine komplette FP-Bibliothek
für Single-Precision in nur 1024 Bytes:
https://www.quinapalus.com/qfplib.html
Lässt man die nicht benötigten Funktionen sqrt, sin, cos und atan2 weg,
bleiben vermutlich deutlich weniger als 800 Bytes übrig, und man hat
auch weniger Stress beim Programmieren.
Wenn man die Wertebereiche, Auflösung und Zahlenformate der Eingangs-
und Ausgangswerte genauer kennen würde, fiele einem sicher auch eine
Lösung weniger als 200 Bytes ein.
Ralph S. schrieb:> Die Verstärker sind kommerzielle Module. Sie sind es deshalb, weil ich> schlicht diese Verstärkung (zudem mit einem Rausch-Signalabstand von 95> db) nicht selbst bauen kann (bzw. ich weiß welche Schwierigkeiten es> dort gibt und von daher sie nicht selbst bauen will).>> Sie werden in einer Klimakammer zwecks kalorimetrischer Messungen> betrieben und nein, ich habe keine Ahnung, wie genau dort die Temperatur> ist, das ist dann allerdings auch nicht mein Problem, weil ich den> Versuchsaufbau nicht konzipiert habe, das hat ein Wissenschaftler> gemacht.>> Dieser hat mir auch die Spezifikation genannt, was er alles haben mag.> Mein Job ist erledigt, wenn die Verstärker eben einstellbar sind.
Sorry, das war aus deinen Ausführungen nicht herauslesbar.
Typischerweise kommt die Forderung nach n Nachkommastellen aus minimalem
Resourcenbedarf von sehr Ahnungslosen. Aber wie andere schon geschrieben
haben, HW, die das ausrechnen kann, kostet genau nichts imVergleich zur
Klimakammer. Und BTW, das kann auch ein 3€-China-Arduino-Nano mit
float-Genauigkeit.
Carl D. schrieb:> Und BTW, das kann auch ein 3€-China-Arduino-Nano mit> float-Genauigkeit.
... weiß ich, allerdings glaube ich dennoch, dass die 32kb eines
ATmega328 auch nicht gereicht hätten, weil im Controller selbst ja noch
genügend andere Firmware enthalten ist.
Prinzipiell mag ich es nicht sonderlich, eine Firmware die nicht von mir
ist zu erweitern weil ich schlicht die "Nebenwirkungen" fürchte. In
diesem Falle ging es einfach nicht anderst.
Wenn, wie schon gesagt, ich gekonnt hätte wie ich wollte, hätte ich
einen STM32F103 genommen. Selbst der kleinste mit 64kb hätte mit der
libm sicherlich gereicht (ohne Klimmzüge zu machen).
Schöner Nebeneffekt jetzt allerdings ist, dass ich, wenn ich denn will,
selbst auf einem F030 (mit 16 kB) in gewissen Grenzen rechnen kann.
Bspw. jetzt im Moment laß ich darauf Lissajousfiguren in Verbindung mit
einem kleinen 128x128 TFT-Display drauf laufen.
Schönes Spielzeug.
Daraus wird dann ein "Ultra-Low-Cost Board" im R3 - Arduino Format
-lach- allerdings weiß ich dort noch nicht so recht, was ich dann damit
anfangen werde, auf einem Steckbrett geht ja jetzt eh schon alles. (aber
das wäre jetzt ein anderer Thread)