Forum: Mikrocontroller und Digitale Elektronik Umrechenfehler bei Float nach int


von Thomas (Gast)


Lesenswert?

Hallo

ich habe ein kleines Verständnisproblem
1
zwischenwert_float = 30030;
2
zwischenwert_uint16_t = zwischenwert_float;

Am Ende ist zwischenwert_uint16_t natürlich 30030;

So jetzt meine Problemstellung.
1
zwischenwert_float = 300.30;
2
zwischenwert_float = zwischenwert_float *100; //Zwischenwert wird zu 30030
3
zwischenwert_uint16_t = zwischenwert_float;

Jetzt ist zwischenwert_uint16_t allerdings 30029.

Wieso ist das so?

von Josef (Gast)


Lesenswert?

Weil 300.30 als float wahrscheinlich nur als 300.2999999 dargestellt 
werden kann.

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

http://stackoverflow.com/questions/11128741/cast-variable-to-int-vs-round-function

Nimm Round().
1
zw_float = zw_float *100; //Zwischenwert wird zu 30029.99999999999

Das liegr daran, dass float mit Binärzahlen (und Binärpotenzen?) rechnet 
und nicht mit Dezimalzahlen.

von Tutnichtszursache (Gast)


Lesenswert?

http://msdn.microsoft.com/de-de/library/bb978923.aspx
Hat mir damals geholfen, das ganze besser zu verstehen.
Nicht erschrecken wegen C#, das Problem hat ja mit der 
Programmiersprache nichts zu tun.

von Torsten C. (torsten_c) Benutzerseite


Lesenswert?

Tutnichtszursache schrieb:
> Nicht erschrecken wegen C#

In C# gibt's dafür ja System::Decimal. In C nicht, oder?

von Tutnichtszursache (Gast)


Lesenswert?

Torsten C. schrieb:
> In C# gibt's dafür ja System::Decimal. In C nicht, oder?

Hab keine Ahnung von C#. Da die Darstellung der Möglichen 
Nachkommastellen (bzw. auch die Abschneidung von Bits) ja auch nichts 
mit der Programmiersprache zu tun hat, ist das doch auch egal. Wichtig 
für's Verständnis ist halt, dass nicht beliebig viele Bits behandelt 
werden können - dabei spielt es auch keine Rolle, ob Soft- oder 
Hard-FPU. Oder habe ich einen Denkfehler? - ist noch früh :-)

von Thomas (Gast)


Lesenswert?

Ah ok, ich verstehe wie ihr meint.

Dann wäre das hier wohl die einfachste Lösung?
1
zwischenwert_float = 300.30;
2
zwischenwert_float = round(zwischenwert_float *100); 
3
zwischenwert_uint16_t = zwischenwert_float;

von Tutnichtszursache (Gast)


Lesenswert?

Ja.
Oder bei MCUs gänzlich auf Gleitkommazahlen verzichten, geht auch: 
http://www.mikrocontroller.net/articles/Festkommaarithmetik

Solche Berechnungen können - ohne FPU - den Programmcode nämlich ganz 
schön aufblähen und langsam machen.

von Bernhard S. (b_spitzer)


Lesenswert?

Gerade die Kommazahlen mit n/10tel als Nachkomma-Stelle sind binär nie 
exakt darstellbar. Die Nachkommastellen bei Binärzahlen haben die 
Wertigkeiten 2^-1, 2^-2 usw. 1/10 (als Beispiel) ist aber 1/(2*5) und 
die Primzahl 5 im Nenner ergibt in Binärdarstellung eine periodische 
Zahl.
Wer mal ein Gefühl für die Zahlendarstellung bekommen will, kann ja mal 
unter http://www.h-schmidt.net/FloatConverter/IEEE754de.html etwas 
experimentieren.

von Stefan F. (Gast)


Lesenswert?

Dieser Effekt ist sogar bei manchen Taschenrechnern zu beobachten.

Gib mal ein: 1 ÷ 3 = × 3 =

Das Ergebnis sollte wieder 1 sein, manche Taschenrechner zeigen jedoch 
0.99999999 an.

von (prx) A. K. (prx)


Lesenswert?

Bernhard Spitzer schrieb:
> Gerade die Kommazahlen mit n/10tel als Nachkomma-Stelle sind binär nie
> exakt darstellbar.

Auch bei n=5? ;-)

von m.n. (Gast)


Lesenswert?

Tutnichtszursache schrieb:
> Oder bei MCUs gänzlich auf Gleitkommazahlen verzichten, geht auch:
> http://www.mikrocontroller.net/articles/Festkommaarithmetik

