Hallo,
habe eine float Variable, die aufgetrennt werden soll nach "Zahl vor dem
Komma" und "Zahl nach dem Komma". Die Nachkomma-Stelle beträgt maximal 2
Ziffern.
Aufgetrennt werden soll nach dem Schema
12345,67:
a = 12345
b = 67
Leider kenne ich mich mit den Variabeltypen in C nicht besonders aus.
Würde es spontan so machen:
1
floatzahl=12345,67;
2
3
uint32_tvorkomma=zahl;// alles hinter dem Komma wird abgeschnitten
4
5
uint8_tnachkomma=zahl*100-vorkomma*100;// alles vor dem Komma wird abgeschnitten und um zwei Dezimalstellen nach 2links2 gerückt
Dann wäre
vorkomma = 12345
und
nachkomma = 67
Bin aber nicht ganz sicher, ob man einfach float- und uint-Variablen
miteinander verrechnen kann!?
Gibt es möglicherweise noch bessere Varianten zur Auftrennung?!?
PS: wollte es prinzipiell einfach ausprobieren und die entstandenen
Variablen auf einem LCD anzeigen lassen, leider wurde aber nur "Unsinn"
angezeigt.
Eine wirklich einfache und anfängerfreundliche Methode, verschiedene
Variabeltypen mit dem AVR-Studio bzw. den dort enthaltenen Libs auf
einem LCD darzustellen, gibt es wahrscheinlich nicht oder?
http://www.mikrocontroller.net/articles/FAQ#sprintf.28.29
mit sprintf hat es jedenfalls nicht funktioniert und mit
itoa()/ldc_string auch nicht, wobei der Fehler sicher bei mir lag.
gerri schrieb:> PS: wollte es prinzipiell einfach ausprobieren und die entstandenen> Variablen auf einem LCD anzeigen lassen, leider wurde aber nur "Unsinn"> angezeigt.
Kein Wunder. Auf dem LCD kann man nur Strings anzeigen lassen. Zahlen
müssen grundsätzlich erst in Text umgewandelt werden, bevor das Display
damit irgendetwas anfangen kann.
Noch mal zu der anderen Frage:
Gibt es zufällig eine "idiotensichere" Routine, die unkompliziert
dezimale Zahlen bis 32 Stellen auf einem 16x2 LCD ausgeben kann?
gerri schrieb:> Noch mal zu der anderen Frage:>> Gibt es zufällig eine "idiotensichere" Routine, die unkompliziert> dezimale Zahlen bis 32 Stellen auf einem 16x2 LCD ausgeben kann?
Wo hast du denn 32 Stellen in einer Integerzahl?
Du zäumst das Pferd am falschen Ende auf.
WEnn du mal dein Programm herzeigen würdest, dann könnte man auch
gezielt helfen.
Karl Heinz Buchegger schrieb:> Wo hast du denn 32 Stellen in einer Integerzahl?> Du zäumst das Pferd am falschen Ende auf.
Die Info bedeutet nur, dass es wenig Sinn hätte, größere Zahlen anzeigen
zu wollen, weil dann das LCD nur 32 Stellen hat.
> WEnn du mal dein Programm herzeigen würdest, dann könnte man auch> gezielt helfen.
Wenn ich irgendwie die entstandenen Zahlen überprüfen könnte (z.B. durch
LCD-Anzeige) wäre mir bereits sehr geholfen.
Ingo schrieb:> Sprintf, kann das glaube ich...
Dann muss man erst einen Buffer erstellen, der von der Größe her
explizit zur verwendeten Variabel-Art passt oder?
Heya ho. schrieb:> Sprintf() .... und flupp ... 16kB code sind sind weg.
Die 16 kB wären nicht so tragisch. Wenn das Programm später richtig
läuft, kann sprintf() ja wieder entfernt werden. Müsste nur zwischendrin
einen größeren Controller nehmen...
gerri schrieb:> Dann muss man erst einen Buffer erstellen, der von der Größe her> explizit zur verwendeten Variabel-Art passt oder?
Ja, und?
In welchem Zahlenbereich bewegen sich denn deine Werte wirklich?
Ob du in einer Funktion mal kurzfristig 20 Bytes für einen char-Buffer
reservierst oder nicht, ist ja meistens irrelevant.
sprintf wird teuer, wenn die float Version dazugelinkt werden muss. Für
eine Ausgabe zwischendurch ist es schon brauchbar, bei moderatem
Speicherverbrauch.
void lcd_float( float value )
{
int32_t whole = value;
uint8_t fraction = ( value - whole ) * 100 + 0.5;
char buffer[20];
sprintf( buffer, "%ld.%02d", whole, fraction );
lcd_string( buffer );
}
Danke Karl Heinz, das sieht ja schon mal gut aus!
Der längste Datentyp im Programm ist double (64 bit).
Wie lang muss dann hier der Buffer mindestens sein?
(schätze 20 Dezimalstellen)
Karl Heinz Buchegger schrieb:> In welchem Zahlenbereich bewegen sich denn deine Werte wirklich?> Ob du in einer Funktion mal kurzfristig 20 Bytes für einen char-Buffer> reservierst oder nicht, ist ja meistens irrelevant.
Wenn es nichts ausmacht, wenn der Buffer größer als nötig ist, kein
Problem :O)
Karl Heinz Buchegger schrieb:> sprintf wird teuer, wenn die float Version dazugelinkt werden muss.
Double Variablen würde ich im Moment schon gerne überprüfen können.
Wo wir beim Thema sind, im AVR-Studio bedeutet
float => Gleitkommazahl 32 bit
und
double => Gleitkommazahl 64 bit
oder?
gerri schrieb:> Karl Heinz Buchegger schrieb:>> sprintf wird teuer, wenn die float Version dazugelinkt werden muss.>> Double Variablen würde ich im Moment schon gerne überprüfen können.
Wobei, wenn nur 2 Nachkomma-Stellen im Gebrauch sind, kann man ja auch
vorher mit 100 multiplizieren und dann in uint umwandeln und anzeigen
und im kopf das komma an die richtige stelle setzen!?
gerri schrieb:> Danke Karl Heinz, das sieht ja schon mal gut aus!>> Der längste Datentyp im Programm ist double (64 bit).> Wie lang muss dann hier der Buffer mindestens sein?> (schätze 20 Dezimalstellen)
Das kommt eben drauf an, welche Zahl in der Variablen drinnen ist.
WEnn du 3.14159 im float stehen hast, dann brauchst du nur 1
Vorkommastelle. WEnn du aber 2.71E58 drinnen hast, dann hast du
theoretisch 58 Vorkommastellen (von denen bis auf die vordersten 3
theoretisch alle 0 sind.
Also entscheide dich endlich mal für einen Zahlenbereich in dem sich
deine Zahlen abspielen und lebe damit. Will man zu allgemein sein, dann
zwirbelt man sich einen Haufen Overhead auf, den man im konkreten gar
nicht braucht.
Karl Heinz Buchegger schrieb:> Also entscheide dich endlich mal für einen Zahlenbereich in dem sich> deine Zahlen abspielen und lebe damit. Will man zu allgemein sein, dann> zwirbelt man sich einen Haufen Overhead auf, den man im konkreten gar> nicht braucht.
Das ist richtig.
Karl Heinz Buchegger schrieb:> void lcd_float( float value )> {> int32_t whole = value;> uint8_t fraction = ( value - whole ) * 100 + 0.5;> char buffer[20];>> sprintf( buffer, "%ld.%02d", whole, fraction );> lcd_string( buffer );> }
Supersupervielen Dank dafür!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Langsam grenzt sich das Problem ein!
>uint8_t fraction = ( value - whole ) * 100 + 0.5;
Warum wird hier noch 0.5 addiert?
(habe das böse Gefühl, das so etwas in meinem Programm auch irgendwo
gemacht werden müsste, irgendwie stimmen die Grundrechenarten mit den
Nachkommastellen nicht 100%ig)
gerri schrieb:>>uint8_t fraction = ( value - whole ) * 100 + 0.5;>> Warum wird hier noch 0.5 addiert?
Rundungskorrektur.
Damit aus 0.99999999 eine glatte 1 wird und keine 0
> (habe das böse Gefühl, das so etwas in meinem Programm auch irgendwo> gemacht werden müsste, irgendwie stimmen die Grundrechenarten mit den> Nachkommastellen nicht 100%ig)
Die stimmen sowieso nicht.
Mit float hast du 5 bis 6 signifikante Stellen.
Achtung: signifikant! nicht Nachkomma.
Bei der Zahl 765.3241
hast du bereits 3 signifikante Stellen vor dem Komma. D.h. es sind nur
noch die ersten 2 bis 3 Stellen nach dem Komma vertrauenswürdig. Alles
dahinter ist gelogen. Und wenn du mit den Float ein bischen rechnen
musst, dann werden aus den 5 bis 6 signifikanten Stellen ganz schnell 4
bis 5. Und wird noch ein wenig mehr gerechnet, dann sinds nur noch 3 bis
4.
Floating Point hat mit dem was du dir in der Mathematik so vorstellst,
unbegrenzte Genauigkeit, nur sehr wenig zu tun.
Wenn du erwartest, dass bei
float sum = 0.0;
for( i = 0; i < 10; i++ )
sum += 0.1;
in sum eine 1.0000 rauskommt, dann hast du dich geschnitten.
Rechnen mit Floating Point
ist wie das Umschaufeln eines Sandhaufens
Jedesmal wenn man es macht
verliert man ein wenig Sand
und 'gewinnt' ein wenig Dreck.
Danke für die ausführliche Erklärung!!!
Karl Heinz Buchegger schrieb:> float sum = 0.0;>> for( i = 0; i < 10; i++ )> sum += 0.1;>> in sum eine 1.0000 rauskommt, dann hast du dich geschnitten.
Ok, dann war mein Programm falsch angelegt.
Habe es mittlerweile so umgestrickt, dass in der Hauptsache mit
natürlichen Zahlen gerechnet werden kann.
Jetzt hat sich aber ein anderes Problem aufgetan:
Nun muss nämlich mit Zahlen bis max. 9.999.999.999 gerechnet werden.
Das Problem:
Wenn die entsprechende Variable (uint32_t) größer als (ca.)
4.294.967.295 wird (32bit?!), läuft der Zähler durch.
Wenn die Variable als uint64_t deklariert wird, bleibt das Problem
weiter bestehen.
Woran kann es liegen? Wird die Variable möglicherweise trotzdem als
uint32_t behandelt???
Oder kommt die verwendete If-Abfrage mit so hohen Zahlen nicht klar?
gerri schrieb:>> float sum = 0.0;>>>> for( i = 0; i < 10; i++ )>> sum += 0.1;>>>> in sum eine 1.0000 rauskommt, dann hast du dich geschnitten.
PS: Welchen Wert hat denn nach Schleifenende die Variable sum? Null?
Du musst schon die Berechnung mit 64 Bit durchführen, nicht erst das
Ergebnis an eine 64-Bit-Variable zuweisen. Dann ist schon zu spät.
Das hier geht also nicht:
1
uint32_ta=5000000;
2
uint32_tb=5000000;
3
uint64_tc=a*b;
Denn a und b sind jeweils 32 Bit, also wird auch a * b nur mit 32 Bit
gerechnet.
Man muss mindestens einen der beiden Operanden in 64 Bit casten. Dann
wird mit 64 Bit gerechnet:
gerri schrieb:> PS: Welchen Wert hat denn nach Schleifenende die Variable sum? Null?
Nein, schon ungefähr 1. Aber es kann auch 0.9999irgendwas oder
1.000irgendwas sein.
gerri schrieb:> Wo wir beim Thema sind, im AVR-Studio bedeutet>> float => Gleitkommazahl 32 bit>> und>> double => Gleitkommazahl 64 bit>> oder?
Da da noch keiner drauf genatwortet hat: Nein.
double is beim avr-gcc auch 32 Bit breit, auch wenn das eigentlich nicht
ISO-C-Konform ist.
Hallo,
Danke für die Antworten!
Fabian O. schrieb:> Man muss mindestens einen der beiden Operanden in 64 Bit casten. Dann> wird mit 64 Bit gerechnet:uint32_t a = 5000000;> uint32_t b = 5000000;> uint64_t c = (uint64_t) a * b;
Hier könnte irgendwo das Problem liegen. Werde es heute Abend noch mal
genauer unter die Lupe nehmen.
Findet man irgendwo allgemeine Informationen zum Thema "Variablen
casten"?
Habe übrigens jetzt das Buch
"Mikrocomputertechnik mit Controllern der Atmel AVR-RISC-Familie" von
Günter Schmitt, 5. Auflage
vorliegen, falls mal jemand hier darauf Bezug nehmen will.
>> PS: Welchen Wert hat denn nach Schleifenende die Variable sum? Null?>> Nein, schon ungefähr 1. Aber es kann auch 0.9999irgendwas oder> 1.000irgendwas sein.Rolf Magnus schrieb:> double is beim avr-gcc auch 32 Bit breit, auch wenn das eigentlich nicht> ISO-C-Konform ist.
Dann war ich mit der ersten Programmversion anscheinend in mehrere
Fallstricke gleichzeitig gelaufen...
D.h. im Klartext, avr-gcc unterscheidet nicht zwischen double und
float!?!
gerri schrieb:> Wo wir beim Thema sind, im AVR-Studio bedeutet>> float => Gleitkommazahl 32 bit>> und>> double => Gleitkommazahl 64 bit>> oder?
nö, double und float sind im AVR gcc identisch 32bit
gerri schrieb:> Findet man irgendwo allgemeine Informationen zum Thema "Variablen> casten"?
Eigentlich sollte das in jedem ernstzunehmenden C-Buch in voller Länge
und Breite abgehandelt sein.
>> Habe übrigens jetzt das Buch> "Mikrocomputertechnik mit Controllern der Atmel AVR-RISC-Familie" von> Günter Schmitt, 5. Auflage> vorliegen, falls mal jemand hier darauf Bezug nehmen will.
Besorg dir noch ein allgemeines C-Buch. Sowas wie den Kernighan&Ritchie.
In diesen Spezialbüchern geht es mehr um die µC als um C. Die
Kurzeinführung in diesen Spezialbüchern kannst du normalerweise
vergessen. Da steht mehr als die Hälfte dessen, was du an Basiswissen
benötigst nicht drinnen.
FAQ: Datentypen in Operationen> D.h. im Klartext, avr-gcc unterscheidet nicht zwischen double und> float!?!
Exakt.
Und weiter im Klartext:
float benötigt man auf einem µC höchst selten
float (bzw. double) öffnet, wenn man nicht weiß was man tut, einen
ganzen neuen Sack an Problemkreisen.
float ist NICHT das Allheilmittel für alles!
http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Heya ho. schrieb:> Sprintf() .... und flupp ... 16kB code sind sind weg.
Niemand hat gesagt, dass du deine unausgereifte eigene Implementierung
von Sprintf() benutzen sollst ...
Karl Heinz Buchegger schrieb:> Besorg dir noch ein allgemeines C-Buch. Sowas wie den Kernighan&Ritchie.
Danke für den Tipp, habe es heute geordert.
Habe mittlerweile auch das Problem im Programm entdeckt. Leider ergibt
sich durch dessen Beseitigung ein neues Problem.
Hier eine kurze Darstellung...
------------------------------
Das hier:
uint64_t a = 100000000;
uint32_t b = a;
uint8_t c = b/10;
geht problemlos mit "Build", also ohne Fehlermeldungen. Leider kann b
den Inhalt von a im weiteren Programmverlauf nicht mehr richtig
aufnehmen, weil b nur eine 32-bit-Zahl ist.
------------------------------
Das hier:
uint64_t a = 100000000;
uint64_t b = a;
uint8_t c = b/10;
korrigiert das o.g. Problem, liefert aber "Build failed with 2 errors".
Wo könnte der Fehler liegen?
------------------------------
gerri schrieb:> Wo könnte der Fehler liegen?
In den Zeilen über der von dir genannten Fehlermeldung findest du genaue
Angaben zum Fehler.
Heya ho. schrieb:> Ein / bedeutet eine floating point division. schau mal nach was eine> integer division ist. allenfalls 'div' ?
Hier wird über C geredet. Lern erstmal C.
gerri schrieb:> Das hier:>> uint64_t a = 100000000;> uint32_t b = a;> uint8_t c = b/10;>> geht problemlos mit "Build", also ohne Fehlermeldungen. Leider kann b> den Inhalt von a im weiteren Programmverlauf nicht mehr richtig> aufnehmen, weil b nur eine 32-bit-Zahl ist.
100 Millionen passen aber locker in eine 32-bit-Zahl, die hört erst
bei reichlich 4 Milliarden auf.
Allerdings sind 100 Millionen geteilt durch 10 immer noch 10 Millionen,
wie bitte sollten die denn in eine 8-bit-Zahl passen?
Danke für die Antworten!
Jörg Wunsch schrieb:> gerri schrieb:>> Das hier:>>>> uint64_t a = 100000000;>> uint32_t b = a;>> uint8_t c = b/10;>>>> geht problemlos mit "Build", also ohne Fehlermeldungen. Leider kann b>> den Inhalt von a im weiteren Programmverlauf nicht mehr richtig>> aufnehmen, weil b nur eine 32-bit-Zahl ist.>> 100 Millionen passen aber locker in eine 32-bit-Zahl, die hört erst> bei reichlich 4 Milliarden auf.>> Allerdings sind 100 Millionen geteilt durch 10 immer noch 10 Millionen,> wie bitte sollten die denn in eine 8-bit-Zahl passen?
Das hier
uint64_t a = 100000000;
uint32_t b = a;
uint8_t c = b/10;
funktioniert problemlos.
Die Variable c nimmt nur einen kleinen Teil der Variable b auf, ist so
korrekt und funktioniert auch im Programm.
Die Frage ist erst mal, warum der Compiler plötzlich bei c = b/10 nicht
mehr mitmacht, wenn b statt uint32_t als uint64_t deklariert wird. Das
ist ja keine grundlegende Programmänderung.
nebenbei, a selber kann Werte bis 999.999.999 annehmen.
gerri schrieb:> Das hier>> uint64_t a = 100000000;> uint32_t b = a;> uint8_t c = b/10;>> funktioniert problemlos.
Dann musst du dazu sagen, wie du 'funktioniert' definierst.
Nur weil es durch den COmpiler geht, funktioniert es noch lange nicht.
Andere Menschen haben eine andere Definition von funktioniert. Für die
funktioniert es erst dann richtig, wenn auch das mathematisch richtige
Ergebnis rauskommt.
> Die Variable c nimmt nur einen kleinen Teil der Variable b auf, ist so> korrekt und funktioniert auch im Programm.
Gut. Dann ist deine Definition: Du willst alles bis auf die dezimale
Einerstelle des Ergebnisses haben und davon dann das Low-Byte. Das kann
hier aber keiner riechen, wenn du es nicht dazuschreibst.
> Die Frage ist erst mal, warum der Compiler plötzlich bei c = b/10 nicht> mehr mitmacht,
verdammt noch mal!
Der Compiler schreibtr nicht einfach hin: Ich mach nicht mehr mit.
Der schreibt eine Fehlermeldung hin. Und in dieser Fehlermeldung kann
man (von einigen Ausnahmen abgesehen) erkennen, WAS hier das Problem
ist. Also lies die Fehlermeldung! Dazu ist sie da.
Und nein. Die Fehlermeldung lautet nicht "Build failed with 2 errors".
Das ist nur die Zusammenfassung. Die Fehlermeldung steht irgendewo
drüber im Ausgabefenster. Da musst du halt mal ein wenig im
Ausgabefenster hochscrollen, um sie zu sehen. (bzw, im 6-er AVR-Studio
gibt es einen eigenen Reiter für die Fehlermeldungen im Ausgabefenster)
Hallo, Danke für die schnelle Antwort!
Karl Heinz Buchegger schrieb:> verdammt noch mal!> Der Compiler schreibtr nicht einfach hin: Ich mach nicht mehr mit.
Ist schon klar, dass Compiler keine Seele haben ;O)
Die Frage ist ja auch, WARUM der Compiler das "plötzlich" nicht mehr
mitmacht.
> Gut. Dann ist deine Definition: Du willst alles bis auf die dezimale> Einerstelle des Ergebnisses haben. Das kann hier aber keiner riechen,> wenn du es nicht dazuschreibst.
Ja genau, darum geht es.
Wollte das Posting nicht überladen und hatte die Info deshalb
weggelassen. Sorry, wenn das zu Verwirrung geführt hat.
Also, die beiden Fehlermeldungen nach "Build":
o c:/programme/atmel/.../avr/bin/ld.exe: main.elf section '.text' will
not fit in region 'text'
o c:/programme/atmel/.../avr/bin/ld.exe: region 'text' owerflowed by 78
bytes
Karl Heinz Buchegger schrieb:> Die Fehlermeldung steht irgendewo> drüber im Ausgabefenster.
Und falls du die vor lauter Warnungen nicht siehst, beseitige die Gründe
für die Warnungen gleich mit.
Auch Warnungen muss man ernst nehmen.
gerri schrieb:> Also, die beiden Fehlermeldungen nach "Build":>> o c:/programme/atmel/.../avr/bin/ld.exe: main.elf section '.text' will> not fit in region 'text'>> o c:/programme/atmel/.../avr/bin/ld.exe: region 'text' owerflowed by 78> bytes
Dein Programm ist in Summe zu groß geworden.
Zeig doch mal dein Programm. Ich kann mir nicht vorstellen, dass bzw.
wie du als Anfänger einen Mega in 0 komma Nix vollmachst. Welchen µC
verwendest du überhaupt?
Den Optimizer hast du aber schon eingeschaltet?
Und PS: wozu muss man auf einem µC in dieser Leistungsklasse bis
999.999.999 zählen? Was wird da gezählt?
Karl Heinz Buchegger schrieb:> Dein Programm ist in Summe zu groß geworden.
TäTäää, ok ;O)
Habe einen M88 mit LCD.
Dann ist also der Controller (bzw. sein flash) zu klein!?!
gerri schrieb:> Karl Heinz Buchegger schrieb:>> Dein Programm ist in Summe zu groß geworden.>> TäTäää, ok ;O)>> Habe einen M88 mit LCD.>> Dann ist also der Controller (bzw. sein flash) zu klein!?!
Dem gehts wie mir.
Ich bin auch nicht zu dick. Ich bin zu klein für mein Gewicht.
Da du die Frage nach dem Code bzw. ob der Optimizer ständig ignorierst,
bin ich raus. Das ist mir nänlich zu blöd dauernd im Dunkeln stochern zu
müssen und nur auf Raten angewiesen zu sein.
gerri schrieb:> Dann ist also der Controller (bzw. sein flash) zu klein!?!
Erst, wenn du schon alle Optimierungsmöglichkeiten ausgereizt hast.
Dazu müssten wir aber alles sehen, nicht nur 3 Zeilen.
Wenn du tatsächlich 64-bit-Arithmetik drin hast, dann solltest du
unbedingt eine aktuelle Compilerversion (4.7.2) benutzen; in den
älteren Compilern waren die 64-bit-Routinen lieblos automatisch
generiert und damit drastisch aufgeblähter als bspw. avr-libc's
handoptimierte Gleitkommaroutinen.
Karl Heinz Buchegger schrieb:> Da du die Frage nach dem Code bzw. ob der Optimizer ständig ignorierst,> bin ich raus. Das ist mir nänlich zu blöd dauernd im Dunkeln stochern zu> müssen und nur auf Raten angewiesen zu sein.
Sorry, wollte dich nicht verprellen, die Frage ist irgendwie
untergegangen.
Als Optimizer ist "-Os" oder so ähnlich eingestellt (bin grade nicht an
dem betreffenden Rechner).
>Das ist mir nänlich zu blöd dauernd im Dunkeln stochern zu müssen
Kann ich voll verstehen, noch mal sorry.
Jörg Wunsch schrieb:> Compilerversion (4.7.2)
Müßte hinkommen.
Ansonsten hatte ich einfach den Controller gewechselt (auf M168), jetzt
funktioniert alles bestens.
Das mit der Optimierung würde ich trotzdem gerne noch testen/prüfen. Ist
die Einstellung "-Os" für den besprochenen Zweck ok?
Karl Heinz Buchegger schrieb:> Zeig doch mal dein Programm. Ich kann mir nicht vorstellen, dass bzw.> wie du als Anfänger einen Mega in 0 komma Nix vollmachst.
Also in Anbetracht dessen, daß er mit float und uint64_t um sich wirft,
kann ich mir das sehr gut vorstellen.