Forum: Compiler & IDEs problem mit kommazahlen


von avrnoob (Gast)


Lesenswert?

hallo,
ich möchte gerne messdaten auf einem 2x16 zeilen display anzeigen.da ich 
die daten seriell zum display übertrage muss ich jede einzelne ziffer 
einer zahl einzeln übertragen.nun mein problem: ich muss die messdaten 
in einzelne ziffern zerlegen. meine idee:

uint8_t Tausender=0;
uint8_t Hunderter=0;
uint8_t Zehner=0;
uint8_t Einer=0;
uint8_t Zehntel=0;
uint8_t Hundertstel=0;
float Zahl=1013.25;
uint8_t i;

for(i=0;i<4;i++){

while(Zahl!=0){
if(Zahl>=1000){Zahl-=1000;Tausender++;}
else if(Zahl>=100){Zahl-=100;Hunderter++;}
else if(Zahl>=10){Zahl-=10;Zehner++;}
else if(Zahl>=1){Zahl-=1;Einer++;}
else if(Zahl>=0.1){Zahl-=0.1;Zehntel++;}
else if(Zahl>=0.01){Zahl-=0.01;Hundertstel++;}
}}


das problem ist jetzt das ich nur 1013,00 übertragen kann da Zehntel und 
Hundertstel gleich null bleiben und die while unterbrochen wird wenn 
Zahl anscheinen 0.25 ist .
wäre über hilfe dankbar :)

von Johannes M. (johnny-m)


Lesenswert?

Ich weiß jetzt nicht, wo Dein Gleitkommawert herkommt, aber mit 
Festkommaarithmetik lassen sich Gleitkommazahlen oft ganz vermeiden 
und so Rechenzeit und Speicher sparen...

Abgesehen davon gibt es zur Umwandlung von Zahlenwerten in ASCII-Strings 
fertige Funktionen in der stdlib.h. Wenns unbedingt ein Gleitkommawert 
sein soll, dann schau Dir die Funktion dtostrf mal an.

von schulzki (Gast)


Lesenswert?

Bin mir da jetzt nicht ganz sicher, ob hier
>while(Zahl!=0)
nur ein int-Wert geprüft wird.
Evtl. hilft: while(Zahl!=0.0)

von Johannes M. (johnny-m)


Lesenswert?

schulzki wrote:
> Bin mir da jetzt nicht ganz sicher, ob hier
>>while(Zahl!=0)
> nur ein int-Wert geprüft wird.
> Evtl. hilft: while(Zahl!=0.0)
Naja, das "!=0" oder wie auch immer kann man in C sowieso getrost 
weglassen.
1
while(Zahl)
2
{}
tut's auch.

von Helmi (Gast)


Lesenswert?

Aufpassen bei Floatwerten

Man darf Floatwerte aufgrund der internen Zahlendarstellung nicht auf 0 
testen sondern auf nur auf einen kleinen wert. Sonst kann es passieren 
wenn man z.B. fortlaufend von einer Zahl einen kleinen Betrag abzieht 
das Ergebnis nie 0 wird . Aufgrund der internen Flieskomma darstellung 
im IEEE Format kann bei einer Berechnung keine 0 entstehen. Die 0 ist im 
IEEE Format eine Sonderzahl.

Besser ist eine Abfrage wie

#define FMIN 0.0001     /* kleinster wert der als 0 akzeptiert wird */
                        /* 0.0001 nur als beispiel hier */

if( fabs(zahl) < FMIN)
{
   tu was
}

Gruss Helmi

von Karl H. (kbuchegg)


Lesenswert?

Das Problem ist der float an sich.
Warum?

Ein float wird mit 4 Byte implementiert. Das
bedeutet, dass du in etwa mit 6 signifikanten Stellen
rechnen kannst. Bei einer Zahl 1013.25 hast du bereits
4 Stellen für den Vorkommaanteil verbraucht, die beiden
restlichen Stellen gehen für die beiden nachkommastellen
drauf.

Nur: Dann darfst du nicht mit der Zahl rechnen. In dem
Momement, in dem du etwas Arithmetik betreibst schrumpfen
die 6 Stellen ganz schnell zu 5 Stellen und bei weiteren
Berechnungen noch sehr viel schneller zu 4 Stellen. Von
den ursprünglichen 6 signifikanten Stellen sind nur noch
4 übrig geblieben, alles andere danch ist nur noch gelogen (*)

