Hallo Leute,
ich hab ein Problem mit sprintf und floats. Ich möchte (wie viele andere
auch) floats auf einem Display anzeigen lassen. Ich habe auch schon
ziemlich viele Beiträge aus dem Forum gelesen, deswegen bitte ich um
Entschuldigung, wenn ich die Lösung übersehen haben sollte.
Da mein aktuelles Projekt zu groß wäre, um hier komplett reingestellt zu
werden habe ich ein Testprojekt angelegt, das die gleiche Problematik
zeigt.
An den dabei benutzten Bibliotheken für TWI und Display liegt es nicht,
wie auch der AVR-Debugger bestätigt.
Meine Umgebung:
WinAVR 20090313
AVR Studio 4.18 SP2 build 700
Projekteinstellungen (aus dem AVR Studio):
General:
atmega8, 10Mhz, -Os, -funsigned-char, -funsigned-bitfields,
-fpack-struct, -fshort-enums
Libraries:
libc.a
libprintf_flt.a
libm.a
Custom options:
[All files]
-Wall -gdwarf-2 -std=gnu99 -DF_CPU=10000000UL -Os -funsigned-char
-funsigned-bitfields -fpack-struct -fshort-enums
[Linker Options]
-Wl,-u,vfprintf
Das Makefile habe ich angehängt.
Meine Testdatei "sprintf_float_test.c":
So, ich hab mir jetzt nen Benutzer angelegt ;) Editieren macht manches
einfacher..
Die sprintf-Zeile habe ich zwischenzeitlich auch abgeändert. Die kommt
leider noch von einer Version, als die Variablen "erste_Zeile" und
"zweite_Zeile" unsigned chars waren.
Aber selbst wenn... einen char-Pointer auf char-Pointer zu casten sollte
keinen Unterschied machen.
Trotzdem danke!
Uwe
Guten Morgen,
wenn ich die libc.a entferne funktioniert es plötzlich. Allerdings bläht
sich das Programm um einiges auf:
Größe des erzeugten Codes (Reihenfolge der Libs beachten!)
Danke für den Tip! Kannst du auch erklären, was dahinter steckt? Und ob
man nicht evt. doch an die Optimierungen der libc kommen kann, auch wenn
man floats braucht? Immerhin würde ich ja knappe 20% Speicher sparen,
wenn ich die libc benutzen könnte.
Uwe
Uwe Sauter schrieb:
> man nicht evt. doch an die Optimierungen der libc kommen kann, auch wenn> man floats braucht? Immerhin würde ich ja knappe 20% Speicher sparen,> wenn ich die libc benutzen könnte.
Du solltest dich lieber fragen, ob du unbedingt sprintf brauchst, oder
ob es andere Nicht-Code-Monster nicht auch tun (zb dtostrf()). Und bei
der Gelegenheit kannst du dich auch fragen, ob es eigentlich überhaupt
float sein muss, oder ob Fixed Point Arithmetik nicht auch zum Ziel
führt.
Floating Point Arithmetik ist für einen µC in der Leistungklasse eines
Mega8 Knochenarbeit.
Es beantwortet zwar nicht die Frage, aber als gedanklicher Anstoß ist
der Kommentar natürlich auch gerne gesehen ;)
Ich bin mir der Problematik mit den floats (zumindestens in groben
Zügen) bewusst. Allerdings habe ich von Fixed Point Arithmetik keine
Ahnung (was nicht heißt, dass das nie der Fall sein wird). Ich stehe
aber etwas unter Druck und habe deswegen weder Lust noch Zeit, mich
darin jetzt zu vertiefen.
Karl heinz Buchegger schrieb:
> andere Nicht-Code-Monster nicht auch tun (zb dtostrf()).
Die Funktion muss ich wohl übersehen haben. Aber damit müsste man ja die
Funktion der "großen" libprintf_flt durch die "kleine" libprintf_min
ersetzen können, oder?
Welche anderen "Nicht-Code-Monster" gibt es denn noch, die aus einer
float einen string (char array) machen?
Uwe
Uwe Sauter schrieb:
> Die Funktion muss ich wohl übersehen haben. Aber damit müsste man ja die> Funktion der "großen" libprintf_flt durch die "kleine" libprintf_min> ersetzen können, oder?
Yep. Du kannst zurückfallen auf die
'Nicht-Spezial-printf-Library'-Variante
> Welche anderen "Nicht-Code-Monster" gibt es denn noch, die aus einer> float einen string (char array) machen?
dtostrf() ist ein guter Ersatz.
printf und Konsorten lohnen sich meistens nur dann, wenn du spezielle
Formatieranforderungen hast, wie zb Ausrichtung in einem Feld, führende
0-en, Einbettung einer Zahl in einen Ausgabestring (aber das ist schon
Grnazfall). In allen anderen Fällen bist du meistens mit den atoi(),
atol(), dtostrf() Funktionen meistens besser bedient.
> Allerdings habe ich von Fixed Point Arithmetik keine> Ahnung (was nicht heißt, dass das nie der Fall sein wird).
Aber geh. Du machst das jeden Tag, wenn du anstelle von Euros (mit
Kommazahlen) in Cents (ohne Kommazahlen) rechnest. Fixed Point ist im
Grunde nichts anderes als: alles wird in Einheiten einer kleinen
Basiseinheit ausgedrückt. Anstelle von Grad Celsius (mit Kommazahlen)
drücken deine Zahlen einfach Hunderstel Grad Celsius aus und schon
kannst du Temperaturen mit 1/100 Grad genau berechnen.
Additionen und Subtraktionen laufen wie gewohnt. Bei
Multiplikationen/Divisionen muss man den Skalierfaktor einmal
rausrechnen und bei der Ausgabe/Eingabe wird der Kommapunkt an die
entsprechende Stelle eingeschmuggelt.
Das ist die Kurzfassung von Fixed Point Arithmetik :-)
@ Uwe Sauter (flightcontrol)
>Zügen) bewusst. Allerdings habe ich von Fixed Point Arithmetik keine>Ahnung (was nicht heißt, dass das nie der Fall sein wird).
Na dann lies mal was über Festkommaarithmetik.
MFG
Falk
Karl heinz Buchegger schrieb:
> Einbettung einer Zahl in einen Ausgabestring
Das ist ja genau das, was ich mache. Eine float in einen string füttern,
der dann auf dem Display ausgegeben werden soll.
Es ist nur so, dass ich in der Menüstruktur, die hinter der ganzen Sache
steht, die Formatierungsstrings der auszugebenden Zeile hinterlegt habe
und meine Funktion, die den string ans Display schickt dadurch ohne
überlegen zu müssen für ints, floats oder sogar andere strings geeignet
ist.
Aber das werde ich wohl dann noch umschreiben auf dtostrf().
Karl heinz Buchegger schrieb:
>> Allerdings habe ich von Fixed Point Arithmetik keine>> Ahnung (was nicht heißt, dass das nie der Fall sein wird).> Aber geh. Du machst das jeden Tag, wenn du anstelle von Euros (mit> Kommazahlen) in Cents (ohne Kommazahlen) rechnest.
Hm. Dann hab ich wohl das Konzept der FP-Arithmetik völlig falsch
verstanden.
Meine Meinung war bisher:
- Ich Teile ein Byte/Word auf in x Bit vor dem Komma und y Bit nach dem
Komma
- die x Bit vor dem Komma werden wie eine (u)intx_t behandelt
- die y Bit nach dem Komma werden als Kehrwert betrachtet (und rückwärts
gelesen)
Beispiel (der Punkt trennt x von y):
1
x=4, y=4: 0001.0000 wäre 1
2
0000.1000 wäre 1/2
3
0000.0100 wäre 1/3
4
0000.1100 wäre 1/4
Allerdings geht mir gerade auf, dass ich dann Probleme hätte, wenn ich
etwas anderes als 1 durch irgendwas darstellen wollte...
Die deutsche Wikipedia-Seite ist auch keine große Hilfe. Der Link von
Falk verspricht mehr ;) Danke!
Uwe Sauter schrieb:
> Es ist nur so, dass ich in der Menüstruktur, die hinter der ganzen Sache> steht, die Formatierungsstrings der auszugebenden Zeile hinterlegt habe> und meine Funktion, die den string ans Display schickt dadurch ohne> überlegen zu müssen für ints, floats oder sogar andere strings geeignet> ist.
Aber du hast ja wahrscheinlich nur einen Wert pro Menüpunkt. D.h. eine
Kennung samt Abfrage in der Display-Schick Funktion, die je nachdem
itoa, utoa, ltoa oder dtostrf benutzt, tuts auch bei wahrscheinlich
massiver Code-Einsaprung :-)
Nichts gegen sprintf und Konsorten. Ich benutz die auch gerne. Aber wenn
ich in Speichernöten bin, ist das das erste was ersetzt wird.
> - Ich Teile ein Byte/Word auf in x Bit vor dem Komma und y Bit nach dem> Komma> - die x Bit vor dem Komma werden wie eine (u)intx_t behandelt> - die y Bit nach dem Komma werden als Kehrwert betrachtet (und rückwärts> gelesen)>> Beispiel (der Punkt trennt x von y):>
1
> x=4, y=4: 0001.0000 wäre 1
2
> 0000.1000 wäre 1/2
3
> 0000.0100 wäre 1/3
4
> 0000.1100 wäre 1/4
5
>
Du bist gerade auf dem Weg eine echte Floating Point Unit zu entwickeln.
Gib noch einen Exponenten dazu und du bist dort :-)
Die Idee zu dieser etwas verqueren Vorstellung kommt letztlich von
Wikipedia, wie mir eben wieder bewusst wurde. Dort steht:
R =real, F=Festkomma, 4Bit vor dem Komma, 4 dahinter
1
0R = 00000000F
2
1R = 00010000F
3
10R = 10100000F
4
0,5R = 00001000F
5
0,625R = 00001010F
6
0,0625R = 00000001F
7
15,9375R = 11111111F
Wenn ich mir also die Zeile
1
0,5R = 00001000F
betrachte, dann bin ich genau bei dem, was ich oben auch schon
geschrieben habe. Allerdings passt
1
0,625R = 00001010F
schon nicht mehr in mein Schema.
Auch Festkommaarithmetik geht weniger auf die technische Darstellung
ein sondern mehr auf die Vermeindung von Gleitkommazahlen durch Wählen
von anderen Einheiten bzw. eines anderen Wertebereichs.
Kann mir da jemand noch auf die Sprünge helfen?
Was steht im Nachkommateil einer FP-Variable wenn ich 0.5, 0.382, etc.
speichern will? Oder anders gefragt: wie wird der Nachkommateil
aufgebaut?
Die Annahme, dass der Teil vor dem Komma wie eine normale int
interpretiert wird, ist doch hoffentlich richtig?
@ Uwe Sauter (flightcontrol)
>Was steht im Nachkommateil einer FP-Variable wenn ich 0.5, 0.382, etc.>speichern will?
Eben diese Zahl, nur halt binär.
> Oder anders gefragt: wie wird der Nachkommateil aufgebaut?
Normal.
>Die Annahme, dass der Teil vor dem Komma wie eine normale int>interpretiert wird, ist doch hoffentlich richtig?
Nöö, Fließkommazahlen haben gar keine Vorkommastellen ;-), die sind
nämlich normalisiert. Sprich, die Zahl wird immer als 0,irgendwas
gespeichert. Der Exponent gibt dann die Kommastelle an.
Also 57,78 wird als 0,5778e2 gespeichert, das ganze naürlich aber als
Binäzahl.
MFG
Falk
Uwe Sauter schrieb:
> Kann mir da jemand noch auf die Sprünge helfen?> Was steht im Nachkommateil einer FP-Variable wenn ich 0.5, 0.382, etc.> speichern will? Oder anders gefragt: wie wird der Nachkommateil> aufgebaut?
In dem Fall gehst du das falsch an.
Die erste Frage lautet
Was will ich eigentlich darstellen? Was sind meine Werte?
Die zweite Frage lautet
Wieviele Nachkommastellen benötigt ich dafür
Von diesen beiden Antworten hängt es ab, was du als Vielfaches wählst.
> Die Annahme, dass der Teil vor dem Komma wie eine normale int> interpretiert wird, ist doch hoffentlich richtig?
Die komplette Zahl wird wie ein normaler int interpretiert. Es gibt da
keine Bits für 1/8 oder dergleichen.
ANgenommen du willst Euros miteinander verrechnen.
Das kannst du machen ....
2.45
+ 12.68
+ 8.99
------
24.12
und dafür die Floating Point Unit auspacken.
Das musst du aber nicht. Du kannst auch ganz einfach sagen: Anstelle von
Euros rechne ich einfach alles in Cent und (euromässig gesehen) 2
Nachkommastellen reichen mir
245 Cent
1268 Cent
899 Cent
-----
2412 Cent
Ade Floating Point Unit. Welcome Integer Addition
Alles was du berücksichtigen musst, ist
Bei der Umrechnung von Euro in Cent sind die Dezimalpunkte weggefallen.
Bei der Ausgabe willst du wieder Euros haben und musst daher den
Dezimalpunkt wieder einschmuggeln. Das ist aber einfach, da du ja weißt,
dass der zwischen Hunderter und Zehnerstelle kommen muss (eine direkte
Konsequenz der Festlegung auf 2 Euro-Nachkommastellen)
Sagst du dir: Hmm 2 Nachkommastellen werden nicht reichen, ich will ja
auch Prozente berechnen und wenn ich die addiere brauch ich eine
Kommastelle mehr, dann, ja, dann mach das doch einfach. Aus 2 Euro 45
werden dann eben 2450 anstelle von 245
2450
12680
8990
-------
24120
und da deine Zahlen jetzt offensichtlich Zehntelcent darstellen, sind
auch das wieder 24 euro und 12 Cent (und 0 Zehntelcent)
Eigentlich unglaublich simpel :-)
(Es sei aber auch nicht verschwiegen, dass man jetzt ein bischen auf die
Wertebereich aufpassen muss. Stichwort Overflow)
Ich glaube, ich habe ein richtig GROSSES Verständnisproblem.
Was du gerade erklärt hast ist für mich einfach eine Änderung der Basis,
auf die sich die int bezieht. Man könnte das evt. auch als Normierung
bezeichnen.
Soll soviel heißen wie: die Einheit wird eben von Volt in Millivolt
geändert, deswegen muss die Zahl mit 1000 multipliziert werden. Die
Genauigkeit von 0,01V oder 0,001V wird damit erreicht, ich brauche aber
nur ganze Zahlen, keine rationalen.
Die Trennung zwischen vor und nach dem Komma findet also quasi durch die
Normierung statt.
Der Wikipedia-Artikel allerdings scheint mir ein Byte in zwei Bereiche
aufzuteilen: x Bit sind für die Zahl vor dem Komma zuständig, y Bit sind
für die Zahl nach dem Komma zuständig.
...
Ach nein, der Wiki-Artikel ist einfach nur doof geschrieben *mir geht
ein Licht auf*. Leider erwähnt der Artikel nichts von der Basis, auf die
sich der Wertebereich bezieht.
Manchmal steh ich aber auch echt auf dem Schlauch...
Dann jetz noch die Frage, ob es in der avr-libc Ausgabe-Funktionen für
festkommazahlen gibt. Also quasi eine Funktion, der ich ne int übergebe
und zusätzlich die Anzahl Stellen hinter dem Komma oder eben die Basis,
mit der die int normiert ist.
DANKE!!
Zurück zur Ausgangsfrage:
Uwe Sauter schrieb:
> Danke für den Tip! Kannst du auch erklären, was dahinter steckt? Und ob> man nicht evt. doch an die Optimierungen der libc kommen kann, auch wenn> man floats braucht? Immerhin würde ich ja knappe 20% Speicher sparen,> wenn ich die libc benutzen könnte.
Kann mir jemand erklären, warum es ohne libc funktioniert?
Stimmt meine Vermutung, dass die libc zwar für den AVR optimiert ist,
aber floats nicht unterstützt?
Und warum in aller Welt ist die Reihenfolge der Libs ausschlaggebend,
wenn man gegen alle drei libs linkt?
Wenn gegen die libc als letzte Lib gelinkt wird, werden nur 14 Bytes
mehr Platz verbraucht. Wird die libc dann allerdings auch genutzt oder
kann man sich die 14 Bytes auch sparen?
Und sollte man im FAQ nicht explizit erwähnen, dass man die libc nicht
benutzten darf?
Uwe Sauter schrieb:
> Ich glaube, ich habe ein richtig GROSSES Verständnisproblem.>> Was du gerade erklärt hast ist für mich einfach eine Änderung der Basis,> auf die sich die int bezieht. Man könnte das evt. auch als Normierung> bezeichnen.
Bingo!
Jetzt hast du's :-)
> Dann jetz noch die Frage, ob es in der avr-libc Ausgabe-Funktionen für> festkommazahlen gibt. Also quasi eine Funktion, der ich ne int übergebe> und zusätzlich die Anzahl Stellen hinter dem Komma oder eben die Basis,> mit der die int normiert ist.
Nein. Leider nicht.
Ist aber keine Hexerei sich sowas zu schreiben.
Wenn du dir sprintf leisten kannst (ohne Floating Point), dann geht zb
sowas:
1
intEuros;// in Wirklichkeit Cent
2
3
sprintf(buffer,"%d.%02d",Euros/100,Euros%100);
Kannst du dir sprintf auch nicht leisten, dann kann man natürlich auch 2
Ausgabefunktionen für zb int benutzen: Die eine gibt eine Zahl so aus,
wie sie ist, die andere fügt noch führende 0-en ein
1
voidprint_int(inti)
2
{
3
charbuffer[5];
4
itoa(buffer,i,10);
5
print_s(buffer);
6
}
7
8
voidprint_int_zero(inti)
9
{
10
if(i<10)
11
print_c('0');
12
printf_int(i);
13
}
14
15
voidprint_Euro(inteuroCent)
16
{
17
print_int(euroCent/100);
18
print_int_zeros(euroCent%100);
19
}
und natürlich gibt es dann auch noch die Möglichkeit, den Dezimalpunkt
während der Ausgabe an die richtige Stelle einzuschmuggeln
1
voidprint_Euro(inteuroCent)
2
{
3
intvor=euroCent/100;
4
intnach=euroCent%100;
5
6
print_int(vor);
7
print_c('.');
8
if(nach<10)
9
print_c('0');
10
print_int(nach);
11
}
Ob man das dann so wie hier auf 2 Nachkommastellen ausprogrammiert oder
ob man sich generelle Funktionen schreibt, hängt im wesentlichen auch
davon ab welchen Skalierfaktor man wählt.
PS: 10 ist kein so guter Skalierfaktor. Eine 2-er Potenz wäre besser,
dann sind Disivion bzw. Modulo-Division trivial :-)
Danke für die Beispiele, wären nicht nötig gewesen. Ich wollte ja nur
wissen, ob es Funktionen gibt oder ob noch etwas Fleißarbeit nötig ist.
Karl heinz Buchegger schrieb:
> PS: 10 ist kein so guter Skalierfaktor. Eine 2-er Potenz wäre besser,> dann sind Disivion bzw. Modulo-Division trivial :-)
Da geb ich dir recht. Aber um noch ein wenig auf mein Projekt
einzugehen:
Ich benutzte Timer2 als Zeitbasis für Timer1. Timer2 läuft also mit
Systemtakt (10MHz) im CTC-Modus und toggelt alle 50 Takte (=5µsec) den
Pin PD5 per Interrupt. Timer1 ist so eingestellt, dass eine steigende
Flanke an PD5 als Takt benutzt wird. Die Zeitbasis für Timer1 ist also
10µsec.
Basierend auf dieser Basis lasse ich Timer1 im Output-Compare-Modus
laufen und schalte in den ISR für OCR1A und OCR1B Pins ein und aus.
Die Werte für OCR1A/B werden aus Werten berechnet, die durch Taster und
das oben erwähnte Menü eingegeben werden können.
Ich muss also quasi die Darstellung des Eingabewerts auf dem Display
ausgeben.
Wenn also der Wert an sich "50" ist, muss ich auf dem Display "50" *
Zeitbasis = 0,5 ms ausgeben (ja, in Millisekunden, nicht Mikro). Sonst
wäre ich auch gar nicht auf die Idee gekommen, sprintf zu benutzen. Ich
habe nur eben in den einzelnen Menüpunkten unterschiedliche
Formatierungsanforderungen, da die Sequenz, die der Kern des Projekts
ist auch beliebig oft wiederholbar sein soll. Und bei Wiederholungen
machen reelle Zahlen keinen Sinn....
Uwe Sauter schrieb:
> Kann mir jemand erklären, warum es ohne libc funktioniert?
Wenn du die libc entfernst, linkst du nicht ohne sie. Sie wird immer
automatisch der Linkliste hinzugefügt, aber am Ende.
> Stimmt meine Vermutung, dass die libc zwar für den AVR optimiert ist,> aber floats nicht unterstützt?
Es geht hier nur um die printf-Familie. Da man selten Floats braucht,
ist in der libc eine Variante, die keine Floats unterstützt, um Platz zu
sparen. Du hast doch schon selber festgestellt, um wie viel größer die
Float unterstützende Version ist.
> Und warum in aller Welt ist die Reihenfolge der Libs ausschlaggebend,> wenn man gegen alle drei libs linkt?
Weil Objekte mit gleichem Namen in mehreren Libs sein können, und nur
das erste gefundene wird genommen. Konkret geht es hier um die Funktion
vfprintf (dort passiert die eigentliche Arbeit der printf-Famile). In
beiden Libs ist sie drin, in libc (ohne Floats) und in libprintf_flt
(mit Floats). Welche Implementation dieser Funktion dann tatsächlich
genommen wird, hängt dann halt davon ab, welche der beiden Libs zuerst
gelinkt wird.
> Wenn gegen die libc als letzte Lib gelinkt wird, werden nur 14 Bytes> mehr Platz verbraucht. Wird die libc dann allerdings auch genutzt oder> kann man sich die 14 Bytes auch sparen?>> Und sollte man im FAQ nicht explizit erwähnen, dass man die libc nicht> benutzten darf?
Wie bereits gesagt, die libc wird immer benutzt, durch das manuelle
hinzufügen änderst du nur die Reihenfolge.
So ganz versteh ich dann aber nicht, warum sich dann die Version (2) und
(3) in meiner Tabelle in der Größe unterscheiden.
Zur Erinnerung: in (2) wurde nur libprinft_flt und libm explizit
angegeben; in (3) danach auch noch libc.
Wenn die libc sowieso automatisch am Ende angefügt wird, warum gibt es
dann einen Unterschied?
Stefan Ernst schrieb:
> Konkret geht es hier um die Funktion> vfprintf (dort passiert die eigentliche Arbeit der printf-Famile). In> beiden Libs ist sie drin, in libc (ohne Floats) und in libprintf_flt> (mit Floats).
Wozu gibt es dann noch die libprintf_min? Ist es nicht so, dass in der
libc überhaupt keine *printf-Funktionen enthalten sind?
Und wenn in der libc keine *printf hinterlegt sind, sollte es dann keine
Rolle spielen, wann gegen sie gelinkt wird?
Uwe Sauter schrieb:
> Aber selbst wenn... einen char-Pointer auf char-Pointer zu casten sollte> keinen Unterschied machen.
Das schon. Aber das machst Du dort nicht.
Du castest ein (char *) [17] auf ein (char *). Mag sein das das hier
nicht relevant ist, aber das Du es überhaupt tust ist ersten
überflüssig, zeigt zweitens das Du den Unterschied nicht kennst und kann
drittens zu weiteren Unklarheiten führen wenn Du denn tatsächlich mal
eine Funktion benutzt die aus irgendeinem Grunde (char *) [] erwartet.
Uwe Sauter schrieb:
> So ganz versteh ich dann aber nicht, warum sich dann die Version (2) und> (3) in meiner Tabelle in der Größe unterscheiden.
Der Unterschied ist der, dass die libc bei (2) nur einmal bearbeitet
wird, und bei (3) zweimal. Was bei der doppelten Bearbeitung die
zusätzlichen Bytes verursacht, kann ich dir so nicht sagen. Ein Blick in
das Map-File könnte dir darüber Aufschluss geben.
> Wozu gibt es dann noch die libprintf_min?
Die hat Einschränkungen bei den verwendbaren Conversion-Flags, um noch
mehr Platz zu sparen.
> Ist es nicht so, dass in der> libc überhaupt keine *printf-Funktionen enthalten sind?>> Und wenn in der libc keine *printf hinterlegt sind, sollte es dann keine> Rolle spielen, wann gegen sie gelinkt wird?
Du hast doch sicher auch schon mal ein printf benutzt, ohne
libprintf_flt oder libprintf_min hinzu gelinkt zu haben, oder? Auf welch
magische Weise hast das dann wohl funktioniert?
Uwe Sauter schrieb:
> So ganz versteh ich dann aber nicht, warum sich dann die Version (2) und> (3) in meiner Tabelle in der Größe unterscheiden.
Dazu müsstest du die Symboltabellen der beiden Ergebnisse vergleichen
(das ist die Ausgabe des Kommandos »avr-nm <yourfile.elf>«).
> Wozu gibt es dann noch die libprintf_min?
Falls man mal unbedingt für irgendwas ein printf benötigt (und sei's
zum Debuggen), die Standardvariante aber "gerade so" nicht mehr passt,
und man daher alle verzichtbaren Dinge (wie Feldbreitenbegrenzungen
etc.) weglassen möchte.
> Ist es nicht so, dass in der> libc überhaupt keine *printf-Funktionen enthalten sind?
Ja, es ist nicht so. ;-) Die Standardvariante ist in der normalen
libc.a enthalten. Die Bibliotheken libprintf_min.a und
libprintf_flt.a enthalten nur die Sondercompilate.
Ganz glücklich bin ich mit dieser Lösung ringsum nicht. Sie skaliert
miserabel, insbesondere bei "orthogonalen" Eigenschaften. Wenn man
also beispielsweise noch Unterstützung für 64-bit-Integer-Formate
mit reinbringen will, dann würden sich praktisch schon doppelt so
viele Varianten ergeben (min, min_int64, std, std_int64, flt,
flt_int64). Noch ein weiteres Kriterium mehr, und man hätte nochmal
doppelt so viele... Leider ist mir einfach bislang nichts besseres
eingefallen.
Jörg,
mal so am Rande. Hat das etwas umständliche Vorgehen mit -Wl,-u,vfprintf
einen besonderen Grund? Wäre es nicht praktischer, einfach Kopien der
Frontend-Funktionen mit in die Speziallibs zu packen?
Stefan Ernst schrieb:
> Wäre es nicht praktischer, einfach Kopien der> Frontend-Funktionen mit in die Speziallibs zu packen?
Ja, wäre es, hat nur niemand gemacht. Die Idee, dass man das natürlich
auch machen könnte, ist mir leider erst gekommen, als die umständliche
Variante mit -uvfprintf schon in allerlei FAQs und Makefile-Templates
angekommen war. :-/
Manchmal hat man eben Tomaten auf den Augen. ;-)
Stefan Ernst schrieb:
> Du hast doch sicher auch schon mal ein printf benutzt, ohne> libprintf_flt oder libprintf_min hinzu gelinkt zu haben, oder? Auf welch> magische Weise hast das dann wohl funktioniert?
Nein ;) Bis jetzt habe ich itoa() benutzt und dann den Inhalt des
bearbeiteten Strings in den Sammel-String Byte für Byte kopiert ;)
Bisher musste ich aber auch noch keine floats ausgeben.
Die beiden Ausgaben von "avr-nm" habe ich mal angehängt. Wenn ich die
Dateien richtig interpretieren unterscheiden sich nur einige Adressen.
Aber insgesamt kann ich wenig damit anfangen.
Gibt es in der Doku einen Hinweis, welche libprintf welchen
Funktionsumfang hat?
Den Hinweis, dass libc nicht eingetragen sein darf, habe ich mal in den
FAQ hinzugefügt.
Uwe Sauter schrieb:
> Die beiden Ausgaben von "avr-nm" habe ich mal angehängt. Wenn ich die> Dateien richtig interpretieren unterscheiden sich nur einige Adressen.> Aber insgesamt kann ich wenig damit anfangen.
Wenn ich das richtig sehe, unterscheiden sie sich ab vfprintf.
Vermutlicher Grund: mit der expliziten Angabe von libc hast Du die
Version in avr/lib/libc.a zuerst gelinkt und deren vfprintf hat eine
etwas andere Länge als die spezifischeren in avr/lib/avr5/libc.a,
avr/lib/avr6/libc.a usw., die der Compiler auswählt. Schau Dir die
Verzeichnisse mal an und vergleiche die Größen der libc.a. Die
differieren etwas.
> Den Hinweis, dass libc nicht eingetragen sein darf, habe ich mal in den> FAQ hinzugefügt.
Das halte ich für eher überflüssig. Es ist ein Grundelement der Sprache
C, dass die libc dazugelinkt wird, ohne dass sie speziell zu erwähnen
ist. Sie gehört bei C einfach automatisch dazu. (Ich verstehe zwar, dass
Du andere vor demselben Fehler schützen willst, aber so häufig ist der
nicht und wenn alle Fehler, die irgendwann begangen werden, erwähnt
würden, würde das doch ziemlich unübersichtlich.)
Uwe Sauter schrieb:> So ganz versteh ich dann aber nicht, warum sich dann die Version (2)> und (3) in meiner Tabelle in der Größe unterscheiden.>> Zur Erinnerung: in (2) wurde nur libprinft_flt und libm explizit> angegeben; in (3) danach auch noch libc.>> Wenn die libc sowieso automatisch am Ende angefügt wird, warum gibt es> dann einen Unterschied?
Am Ende wird nicht nur libc, sondern noch andere Dinge angehängt. Deren
Reihenfolge hast du durch explizite Angabe von libc verändert. Ich
schätze mal, daß es damit in irgendeiner Form zusammenhängt.
Jörg Wunsch schrieb:> Die Idee, dass man das natürlich> auch machen könnte, ist mir leider erst gekommen, als die umständliche> Variante mit -uvfprintf schon in allerlei FAQs und Makefile-Templates> angekommen war. :-/
Och, das ist ja kein Hinderungsgrund, es wäre ja quasi
abwärtskompatibel. ;-)
Der Hinderungsgrund ist nur, dass es Arbeit macht, das Makefile-
Gewurschtel entsprechend anzupassen. ;-)
Lieber wäre mir eigentlich eine geniale Idee, wie man davon ganz
weg kommen könnte...
> Lieber wäre mir eigentlich eine geniale Idee, wie man davon ganz> weg kommen könnte...
Ich würde ja vorschlagen, C++ zu nehmen, aber das wird wohl nicht arg
hilfreich sein ;-)
Bei printf ist ja das Problem, daß alles in einer Funktion steckt und
man daher den Code für alle unterstützten Formatierungen braucht, auch
wenn er im konkreten Programm gar nicht verwendet wird.
Man kann sich in C++ aber eine Klasse basteln, die einen iostream in
großen Teilen nachbildet. Dann hätte man ein cout statt printf. Der
große Vorteil gegenüber printf ist, daß man für jeden Typ eine separate
Funktion hat, und wenn man die nicht aufruft, muß sie auch nicht mit
gelinkt werden. Damit ist das Problem da gar nicht existent.
Da ich mich nicht wirklich mit Libraries auskenne, kommt hier ein
bescheidener Vorschlag eines Laien:
Wie wäre es, wenn man den Funktionsumfang der gewünschten printf durch
#define begrenzt, die in den printf-Sourcen eben die jeweiligen
Codeabschnitte ein-/ausschalten.
Mir ist bewusst, dass man dann printf jedes mal neu übersetzen muss und
es dementsprechend eigentlich keine Lib mehr ist. Andererseits könnte
man so nur die Funktionalität auswählen, die printf haben soll.
Falls das kein guter Vorschlag ist, lese ich gerne eine Erklärung,
warum. Ich bin ja lernfähig ;)
Rolf Magnus schrieb:> Ich würde ja vorschlagen, C++ zu nehmen, aber das wird wohl nicht arg> hilfreich sein ;-)
Richtig, das hilft für printf nicht, ich weiß, dass die ostream-
Formatierungen in C++ das Problem lösen. Dafür sind sie halt reichlich
umständlich zu bedienen im Vergleich zum "Schweizer Messer" printf.
Uwe Sauter schrieb:> Wie wäre es, wenn man den Funktionsumfang der gewünschten printf durch> #define begrenzt, die in den printf-Sourcen eben die jeweiligen> Codeabschnitte ein-/ausschalten.
Genau so ist es ja im Sourcecode gelöst. Aber du hast das Problem
schon erkannt: es ist dann halt keine Library. Im Moment sind es
halt derer drei, je nachdem, mit welchen -D-Optionen compiliert worden
ist.