Eine Frage die Allgemeinheit:
Wie sinvoll / unsinnig / überflüssig wäre Fixed-Point Unterstützung in
avr-gcc?
Eine solche auf ISO/IEC TR 18037 "Emdedded-C" basierende Unterstützung
war für avr-gcc 4.8 geplant und ist in ähnlicher Form bereits in den
Atmel-Tools basierend auf avr-gcc 4.6 enthalten.
Aspekte sind u.a:
• Effizient im Vergleich zu float oder einer eigenen
Fixed-Arithmetik
• Klarheit der Quelle mit Ausdrücken wie 1.2k * x + y anstatt
x-fach verschachtelter Konversionen / Funktionsaufrufen / Makros
• Aufwand / Testabdeckung im Vergleich zur eigenen Arithmetik.
• Einfaches Lesen aus dem Flash (mit C++ zB nicht möglich, auch nicht
mit pgm_read)
• Skalierbarkeit was den Wertebereich / Position des Dezimalpunkts
angeht.
• "Emdedded C" (das zB auch __flash implementiert) ist nicht in den
C-Standard aufgenommen, auch nicht in C11. Gleichwohl gibt es den
Quasi-Standard "Emdedded C"
• Destabilisierung der Toolchain durch neue Features
Wiki: Festkommaarithmetik, AVR Arithmetik/Saturierung
Johann L. schrieb:> Wie sinvoll / unsinnig / überflüssig wäre Fixed-Point> Unterstützung in avr-gcc?
Fände ich persönlich eine tolle Sache, besonders für nicht so versierte
Nutzer sicher eine Bereicherung, sollte dann natürlich gegenüber echtem
float wenn möglich platzsparender sein dann wäre es meiner Meinung nach
eine Bereicherung.
Mit avr-g++ könnte man sich auch eine Fixed-Point-Klasse basteln, die
sich annähernd wie ein eingebauter Datentyp verhält, und das ganz ohne
irgendwelche Änderungen am Compiler. Wenn man's als Template definiert,
auch mit wählbarer Zahl an Vor- und Nachkommastellen.
Ich hatte sowas mal angefangen, aber mangels persönlicher Notwendigkeit
nicht fertiggemacht.
Rolf Magnus schrieb:> Mit avr-g++ könnte man sich auch eine Fixed-Point-Klasse basteln,> die sich annähernd wie ein eingebauter Datentyp verhält,
Mit der gleichen Effizient wird man das kaum hinbekommen.
> und das ganz ohne irgendwelche Änderungen am Compiler.> Wenn man's als Template definiert, auch mit wählbarer Zahl an> Vor- und Nachkommastellen. Ich hatte sowas mal angefangen,> aber mangels persönlicher Notwendigkeit nicht fertiggemacht.
Ich habe bisher noch keine solche Implementierung gesehen. Es wird
immer behauptet, es sei einfach und naheliegend.
Ich behaupte jetze einfach mal, in C++ ist das nicht naheliegend.
Wie würde zB eine einfache Klasse / Template konkret aussehen, die
folgenden Code mit der gleichen Effizienz wie avr-gcc implementiert?
1
#include<stdfix.h>
2
3
const__flashaccumoffset[]={1.2k,-3.45k};
4
5
sataccumadd_offset(sataccuma,unsignedi)
6
{
7
returna+offset[i];
8
}
Und von mir aus auch mit 1/4 der Performance... avr-gcc brauch dafür 26
bytes Code (ATmega8); Funktionsaufrufe werden nicht benötigt.
Vor allem habe ich keine offensichtliche Lösung gefunden für
- Literale wie 1.23
- Solche Objekte (Lookup-Tabellen!) in den Flash legen
- Arithmetik wie Multiplikation oder saturierte Multiplikation ohne
den nächstbreiteren Typ zu brauchen.
- Auch nur halbwegs so effizient zu sein wie avr-gcc ohne Assembler
zu verwenden. Selbst bei Verwendung von Assembler wird sich kaum
jemand den Wolf machen wollen...
Auch Implementierungen wie libfixmath (C99) machen gegen avr-gcc keinen
Stich.
Johann L. schrieb:> Ich behaupte jetze einfach mal, in C++ ist das nicht naheliegend.>> Wie würde zB eine einfache Klasse / Template konkret aussehen, die> folgenden Code mit der gleichen Effizienz wie avr-gcc implementiert?>
1
>#include<stdfix.h>
2
>
3
>const__flashaccumoffset[]={1.2k,-3.45k};
4
>
5
>sataccumadd_offset(sataccuma,unsignedi)
6
>{
7
>returna+offset[i];
8
>}
>> Und von mir aus auch mit 1/4 der Performance... avr-gcc brauch dafür 26> bytes Code (ATmega8); Funktionsaufrufe werden nicht benötigt.
Zum Vergleich: Hierist der Code vom avr-gcc:
Johann L. schrieb:> Wie sinvoll unsinnig überflüssig wäre Fixed-Point Unterstützung in> avr-gcc?
Ich fände so etwas recht nützlich. Interessant wäre insbesondere die
32-Bit-Fixed-Point-Arithmetik, weil hier bei der Implementierung in
Standard-C der für Zwischenergebnisse benötigte nächstgrößere Integer-
typ 64 Bit breit ist, den man sich auf dem AVR nicht unbedingt antun
möchte.
> Eine solche auf ISO/IEC TR 18037 "Emdedded-C" basierende Unterstützung> war für avr-gcc 4.8 geplant
Im TR sind ja die Anzahl der Nachkommabits bei den fract-Typen und die
Anzahl der Vor- und Nachkommabits bei den accum- und sat-Typen nicht
genau festgelegt. Welche Formate würden denn vom AVR-GCC unterstützt
werden?
> • Einfaches Lesen aus dem Flash
Bedeutet das, dass die "named adress spaces" aus dem TR implementiert
würden, so dass man auch beliebige andere const-Variablen ohne
pgm-*-Aufrufe direkt aus dem Flash lesen kann? Damit würde einer der
Nachteile, die der GCC gegenüber ander AVR-C-Compilern (z.B. IAR)
hat, beseitigt werden.
> #include <stdfix.h>>> const __flash accum offset[] = { 1.2k, -3.45k };>> sat accum add_offset (sat accum a, unsigned i)> {> return a + offset[i];> }>>> avr-gcc brauch dafür 26 bytes Code (ATmega8)
Gerade bei saturierender Arithmetik bringt natürlich eine Implementie-
rung als Spracherweiterung deutliche Effizienzvorteile, da dann das
Overflow- und andere Prozessorstatusbits genutzt werden können, auf
die man von C aus keinen direkten Zugriff hat.
> Zum Vergleich: Hierist der Code vom avr-gcc:> ...
Das sieht schon sehr nach dem Optimum aus, sowohl was die saturierende
Arithmetik als auch die direkte Einbindung der Flash-Zugriffe betrifft.
Das heißt aber ja auch, dass die Implementierung der Erweiterungen
schon sehr fortgeschritten ist. Wäre es da nicht sinnvoll, diese auf
jeden Fall offziell zu machen, auch wenn sich hier vielleicht nicht
jeder zu Begeisterungsstürmen hinreißen lässt?
Bitte nicht mit Gewalt das Rad neu erfinden.
Es gibt bereits freie Bibliotheken mit Festkommaarithmetik.
Ob fertig für die AVR-Serie weiß ich nicht.
Als Sourcecode (C): Auf jeden Fall.
Der Name ist mir allerdings derzeit nicht geläufig.
Johann L. schrieb:> Eine Frage die Allgemeinheit:>> Wie sinvoll unsinnig überflüssig wäre Fixed-Point Unterstützung in> avr-gcc?
Es mag ungenutzte Compilerfeatures geben - unsinnig oder überflüssig
wären sie deswegen noch lange nicht.
Im Moment behelfe ich mir in diesen Fällen mit Assembler, aber in der
Tat wäre Anwendungscode in gewohnter Operatorschreibweise schöner. In
C++ habe ich sowas früher auch gemacht, aber da war es für Bignums bzw.
Brüche aus Bignums. Für kurze Festkommaarithmetik wäre der C++ typische
Overhead zu groß um noch sinnvoll (besser als Fließkomma) zu sein.
Langer Rede kurzer Sinn: ja bitte!
> Eine solche auf ISO/IEC TR 18037 "Emdedded-C" basierende Unterstützung> war für avr-gcc 4.8 geplant und ist in ähnlicher Form bereits in den> Atmel-Tools basierend auf avr-gcc 4.6 enthalten.
Google findet nur Bezahlquellen für den Standard. Gibts nicht irgendwo
einen Draft oder zumindest eine informelle Beschreibung zum freien
Download?
XL
amateur schrieb:> Bitte nicht mit Gewalt das Rad neu erfinden.> Es gibt bereits freie Bibliotheken mit Festkommaarithmetik.
[ ] Du hast die Frage verstanden
Es geht um arithmetische Typen, die kürzeren und schnelleren Code
erlauben als Fließkomma. Da kann eine Bibliothek aus mehreren Gründen
nicht mit eingebauten Typen mithalten. Weder beim Komfort
(Operatorüberladung) noch bei Codegröße/Geschwindigkeit (call frames
etc.)
XL
Nimm den Dreisatz: Google (Fixed-Point)-> Wikipedia Links (am
Seitenende)->
und Du landest bei: z.B. libfixmath
Würde mich auch wundern, wenn es bei sourceforge.net nix gäbe.
Dauerte weniger als eine Minute.
amateur schrieb:> Nimm den Dreisatz: Google (Fixed-Point)-> Wikipedia Links (am> Seitenende)->> und Du landest bei: z.B. libfixmath
Bevor du da weiter drauf herumreitest, lies dir bitte durch, warum
das keine optimale Variante ist:
Beitrag "Re: Fixed-Point Support in avr-gcc?"
Yalu X. schrieb:> Johann L. schrieb:>> Wie sinvoll / unsinnig / überflüssig wäre Fixed-Point Unterstützung in>> avr-gcc?>> Ich fände so etwas recht nützlich. Interessant wäre insbesondere die> 32-Bit-Fixed-Point-Arithmetik, weil hier bei der Implementierung in> Standard-C der für Zwischenergebnisse benötigte nächstgrößere Integer-> typ 64 Bit breit ist, den man sich auf dem AVR nicht unbedingt antun> möchte.
Die Basisarithmetik ist vorhanden; die saturierenden 32-Bit
Multiplikation und Division ist allerdings nicht Assembler-Optimiert und
offen in C codiert (libgcc). Wie bei der 64-Bit Arithmetik auch,
besteht natürlich die Möglichkeit, die Anwendung gegen eigene
Implementierungen zu linken.
>> Eine solche auf ISO/IEC TR 18037 "Emdedded-C" basierende Unterstützung>> war für avr-gcc 4.8 geplant>> Im TR sind ja die Anzahl der Nachkommabits bei den fract-Typen und die> Anzahl der Vor- und Nachkommabits bei den accum- und sat-Typen nicht> genau festgelegt. Welche Formate würden denn vom AVR-GCC unterstützt> werden?http://gcc.gnu.org/wiki/avr-gcc#Fixed-Point_Support>> • Einfaches Lesen aus dem Flash>> Bedeutet das, dass die "named adress spaces" aus dem TR implementiert> würden, so dass man auch beliebige andere const-Variablen ohne> pgm-*-Aufrufe direkt aus dem Flash lesen kann?
Nicht würden. Die sind bereits seit ca. 1 Jahr (2012-03-22)
releast:
http://gcc.gnu.org/gcc-4.7/changes.html>> #include <stdfix.h>>>>> const __flash accum offset[] = { 1.2k, -3.45k };>>>> sat accum add_offset (sat accum a, unsigned i)>> {>> return a + offset[i];>> }>>>>>> avr-gcc brauch dafür 26 bytes Code (ATmega8)>> Gerade bei saturierender Arithmetik bringt natürlich eine Implementie-> rung als Spracherweiterung deutliche Effizienzvorteile, da dann das> Overflow- und andere Prozessorstatusbits genutzt werden können, auf> die man von C aus keinen direkten Zugriff hat.
Selbst wenn man in AVR-Assembler erfahren ist, hat man hier ruckzuck
falschen Code falls man nicht extrem aufpasst.
Es ist zum Beispiel nicht mehr egal, ob ein negativer Wert addiert oder
ein positiver abgezogen wird. Zudem werden evtl. Sonderbehandlungen für
0x80 notwendig. Zumindest wenn man die AVR-Instruktionen optimal nutzen
will.
> Das heißt aber ja auch, dass die Implementierung der Erweiterungen> schon sehr fortgeschritten ist. Wäre es da nicht sinnvoll, diese auf> jeden Fall offziell zu machen, auch wenn sich hier vielleicht nicht> jeder zu Begeisterungsstürmen hinreißen lässt?
Es basiert auf einer Implementierung von Sean D'Épagnier, die in den
avr-atmel-gcc 4.6 eingeflossen ist. Diese verwendet allerdings ein
anderes Layout, z.b. s15.16 für Accum anstatt s16.15 wie es avr-gcc 4.8
tut.
Wäre es nicht sinnvoll, von Anfang an auch Typen mit expliziter Breite
anzubieten, wie in stdint.h?
1
typedefshort_Fractfract8_t;
2
typedef_Fractfract16_t;
3
typedeflong_Fractfract32_t;
4
typedeflonglong_Fractfract64_t;
5
6
typedefshort_Accumaccum16_t;
7
typedef_Accumaccum32_t;
8
typedeflong_Accumaccum64_t;
Ansonsten klingt das sehr praktisch, vor allem die eingebaute
Saturierung. Schade ist halt, dass Code mit diesen Typen völlig
unportabel wird. Aber gut, irgendwo muss man ja anfangen. Vielleicht
findet es mit der Zeit ja Einzug in andere Compiler.
Ich selber würde es erstmal nur in kleinen Projekten einsetzen, in denen
man viel Festkommarechnung benötigt und den Code ziemlich sicher nicht
auf einer anderen Plattform wiederverwenden will.
Fabian O. schrieb:> Wäre es nicht sinnvoll, von Anfang an auch Typen mit expliziter Breite> anzubieten, wie in stdint.h?> typedef long _Accum accum64_t;
Es gibt 8 accum-Typen die 64 Bits breit sind:
signed long accum
unsigned long accum
sat signed long accum
sat unsigned long accum
signed long long accum
unsigned long long accum
sat signed long long accum
sat unsigned long long accum
Ausserdem ist accum zumindest per stdfix.h "standardisiert" und stdint.h
gehört ganz offiziell zu C99.
> Schade ist halt, dass Code mit diesen Typen völlig> unportabel wird. Aber gut, irgendwo muss man ja anfangen. Vielleicht> findet es mit der Zeit ja Einzug in andere Compiler.
"Völlig" unportabel ist es nicht, zumindest innerhalb von GCC ist es
portabel. Und der deckt immerhin einiges an Silizium ab.
Portabilität ist aber nicht die Aufgabe des Compilers, der stellt
zunächst nur Features zur Verfügung, die speziell für Maschinen mit
(sehr) knappen Resoucren sinnvoll bis unentbehrlich sind.
Portabilität könnte man zB mittels Bibliotheken wie libfixmath
erreichen, falls diese das Vorhandenseit / Nichtvorhandensein eines
entsprechenden, nativen Fixedpoit-Supports herausfaktorisiert.
Das Interface wäre immer noch Portabel, nur die Innereien der Lib würden
sich bei Verwendung eines anderen Compilers verbiegen.
Generell zur Portabilität: Wie Portabel sind denn AVR-Anwendungen?
Laufen deine AVR-Anwendungen auf einem PC? Ohne Anpassungen?
Und was ist mit PROGMEM etc. Das ist "nur" eine Optimierung, aber eine,
ohne die man die AVR-Hardware nicht wirklich nutzen kann.
> Ich selber würde es erstmal nur in kleinen Projekten einsetzen, in denen> man viel Festkommarechnung benötigt und den Code ziemlich sicher nicht> auf einer anderen Plattform wiederverwenden will.
Bislang hab ich mir noch nicht den Wolf gemacht und Programme wie zB
4000 Stellen von Pi mit ATtiny2313 nach Fixed portiert. Wäre als
Vergleich / Arithmetik-Benchmark bestimmt interessant.
Johann L. schrieb:> Es gibt 8 accum-Typen die 64 Bits breit sind:>> signed long accum> unsigned long accum> sat signed long accum> sat unsigned long accum> signed long long accum> unsigned long long accum> sat signed long long accum> sat unsigned long long accum
OK. War mir nicht bewusst, dass es eigene Typen für die saturierte
Artithmetik gibt. Für signed und unsigned hätte ich in Anlehnung an
stdint.h accum64_t und uaccum64_t vorgeschlagen. Nach dem gleichen
Schema könnte man die saturierten Typen mit einem zusätzlichen "s"
kennzeichnen:
1
typedefsignedlong_Accumaccum64_t;
2
typedefunsignedlong_Accumuaccum64_t;
3
typedefsatsignedlong_Accumsaccum64_t;
4
typedefsatunsignedlong_Accumsuaccum64_t;
Der "unsymmetrische" long long accum tanzt eh aus der Reihe, dafür
bräuchte man imo keinen alternativen Namen. Oder man nimmt meinetwegen
accum64_48_t o.ä..
> Ausserdem ist accum zumindest per stdfix.h "standardisiert" und stdint.h> gehört ganz offiziell zu C99.
Genau das finde ich inkonsequent: In der offiziellen stdint.h gibt es
endlich Typen mit eindeutiger Länge und in der neuen stdfix.h fängt man
wieder mit dem schwammigen "short, nichts, long, long long"-Kram an ...
> Portabilität könnte man zB mittels Bibliotheken wie libfixmath> erreichen, falls diese das Vorhandenseit / Nichtvorhandensein eines> entsprechenden, nativen Fixedpoit-Supports herausfaktorisiert.>> Das Interface wäre immer noch Portabel, nur die Innereien der Lib würden> sich bei Verwendung eines anderen Compilers verbiegen.
Klar, nur hat man dann in C nicht mehr die natürliche Schreibweise in
Ausdrücken, sondern muss wieder Makros bzw. Funktionsaufrufe nutzen.
Vorteil wäre dann "nur" noch die höhere Geschwindigkeit und eventuell
kompakterer Code.
> Generell zur Portabilität: Wie Portabel sind denn AVR-Anwendungen?> Laufen deine AVR-Anwendungen auf einem PC? Ohne Anpassungen?
Komplette Anwendungen natürlich nicht. Die Module oberhalb der
Hardwareabstraktionsebene kann man allerdings unverändert in
PC-Anwendungen einbauen, ja.
> Und was ist mit PROGMEM etc. Das ist "nur" eine Optimierung, aber eine,> ohne die man die AVR-Hardware nicht wirklich nutzen kann.
PROGMEM lässt sich immerhin portieren, indem man eine pgmspace.h mit
Dummy-Implementierungen für die Makros und Funktionen anlegt. Mit der
Festkomma-Arithmetik geht das leider nicht.
Aber versteh mich nicht falsch, ich habe absolut nichts dagegen, dass
die Fixpoint-Arithmetik in avr-gcc eingebaut wird, ganz im Gegenteil.
Ich würde es nur aus Portabilitätsgründen eben nicht in allen Projekten
einsetzen, solange es nicht auch außerhalb des GCCs unterstützt wird.
Fabian O. schrieb:> OK. War mir nicht bewusst, dass es eigene Typen für die saturierte> Artithmetik gibt. Für signed und unsigned hätte ich in Anlehnung an> stdint.h accum64_t und uaccum64_t vorgeschlagen. Nach dem gleichen> Schema könnte man die saturierten Typen mit einem zusätzlichen "s"> kennzeichnen:
Ja, hätte man. Ist aber nicht so. Die vorgeschlagenen typedefs sind ja
auch nirdendwo standardisiert, was soll das also bringen? Zudem liefern
sie keine neue Funktionalität. Und jeder kann sich selbst Typed
definieren wenn er will. Ausserdem wäre ein 32-Bit Typ in der
Atmel-Toolchain ein anderer Typ und nicht binärkompatibel mit dem
32-Bit-Typ im offiziellen avr-gcc. Da ist ein "accum" noch portabler!
Im momentanen Stand der Implementierung geht es auch erst mal um
Basisfunktionalität, nicht um Schnickschnack drumrum. Und auch nicht
um Standardisierung; die ist nicht aufgabe von GCC, sondern der WGs der
ISO.
Johann L. schrieb:> Wie sinvoll / unsinnig / überflüssig wäre Fixed-Point Unterstützung in> avr-gcc?
Welche Anwendungen dafür hast Du denn?
Ich habe es bisher weder vermißt noch benötigt.
Float brauche ich nur für Ein-/Ausgaben und da ist der Mensch viel
langsamer, die Rechenzeit also völlig wurscht.
Regelungen mache ich in integer, die ADCs und DACs verstehen ja auch nur
integer, wozu also unnötig umwandeln.
Bei Bedarf denke ich mir für die Zwischenrechnungen (32 Bit) 1 Byte als
Nachkommastelle.
Ohne wirklichen zwingenden Grund nutze ich keine nicht portablen
Funktionen oder gar Assembler.
Ich amüsiere mich nur über Leute, die mit großem Aufwand an völlig
unbedeutenden Stellen um einzelne Zyklen oder Bytes kämpfen.
Peter Dannegger schrieb:> Ich amüsiere mich nur über Leute, die mit großem Aufwand an völlig> unbedeutenden Stellen um einzelne Zyklen oder Bytes kämpfen.
Heisst also konkret: avr-gcc ist git wie er ist und bedarf keiner
weiteren Optimierungen oder Featues.
Schön :-)
Wie bei Thunderbird: Wird eingestapmft da perfekt und nicht mehr
verbesserbar :-)
Johann L. schrieb:>> Bedeutet das, dass die "named adress spaces" aus dem TR implementiert>> würden, so dass man auch beliebige andere const-Variablen ohne>> pgm-*-Aufrufe direkt aus dem Flash lesen kann?>> Nicht würden. Die sind bereits seit ca. 1 Jahr (2012-03-22)> releast:>> http://gcc.gnu.org/gcc-4.7/changes.html
Jetzt bin ich von den Socken, das geht ja tatsächlich schon. Vielleicht
sollte ich beim nächsten Update mal die Release-Notes etwas aufmerksamer
durchlesen ;-)
>> Im TR sind ja die Anzahl der Nachkommabits bei den fract-Typen und die>> Anzahl der Vor- und Nachkommabits bei den accum- und sat-Typen nicht>> genau festgelegt. Welche Formate würden denn vom AVR-GCC unterstützt>> werden?>> http://gcc.gnu.org/wiki/avr-gcc#Fixed-Point_Support
Die Halbe-Halbe-Aufteilung bei den Accum-Typen klingt vernünftig. Im TR
ist ja ein Minimum von 4 Vorkommabits vorgesehen, was aber gerade einmal
reicht, um 16 Zahlen aus [0,1) aufzuaddieren. Gut, dass sich der GCC
nicht auf dieses Minimum beschränkt.
Wenn ich am Wochenende die Zeit finde, werde ich mal ein 4.8er-Snapshot
saugen und ein Bisschen mit den Fixed-Point-Operationen herumspielen.
Johann L. schrieb:> Heisst also konkret: avr-gcc ist git wie er ist und bedarf keiner> weiteren Optimierungen oder Featues.
Wer hat das behauptet?
Ich meinte ausschließlich den Anwender des Compilers.
D.h. im Quelltext möchte ich nicht kryptischen, unleserlichen,
fehlerträchtigen, unportablen Inline-Assembler sehen, nur um 2 Zyklen
oder 2 Words einzusparen.
Am Compiler gibt es durchaus noch Potential. Z.B. die Nutzung der
Register R0,1 ändern, damit endlich mal die Würgarounds mit LPM, SPM,
MUL und Interrupts entfallen können.
Peter Dannegger schrieb:> Johann L. schrieb:>> Wie sinvoll / unsinnig / überflüssig wäre Fixed-Point Unterstützung in>> avr-gcc?>> Welche Anwendungen dafür hast Du denn?> Ich habe es bisher weder vermißt noch benötigt.
Das habe ich mir bei meinen Hobby-Programierniveau gar nicht getraut zu
fragen. Würde mich aber auch mal interessieren.
Peter Dannegger schrieb:> D.h. im Quelltext möchte ich nicht kryptischen, unleserlichen,> fehlerträchtigen, unportablen Inline-Assembler sehen, nur um 2 Zyklen> oder 2 Words einzusparen.
Genau daher ist ja die Implementierung von sowas wie fixed point
im Compiler der richtige Ansatz. Hast du dir Johanns Beispiel
da oben mal angesehen? Da ist nichts kryptisch oder unleserlich
dran.
Jörg Wunsch schrieb:> Hast du dir Johanns Beispiel> da oben mal angesehen?
Welchen Beitrag meinst Du?
Wie gesagt, ich hatte bisher nicht die Notwendigkeit, andere als die
Standardtypen zu verwenden.
Das Überlaufproblem ist mir auch aufgefallen. Ich habe dann mit dem
16Bit-ADC-Wert alle Rechnungen 32Bit gemacht und zum Schluß für den DAC
wieder auf 16Bit begrenzt.
Ein anders Problem hatte ich mit der Division, wenn der ADC 0 liefert.
Ich habe dann einfach zum ADC-Wert 1 addiert.
Man muß bei Regelungen auch höllisch aufpassen, wo man signed und wo man
unsigned rechnet.
Jörg Wunsch schrieb:> Da ist nichts kryptisch oder unleserlich> dran.
Das bezog sich nur auf die vielen Threads, wo immer gleich der Inline
Assembler hervorgekramt wird, sobald im Listing ein Befehl zuviel
auftaucht. War also etwas OT.
schön an SAT wäre gerade im Embeded Bereich, daß z.B.
Stellgrößen/interne Reglerwerte nie überlaufen und man deswegen das
Regelverfahren als Quelltext schreibt und nicht die Umgehung der
DatentypUnzulänglichkeiten.
Carl Drexler schrieb:> schön an SAT wäre gerade im Embeded Bereich, daß z.B.> Stellgrößen/interne Reglerwerte nie überlaufen
Dann sollte das aber auch einstellbar sein. Z.B. wenn der DAC nur 10Bit
hat, auf 0..1023.
Das __flash mittlerweile eingebaut ist, ist gerade auch neu für mich :-O
Danke dafür! Ich weiß noch, dass es da mal mehrere Diskussionen drum
gab, aber wusste nicht, dass es mittlerweile Einzug gehalten hat.
pgmspace.h ade?
Kommazahlen (Egal ob float oder fix) braucht man eher selten. Aber wenn,
dann meist in Algorithmen und da wäre fixkomma schon eine praktische und
brauchbare Sache.
Peter Dannegger schrieb:> Bei Bedarf denke ich mir für die Zwischenrechnungen (32 Bit) 1 Byte als> Nachkommastelle.
Und genau sowas könnte man mit einer offiziellen Implementierung eben
vermeiden. Wo man sich dann bei deinem Code erst "reindenken" muss, wäre
bei Verwendung von offiziellen Fixkomma Typen der Fall klar.
Aber ich sehe auch ein, dass es größtenteils Geschmackssache ist. Auch
ich bin bisher mit manuellen Fixkomma Implementierungen gut zurecht
gekommen. Einen nennenswerten Vorteil sehe auch ich hier nur bei
Anfängern oder Nicht-Hardcore Programmierern. Die können die Vorteile
von float benutzen mit der Geschwindigkeit von int :-)
Johann L. schrieb:> Vor allem habe ich keine offensichtliche Lösung gefunden für>> - Literale wie 1.23
Wäre das nicht der klassische Anwendungsfall für die User Defined
Literals in C++11?
Malte S. schrieb:> Johann L. schrieb:>> Vor allem habe ich keine offensichtliche Lösung gefunden für>>>> - Literale wie 1.23>> Wäre das nicht der klassische Anwendungsfall für die User Defined> Literals in C++11?
Ich bin wirklich nicht vertraut mit C++. Mehrere Anläufe, immer
großartig gescheitert :-)
Von daher auch meine Frage auf Rolfs Aussage in
Rolf Magnus schrieb:
wie denn so ein C++ konkret aussieht. Mit großer Wahrscheinlichkeit,
liegt es an meinen bescheidenen C++ Kenntnissen. Ein minimales
Beispiel, das dem 3-Zeiler in C entspricht und nicht im kompletten
Performance-Waterloo endet, würd ja schon reichen um es zu illustrieren.
Das kann doch soooo schwer nicht sein?
Oder doch?
Mein Versuch mit Literals endete zB im kompletten Code-Bloat.
Jedenfalls ist die Klasse damit (oder mit Konstruktoren) nicht-POD, kann
also nicht ins Flash gelegt werden.
Lookup-Tabelle aufbauen? Njet! progmem- und section-Atribut (müssen)
versagen.
Johann L. schrieb:> wie denn so ein C++ konkret aussieht. Mit großer Wahrscheinlichkeit,> liegt es an meinen bescheidenen C++ Kenntnissen. Ein minimales> Beispiel, das dem 3-Zeiler in C entspricht und nicht im kompletten> Performance-Waterloo endet, würd ja schon reichen um es zu illustrieren.
Genau da liegt das Problem.
Im Prinzip ist sowas in C++ nicht schwer zu machen. Aber es performant
zu machen, ist eine ganz andere Sache. Denn im Compiler kannst du auch
'schräge' Datentypen mit zb 24 Bit problemlos benutzen, was auf C++
Ebene so nicht geht.
Auch bei saturierter Arithmetik brauchen wir uns gar nicht weiter
unterhalten. Da hast du auf Hochsprachenebene keine Chance, weil der
Zugriff auf das Overflow-Flag fehlt. Das ist der Dolchstoss, mit dem
einem hier jeder Assembler-Programmierer aussticht.
> Mein Versuch mit Literals endete zB im kompletten Code-Bloat.>> Jedenfalls ist die Klasse damit (oder mit Konstruktoren) nicht-POD, kann> also nicht ins Flash gelegt werden.
Der Punkt ist mir noch nicht klar.
Ob eine Klasse ein POD ist oder nicht, hängt nicht davon ab, ob sie
Konstruktoren hat oder nicht, sondern ausschliesslich davon, ob ihre
Member-Variablen selber POD sind.
Karl Heinz Buchegger schrieb:> Johann L. schrieb:>>> wie denn so ein C++ konkret aussieht. Mit großer Wahrscheinlichkeit,>> liegt es an meinen bescheidenen C++ Kenntnissen. Ein minimales>> Beispiel, das dem 3-Zeiler in C entspricht und nicht im kompletten>> Performance-Waterloo endet, würd ja schon reichen um es zu illustrieren.>> Genau da liegt das Problem.> Im Prinzip ist sowas in C++ nicht schwer zu machen. Aber es performant> zu machen, ist eine ganz andere Sache. Denn im Compiler kannst du auch> 'schräge' Datentypen mit zb 24 Bit problemlos benutzen, was auf C++> Ebene so nicht geht.
Die 3-Byte Typen machen hier keinen Unterschied, die werden bei der
Fixed-Implementierung weder benötigt noch verwendet. Diese Typen sind
lediglich bei den 3-Byte Adressen als tiefhängendes Obst angefallen,
spielen aber ansonsten keine Rolle im Compiler — ausser daß sie
"öffentlich" gemacht wurden.
>> Mein Versuch mit Literals endete zB im kompletten Code-Bloat.>>>> Jedenfalls ist die Klasse damit (oder mit Konstruktoren) nicht-POD, kann>> also nicht ins Flash gelegt werden.>> Der Punkt ist mir noch nicht klar.> Ob eine Klasse ein POD ist oder nicht, hängt nicht davon ab, ob sie> Konstruktoren hat oder nicht, sondern ausschliesslich davon, ob ihre> Member-Variablen selber POD sind.
Der Knackpunkt ist jedenfalls: Sobald Konstruktion notwendig ist, kann
das Ding nicht mehr im Flash (.progmem) liegen und wird nach .rodata
oder sogar .data gelegt.
Alles andere würde bedeuten, daß der Compiler zur Compilezeit den Code
der Konstruktors simulieren müsste, um zu wissen, was in Flash kommt.
Und bei virtuellen Klassen ist es erst zur Laufzeit bekannt, was was
ist.
Beispiel:
Da könnten die neuen constexpr-Konstruktoren helfen (welche auch überaus
nützlich für die Berechnung von Baudraten-Registerwerten sind).
Solch eine C++-Implementierung könnte evtl. bei der Umsetzung von
digitalen Filtern helfen, wo man zuweilen eine feinere Abstimmung
zwischen Vor- und Nachkommazahlen braucht.
Gibt es für die stdfix.h eine brauchbare Einführungsseite? Welchen
Vorteil bieten die __Accum-Typen? Wird bei denen die eigentliche
Operation mit einer größeren Bitanzahl durchgeführt?
oh, sehe den Thread jetzt erst durch die letzte Antwort - keine Ahnung,
wie interessant die Fragen darin noch sind.
Johann L. schrieb:> Wie sinvoll / unsinnig / überflüssig wäre Fixed-Point Unterstützung in> avr-gcc?
Sicher sehr interessant, weil:
- (in C zumindest) viel besser lesbar als selbstgebautes
- hoffentlich effizienter und besser optimierbar als selbstgebautes
Schön wäre natürlich, wenn es eine Lösung gäbe, die halbwegs portabel
bleibt in der Zukunft. Aber das wird natürlich Hoffnung bleiben...
Ein Ansatz dazu soll wohl ISO/IEC TR 18037:2008 gewesen sein. Kenne ich
nicht näher, aber ist wohl sehr eingeschränkt bzgl. der Stellenzahlen.
Generell sehe ich eine Nützlichkeit aber eher für Leute, die sich für
immer C++ verweigern.
Ich habe da weniger Scheu und nehme von C++ auch das, was mir nützlich
erscheint, und bin dann schnell bei templates.
Johann L. schrieb:> Johann L. schrieb:>> Ich behaupte jetze einfach mal, in C++ ist das nicht naheliegend.>>>> Wie würde zB eine einfache Klasse / Template konkret aussehen, die>> folgenden Code mit der gleichen Effizienz wie avr-gcc implementiert?>>> #include <stdfix.h>>>>> const __flash accum offset[] = { 1.2k, -3.45k };>>>> sat accum add_offset (sat accum a, unsigned i)>> {>> return a + offset[i];>> } >>> Und von mir aus auch mit 1/4 der Performance... avr-gcc brauch dafür 26>> bytes Code (ATmega8); Funktionsaufrufe werden nicht benötigt.>> Zum Vergleich: Hierist der Code vom avr-gcc:add_offset:> push r16> push r17> /* prologue: function */> /* frame size = 0 */> /* stack size = 2 */> .L__stack_usage = 2> lsl r20> rol r21> lsl r20> rol r21> subi r20,lo8(-(offset))> sbci r21,hi8(-(offset))> movw r30,r20> lpm r16,Z+> lpm r17,Z+> lpm r18,Z+> lpm r19,Z> add r22,r16> adc r23,r17> adc r24,r18> adc r25,r19> brvc 0f> ldi r25,0x80> cp r19,r25> sbc r24,r24> sbci r25,0> mov r22,r24> mov r23,r24> 0:> /* epilogue start */> pop r17> pop r16> ret
Ich hatte ja vor einiger Zeit schon mal etwas dazu in
Beitrag "Festkommazahlen mit C++" geschrieben, was ich auch
gelegentlich selbst verwende.
Zum Vergleich habe ich den letzten Stand ausgegraben. Leider lief der
nicht mit avr-g++, weil da keine std::limits<> vorhanden sind.
Die habe ich schnell stillgelegt, weil sie hier nichts zur Sache tun,
und habe sie spaßeshalber gegen deine Funktion add_offset() antreten
lassen.
Jetzt ist es natürlich schwer, verschiedene Datentypen miteinander zu
vergleichen.
Dein accum kenne ich jetzt nicht und habe einfach zum Vergleich eine
Festkommazahl aus einer int16_t genommen, die mit 2⁴ skaliert wird, also
das Komma 4 Stellen nach links geschoben (rechnet intern also in 1/16).
Damit kann man etwa 1.2 und 3.45 abdecken, wenn auch nicht exakt.
Auch kennen meine Templates aktuell keine Saturierung, was den Vergleich
noch fragwürdiger macht.
Trotzdem als Orientierung, was der avr-g++ 4.7.2 aus dieser Funktion
macht:
Sieht also doch gar nicht so schlimm, aus - du hattest ja nur 1/4 der
Effizienz der internen Lösung gefordert :-)
Natürlich ist es schwer, mit einer compilerinternen Lösung mitzuhalten
(insbesondere können meine templates ja nur auf vorhandenen Datentypen
aufsetzen mit 8/16/32/64 Bit, und prinzipiell nicht mit 24 Bi oder noch
krummeren, solange der gcc sie nicht anbietet).
Aber so schlimm ist es dann doch nicht geworden, wie ich befürchtet
hatte.
Aber wie gesagt: der Vergleich hinkt von vornherein, wenn man
unterschiedliche Wortbreiten vergleicht.
Klaus Wachtler schrieb:> Johann L. schrieb:>> Johann L. schrieb:>>> Ich behaupte jetze einfach mal, in C++ ist das nicht naheliegend.>>>>>> Wie würde zB eine einfache Klasse / Template konkret aussehen, die>>> folgenden Code mit der gleichen Effizienz wie avr-gcc implementiert?>>>> #include <stdfix.h>>>>>>> const __flash accum offset[] = { 1.2k, -3.45k };>>>>>> sat accum add_offset (sat accum a, unsigned i)>>> {>>> return a + offset[i];>>> } >>>> Und von mir aus auch mit 1/4 der Performance... avr-gcc brauch dafür 26>>> bytes Code (ATmega8); Funktionsaufrufe werden nicht benötigt.>>>> Zum Vergleich: Hierist der Code vom avr-gcc:add_offset:>> push r16>> push r17>> /* prologue: function */>> /* frame size = 0 */>> /* stack size = 2 */>> .L__stack_usage = 2>> lsl r20>> rol r21>> lsl r20>> rol r21>> subi r20,lo8(-(offset))>> sbci r21,hi8(-(offset))>> movw r30,r20>> lpm r16,Z+>> lpm r17,Z+>> lpm r18,Z+>> lpm r19,Z>> add r22,r16>> adc r23,r17>> adc r24,r18>> adc r25,r19>> brvc 0f>> ldi r25,0x80>> cp r19,r25>> sbc r24,r24>> sbci r25,0>> mov r22,r24>> mov r23,r24>> 0:>> /* epilogue start */>> pop r17>> pop r16>> ret> Dein accum kenne ich jetzt nicht und habehttp://gcc.gnu.org/wiki/avr-gcc#Fixed-Point_Support> Trotzdem als Orientierung, was der avr-g++ 4.7.2 aus dieser Funktion> macht:>
>> Sieht also doch gar nicht so schlimm, aus - du hattest ja nur 1/4 der> Effizienz der internen Lösung gefordert :-)
Im C-Beispiel ist die Berechnung mit 32 Bit: accum ist Q-Format s16.15,
d.h. 1 Vorzeichenbit, 16 Bits vor dem Komma und 15 danach.
Zudem saturiert der Code im C-Beispiel, d.h. 40000 + 40000 läüft nicht
über zu einem negativen Wert sondern bleibt beim Maximum, also knapp
65535, stehen.
Diese Saturiereung geschieht ohne (inter) mit 64 Bit zu rechnen.
Was mit an der C++ Lösung nicht gefällt ist dass die Konstanten als int
angegeben werden, d.h. es ist nicht ersichtlich, welchen Wert "120" im
Endeffekt hat.
Dritter Nachteil ist, dass die Konstanten in der Lookuop-Tabelle nicht
im Flash stehen, und da bekommt man sie auch mit progmem nicht hin, denn
dein accum-Typ ist kein POD (pretty old data).
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Johann L. schrieb:> Was mit an der C++ Lösung nicht gefällt ist dass die Konstanten als int> angegeben werden, d.h. es ist nicht ersichtlich, welchen Wert "120" im> Endeffekt hat.
Das hängt natürlich davon ab, welche Konstruktoren man spendiert.
In meiner Implementation gibt es davon 2:
a) Initialisierung mit einer Gleitkommazahl
b) wie hier mit 2 ganzen Zahlen, deren Quotient dann den Wert ergibt
(120,100) ergibt also 1.20
Wenn man letzteren verwendet, vermeidet man prinzipiell jede
Gleitkommarechnung zur Laufzeit.
Der erstere kann das vermeiden, wenn der Compiler schon mitdenkt udn
gleich daraus den richtigen ganzen Werte macht. Ich gehe davon aus, daß
der gcc das schafft, habe es aber nicht getestet.