Forum: Mikrocontroller und Digitale Elektronik eigene pow - Funktion


von Ralph S. (jjflash)


Angehängte Dateien:

Lesenswert?

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?

von Joe F. (easylife)


Angehängte Dateien:

Lesenswert?

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)

: Bearbeitet durch User
von Ralph S. (jjflash)


Lesenswert?

... 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.

von Joe F. (easylife)


Angehängte Dateien:

Lesenswert?

Anbei noch das "Programm" mit dem ich es getestet habe.

von Ralph S. (jjflash)


Lesenswert?

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

von Joe F. (easylife)


Lesenswert?

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.

von Ralph S. (jjflash)


Lesenswert?

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.

von nicht"Gast" (Gast)


Lesenswert?

Hallo


Rekursion würde ich an deiner Stelle auf uC vermeiden. Das kann schnell 
krachen.


Grüße

von Stefan S. (Gast)


Lesenswert?

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.

von Ralph S. (jjflash)


Lesenswert?

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

von Carl D. (jcw2)


Lesenswert?

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.

von M. K. (sylaina)


Lesenswert?

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?

von Ralph S. (jjflash)


Lesenswert?

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!

von Ralph S. (jjflash)


Lesenswert?

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.

von Christian K. (Gast)


Lesenswert?

Warum geizt Du dann mit dem Controller Flash wenn in der Endanwendung 
das Geld keine Rolle spielt?

Mit freundlichen Grüßen
Christian

von Stefan Salewski (Gast)


Lesenswert?

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().

von Ralph S. (jjflash)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

: Bearbeitet durch Moderator
von Joe F. (easylife)


Lesenswert?

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%.

von M. K. (sylaina)


Lesenswert?

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.

von Ralph S. (jjflash)


Angehängte Dateien:

Lesenswert?

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...

von Axel S. (a-za-z0-9)


Lesenswert?

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.

von Joe F. (easylife)


Lesenswert?

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ß.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Carl D. (jcw2)


Lesenswert?

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.

von Ralph S. (jjflash)


Lesenswert?

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)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.