Nun hast du aber eine Menge Berechnungen. Selbst wenn du
ursprünglich mit der Zahl 1013.1 gestartet wärest, würde da
nach dem Abziehen der ganzzahligen Anteile keine 0.1 heraus-
kommen. Durch die ganzen Zwischenberechnungen hast du sukzessive
immer mehr Genauigkeit verloren.

Ein etwas vertrauteres Beispiel:
Du kennst doch periodische Zahlen. Ein Beispiel: 1/3
Angenommen ich lasse dir 6 signifikante Ziffern zu.
Wenn du dann 1/3 berechnen sollst, dann kommt da 0.333333
heraus. Aber so sehr du dich auch windest, 0.333333 * 3
wird nicht mehr 1.0 ergeben. Des Ergebnis 0.999999 ist zwar
sehr nahe an 1.0 aber eben nicht exakt 1.0
Wenn ich da jeztt weiter rechne, zb 3 addieren

   0.999999 + 3.0 -> 2.99999

(warum nur 5 Nachkommastellen? Zähle die signifikanten Stellen,
es sind immer noch 6, aber für die eine jetzt signifikante
Vorkommastelle fällt eine Nachkommastelle weg).

und das wiederrum durch 3 dividieren

 2.99999 / 3

dann kommt als Ergebnis 0.999996 heraus.
Rein Mathematisch müsste das Ergebnis immer noch 1.0 sein, aber
Floating Point Berechnungen haben nun mal mit Mathematik sehr
wenig zu tun.

Man sieht aber auch, wie sich der Fehler, je nach tatsächlichen
Zahlen und Operationen vergrößert.

Soweit zum Dezimalsystem. Im Binärsystem, in dem deine Floating
Point Operationen ablaufen ist das im Prinzip nicht anders. Eher
sogar schlimmer, weil es dort sehr viel mehr periodische Zahlen
gibt. Z.b. ist 0.1, also 1/10, in diesem Zahlensystem nicht
exakt darstellbar, da es eine periodische Zahl ist.


Konklusio:
Vermeide float wenn immer du kannst. Wenns gar nicht anders geht,
dann nimm sie, aber achte sehr genau darauf welche Operationen
du in welcher Reichenfolge machst und welche Zahlen da beteilgt
sind. Leider gibt es da kein Patentrezept, sondern jeder einzelne
Fall muss seperat für sich untersucht werden. Und solche Untersuchungen
können einen in den Wahnsinn treiben.

Rechnen mit Gleitkommazahlen
ist wie das Umschaufeln eines Sandhaufens.
Je öfter man es macht,
desto mehr Sand verliert man und
desto mehr Dreck mischt man in den Sand.



(*) gelogen ist ein hartes Wort. Natürlich kann man erklären
wie die weiteren Nachkommastellen zustande kommen, aber da
muss man schon sehr tief in Fliesskommaverarbeitung einsteigen.

von Rainer (Gast)


Lesenswert?

Um Dir jetzt ein Lösung an die Hand zu geben, wie wäre es mit folgendem?
1
float zahl = 1023.25;
2
char puffer[8]; // 7 Ziffern + abschließende Null
3
if ( sprintf( puffer, "%04.2f", zahl) == 7 )
4
{
5
  printf("Die Ausgabe der Zahl in den Puffer war erfolgreich!\n");
6
}
7
printf("Die Zeichenkette der Zahl %f ist %s\n", zahl, puffer );

Dann kannst Du Zeichen für Zeichen aus der Zeichenkette holen, bis Du 
auf die abschließende Null triffst.

Dann erlöst Dich zwar nicht von etwaigen Problemen mit der 
Gleitkommaarithmetik, aber immerhin eine Lösung!

von Walter (Gast)


Lesenswert?

Spar dir den Ärger mit float und rechne einfach mit ganzen Zahlen:

float Zahl=1013.25;
long lzahl = Zahl*100;

tausender = lzahl/100000L;
lzahl = lzahl%100000L;
hunderter = lzahl/10000;
usw....

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.