Nein, nein, nein!
Wichtig ist eher, solange 'float' weiterzurechnen, bis eine gerundete 
Ausgabe notwendig wird.

> Solche Berechnungen können - ohne FPU - den Programmcode nämlich ganz
> schön aufblähen und langsam machen.

Und nochmals für allgemeine Anwendungen: Nein!

von (prx) A. K. (prx)


Lesenswert?

m.n. schrieb:
>> Solche Berechnungen können - ohne FPU - den Programmcode nämlich ganz
>> schön aufblähen und langsam machen.
>
> Und nochmals für allgemeine Anwendungen: Nein!

Das kriegst du ebenso wenig wieder aus der Welt geschafft wie die 
beliebte VCC Verbindung an AREF. ;-)

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Thomas schrieb:
> Ah ok, ich verstehe wie ihr meint.
>
> Dann wäre das hier wohl die einfachste Lösung?
>
>
1
> zwischenwert_float = 300.30;
2
> zwischenwert_float = round(zwischenwert_float *100);
3
> zwischenwert_uint16_t = zwischenwert_float;
4
>

Das hilft dir nichts, wenn du das Ergebnis vom round dann wieder an 
einen float zuweist.

Der entscheidende Punkt ist, dass du bei der Umwandlung in einen int 
runden musst. Damit aus 30029.99999999 dann eben 30030 wird und nicht 
durch abschneiden der Kommastellen 30029.

Denn was machst du bei
1
  zwischenwert_float = 300.304;
2
  zwischenwert_float = round(zwischenwert_float *100);

Das Ergebnis sollte eigentlich 30030.4 sein. Durch dein Runden wird es 
aber zu 30030.0. Wenn du mit dieser float Zahl noch weiterrechnen muss, 
dann ist das ein signifikanter Fehler.

-> Die Rundung willst du soweit nach hinten schieben wie nur möglich. In 
deinem Fall erst bei der Umwandlung nach int.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Karl Heinz schrieb:
> Das hilft dir nichts, wenn du das Ergebnis vom round dann wieder an
> einen float zuweist.

Ganzzahlen sind als Fliesskommawert exakt darstellbar, so lange die 
Stellenzahl der Mantisse ausreicht. Es kommt also darauf an, was man 
erreichen will. Wenn die Multiplikation mit 100 den Sinn hat, 2 
Nachkommastellen darzustellen, dann geht das.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

A. K. schrieb:
> Karl Heinz schrieb:
>> Das hilft dir nichts, wenn du das Ergebnis vom round dann wieder an
>> einen float zuweist.
>
> Doch, denn Ganzzahlen sind als Fliesskommawert exakt darstellbar, so
> lange die Stellenzahl der Mantisse ausreicht.

Ich hab meinen Post noch  mal geändert, weil ich eine schwache 
Begründung hatte.

von Tutnichtszursache (Gast)


Lesenswert?

m.n. schrieb:
> Nein, nein, nein!
> Wichtig ist eher, solange 'float' weiterzurechnen, bis eine gerundete
> Ausgabe notwendig wird.

Hi!
Ich habe das anders gelernt. Sobald eben keine FPU vorhanden ist, muss 
das Berechnen von Gleitkommazahlen ja softwareseitig umgesetzt werden. 
Ich dachte immer, das sei sehr rechenintensiv und benötigt viel 
Speicher. Werde heute Abend mal ein paar Tests machen (float vs 
hochgerechnete Integer) und mir selber ein Bild davon machen. Danke für 
den Hinweis.


Dass die Ausgabe eines floats durch zusätzliche Bibliotheken den 
Speicher dann noch zusätzlich aufbläht, ist ja klar.

von (prx) A. K. (prx)


Lesenswert?

Tutnichtszursache schrieb:
> Ich habe das anders gelernt. Sobald eben keine FPU vorhanden ist, muss
> das Berechnen von Gleitkommazahlen ja softwareseitig umgesetzt werden.
> Ich dachte immer, das sei sehr rechenintensiv und benötigt viel
> Speicher.

Wobei "viel" bei einem Zwerg mit 4KB ROM anders zu bewerten ist als bei 
einem normalen Controller mit 64KB ROM.

Teuer ist printf und scanf mit Fliesskommaunterstützung, die man aber 
leichter vermeiden kann als komplette bereits in Fliesskommaformeln 
vorhandene Rechnungen. Die recht gut optimierten Laufzeitfunktionen 
hingegen sind bei AVRs nicht so teuer.

Die Gegenrechnung zum etwas höheren Laufzeitaufwand gegenüber 
Festkommaarithmetik ist der Zusatzaufwand bei der Programmierung. 
Folglich ist es eine Einzelfallentscheidung, auf welche Art man rechnet. 
Von einer ehernen Regel, Fliesskommarechnung sei "bäh" wenn keine FPU 
vorhanden ist, halte ich nichts.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Tutnichtszursache schrieb:
> m.n. schrieb:
>> Nein, nein, nein!
>> Wichtig ist eher, solange 'float' weiterzurechnen, bis eine gerundete
>> Ausgabe notwendig wird.
>
> Hi!
> Ich habe das anders gelernt. Sobald eben keine FPU vorhanden ist, muss
> das Berechnen von Gleitkommazahlen ja softwareseitig umgesetzt werden.
> Ich dachte immer, das sei sehr rechenintensiv


definiere 'sehr rechenintensiv'.

Wenn die Aufgabe darin besteht, alle 2 Sekunden den Messwert eines 
DS18B20 als Temperatur auf einem LCD darzustellen und sonst nichts, dann 
hat der µC alle Zeit der Welt, dies zu tun. Es ist überhaupt kein 
Problem das in float zu rechnen. Selbst wenn du die Temperatur 10 mal 
pro Sekunde benötigen würdest, wäre das noch rein von der Berechnung her 
kein Problem.

> und benötigt viel
> Speicher.

Für nicht benutzten Speicher gibt es kein Geld zurück.
generell ist die Zeit des Programmierers teurer als der nächst größere 
µC. Für das Geld, dass 8K Flash mehr kosten, kann ich gerade mal ein 
paar Sekunden arbeiten.

-> hüte dich vor verallgemeinerten Aussagen. Es gibt nur sehr wenige 
Regeln in der Informatik, die wirklich universell gültig sind. Die 
meisten sind aufzufassen als: "Im Prinzip: ja. Es gibt aber auch 
Ausnahmen. 'One size fits all' funktioniert bei Baseballkappen, aber 
nicht in der Softwareentwicklung.". Was nicht heisst, dass man sich an 
gar keine Regeln halten soll. Die meisten Regeln haben schon ihren Sinn. 
Sie sind aber mehr als Richtlinien zu verstehen. Wenn du aber einen Satz 
von Regeln haben willst, an die du dich nur dogmatisch halten musst und 
alles ist gut, dann bist du bei Religionen besser aufgehoben.

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

Karl Heinz schrieb:
> generell ist die Zeit des Programmierers teurer als der nächst größere
> µC. Für das Geld, dass 8K Flash mehr kosten, kann ich gerade mal ein
> paar Sekunden arbeiten.

und was ist wenn mal 10.000 µC programmiert?

von (prx) A. K. (prx)


Lesenswert?

Karl Heinz schrieb:
> 'One size fits all' funktioniert bei Baseballkappen,

Bei denen auch nicht. ;-)

Peter II schrieb:
> und was ist wenn mal 10.000 µC programmiert?

Deshalb ja Einzelfallentscheidung.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Peter II schrieb:
> Karl Heinz schrieb:
>> generell ist die Zeit des Programmierers teurer als der nächst größere
>> µC. Für das Geld, dass 8K Flash mehr kosten, kann ich gerade mal ein
>> paar Sekunden arbeiten.
>
> und was ist wenn mal 10.000 µC programmiert?

10-tausend Sekunden sind auch erst knapp unter 3 Stunden.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:
> Peter II schrieb:
>> Karl Heinz schrieb:
>>> generell ist die Zeit des Programmierers teurer als der nächst größere
>>> µC. Für das Geld, dass 8K Flash mehr kosten, kann ich gerade mal ein
>>> paar Sekunden arbeiten.
>>
>> und was ist wenn mal 10.000 µC programmiert?
>
> 10-tausend Sekunden sind auch erst knapp unter 3 Stunden.

Ich hab mal einen 3D-Hidden Line Algorithmus auf einem PC-AT 
geschrieben. Das war so um 1987/88 rum. Um auf Speed zu kommen, hab ich 
ihn in Integer mit Festkommaarithmetik geschrieben. In Summe bin ich da 
gut und gerne ein paar Monate verteilt auf ein 3/4 Jahr gesessen, bis 
ich die Fix-Komma Konstanten soweit angepasst hatte, dass ich den 
Kompromiss aus Auflösung und möglichst kleinem Datentyp abschnittweise 
soweit im Griff hatte, dass er zufriedenstellend funktioniert hat. Klar, 
bei einem 386 war das notwendig. Integer haben die Floating Point 
Rechnungen ganz klar abgehängt und es war damals wichtig, dass das 
schnell lief. FPUs waren damals noch nicht verbreitet.

Ein paar Jahre später kamen die 486 mit eingebauter FPU auf den Markt. 
Das ganze neu geschrieben, diesmal mit double und nach ein paar Tagen 
lief es problemlos.

: Bearbeitet durch User
von Tutnichtszursache (Gast)


Lesenswert?

Ok, ist voll und ganz verständlich, was ihr meint :-)
An die Optimierung der Arbeitszeit hab ich dabei wirklich nicht gedacht. 
Da ich persönlich oft und gerne mit kleineren Mikrocontrollern arbeite, 
liegt darin vermutlich der Grund zur Speicheroptimierung. Aber klar, ihr 
habt vollkommen recht, dass die ganzen Umrechnungen ebenfalls zu mehr 
Speicherverbrauch führen.

Wie gesagt, ich werde es heute Abend einmal genauer austesten und in 
Zukunft dann wohl doch nicht mehr so float-feindlich sein. Kommt 
natürlich auch darauf an, wie oft man im Code dann Berechnungen 
durchführt.
Danke für eure Erklärungen bzw. Hinweise!

von Karl H. (kbuchegg)


Lesenswert?

Tutnichtszursache schrieb:

> wohl doch nicht mehr so float-feindlich sein. Kommt

Wobei man auch sagen muss, dass Floating Point auch nicht die Lösung 
aller Probleme ist. Sie hat auch ihre Tücken. Vor allen Dingen dann, 
wenn Fehlerfortpflanzung über viele Berechnungen hin ins Spiel kommt, 
wie auch aus leidvoller und peinlicher Erfahrung lernen musste.

: Bearbeitet durch User
von m.n. (Gast)


Lesenswert?

A. K. schrieb:
> m.n. schrieb:
>>> Solche Berechnungen können - ohne FPU - den Programmcode nämlich ganz
>>> schön aufblähen und langsam machen.
>>
>> Und nochmals für allgemeine Anwendungen: Nein!
>
> Das kriegst du ebenso wenig wieder aus der Welt geschafft wie die
> beliebte VCC Verbindung an AREF. ;-)

Aber ab und zu kann eine Seele gerettet werden ;-)

Tutnichtszursache schrieb:
> Werde heute Abend mal ein paar Tests machen (float vs
> hochgerechnete Integer) und mir selber ein Bild davon machen.

Das ist sehr löblich!
Zu AVRs: als diese noch keine MUL-Befehle hatten, waren die 
Multiplikation für int32 und float fast gleich. Bei float müssen nur 24 
Bit Mantisse gerechnet werden, dafür braucht die Anpassung des 
Exponenten ein paar Takte zusätzlich.
Bei Division gibt es keine DIV-Befehle, sodaß hier die Ausführungszeiten 
ebenfalls fast gleich sind.

> Dass die Ausgabe eines floats durch zusätzliche Bibliotheken den
> Speicher dann noch zusätzlich aufbläht, ist ja klar.

Beim AVR-GCC mit libc.a werden rund 1K benötigt. Sobald der µC >= 4K 
Flash hat, geht float schon mühelos. Aktuell tippe ich ein Programm für 
ATtiny44 zur PT1000-Auswertung und Anzeige auf einem 4-stell. LCD. 
Codegröße inkl. float-Routinen: 2282 und 26 Byte RAM.
Kein Grund auf float zu verzichten.

Noch etwas: wenn man Integer 10/3 rechnet und dann mit 3 multipliziert 
erhält man als Ergebnis 9. Das wundert Niemanden. Erst wenn solche 
Sachen bei float passieren ruft die 'Fachwelt': Verrat!

von (prx) A. K. (prx)


Lesenswert?

m.n. schrieb:
> Zu AVRs: als diese noch keine MUL-Befehle hatten, waren die
> Multiplikation für int32 und float fast gleich.

Teurer sind add/sub aufgrund der abhängig von der Exponentendifferenz 
notwendigen Mantissenverschiebung. Die entfällt bei Festkomma und ist 
bei AVRs recht aufwendig.

: Bearbeitet durch User
von m.n. (Gast)


Lesenswert?

A. K. schrieb:
> Teurer sind add/sub aufgrund der abhängig von der Exponentendifferenz
> notwendigen Mantissenverschiebung.

Ich steh auf Luxus ;-)

von Nilix (Gast)


Lesenswert?

Normalerweise so:

zwischenwert_float = 300.30;
zwischenwert_float = round(zwischenwert_float *100.0);
zwischenwert_uint16_t = zwischenwert_float;

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.