Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (ANSI-Standard) » Problem mit float Variablen

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 <
000
07.09.2008, 22:00 Uhr
~Mathias
Gast


Hallo,

ich habe einen Variable vom Typ float diese ist auf -1.0 initialisiert.
nun erhöhe ich diese schrittweise um 0.1 man sollte nun ja denken das nach
-1.0 -> -0.9 -0.8 kommt das ganze stimmt aber nicht ab und zu bekomme ich Werte mit viel zu vielen Nachkommastellen.

Hier ma ein kleines Testprogramm:

#include <iostream>

using namespace std;

int main(){

for (float i = -1.0f; i <= 1.0f; i += 0.1f){
cout << "Hallo ich bin: " << i << endl;
}

return 0;
}

Consolen Ausgabe:

Hallo ich bin: -1
Hallo ich bin: -0.9
Hallo ich bin: -0.8
Hallo ich bin: -0.7
Hallo ich bin: -0.6
Hallo ich bin: -0.5
Hallo ich bin: -0.4
Hallo ich bin: -0.3
Hallo ich bin: -0.2
Hallo ich bin: -0.0999999 <----- ???
Hallo ich bin: 7.45058e-08 <---- ???
Hallo ich bin: 0.1
Hallo ich bin: 0.2
Hallo ich bin: 0.3
Hallo ich bin: 0.4
Hallo ich bin: 0.5
Hallo ich bin: 0.6
Hallo ich bin: 0.7
Hallo ich bin: 0.8
Hallo ich bin: 0.9


Vielen Dank

Mathias
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
07.09.2008, 22:53 Uhr
Lensflare



Hab gerade versucht den Thread zu finden, in dem eine ähnliche Frage gestellt wurde.
Leider nicht gefunden.

Es ging darum dass 12.0-12.0 nicht exakt 0.0 ergeben hat.

Das liegt einfach daran, dass Fließkomma Datentypen so Aufgebaut sind, dass sie eine Genauigkeit von einer bestimmten Anzahl an Stellen bieten.
Wenn man mit ihnen rechnet, dann kann es zu Ungenauigkeiten kommen.
Ist also völlig normal.
--
Wenn das Gehirn so einfach wäre, dass wir es verstehen könnten, wären wir so einfach, dass wir es nicht verstehen könnten.
(Emerson Pugh Trost)

Dieser Post wurde am 07.09.2008 um 22:58 Uhr von Lensflare editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
07.09.2008, 22:58 Uhr
Hans
Library Walker
(Operator)


Hi,

das Problem, das Du da hast, hat damit zu tun, das sich gebrochene Zahlen im Rechner nicht exakt darstellen lassen. Das gilt für float, aber auch für double und long double. An diesen Stellen:
Code:
Hallo ich bin: -0.0999999 <----- ???
Hallo ich bin: 7.45058e-08 <---- ???

schlagen die Rundungsfehler voll zu, die bei diesen Datentypen intern immer auftreten. Wenn Du -0.0999999 auf eine Nachkommastelle aufrundest, kommt -0.1 heraus, - der Wert liegt also noch im Rahmen dessen, was als korrekt durchgehen kann.

7.45058e-08 steht für 7.45058 mal 10 hoch -08.
Hier kommt jetzt die Genauigkeit ins Spiel, mit der Zahlen dargestellt werden können. Bei float sind das rund 7 Stellen. xxxxxxe-8 bedeutet, dass das Komma um 8 Stellen nach links zu verschieben ist, um die richtige Zahl zu erhalten. Mit jedem Schritt, den das Komma dabei verschoben wird, fallen die Stellen rechts weg, und links muss einen Null ergänzt werden. Wenn man das 8 mal macht, bleibt Null übrig. Also, von 7.45058e-08 das Komma verschieben ergibt:
7.45058e-08 ohne verschieben
0.745058e-07 nach einmal verschieben
0.074506e-06 nach 2 mal verschieben
0.007451e-05 nach 3 mal verschieben
0.000745e-04 nach 4 mal verschieben
0.000075e-03 nach 5 mal verschieben
0.000007e-02 nach 6 mal verschieben
0.000000e-01 nach 7 mal verschieben
0.000000e-00 nach 8 mal verschieben
Der Wert ist also Null, auch wenn es etwas kompliziert dargestellt ist.
Wenn das alles nicht sofort klar wird, keine Sorge, das kommt schon noch.

Hans
--
Man muss nicht alles wissen, aber man sollte wissen, wo es steht. Zum Beispiel hier: Nachdenkseiten oder Infoportal Globalisierung.

Dieser Post wurde am 07.09.2008 um 23:02 Uhr von Hans editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
07.09.2008, 23:27 Uhr
~Mathias
Gast


Hallo,

vielen Dank für eure schnellen Antworten :-)

Hmm ok ich weiß nun das das Rechnen mit den float Variablen nicht so genau ist.

Wie Löse ich denn nun folgendes Problem?

Code:

#include <iostream>

using namespace std;

int main(){

for (float i = -1.0f; i <= 1.0f; i += 0.1f){

if ( i == 0.8f ){
cout << "Ich bin die 8 und Stelle mich persönlich vor" << endl;
}else{
cout << "Hallo ich bin: " << i << endl;
}

}
return 0;
}

Ausgabe:

Hallo ich bin: -1
Hallo ich bin: -0.9
Hallo ich bin: -0.8
Hallo ich bin: -0.7
Hallo ich bin: -0.6
Hallo ich bin: -0.5
Hallo ich bin: -0.4
Hallo ich bin: -0.3
Hallo ich bin: -0.2
Hallo ich bin: -0.0999999
Hallo ich bin: 7.45058e-08
Hallo ich bin: 0.1
Hallo ich bin: 0.2
Hallo ich bin: 0.3
Hallo ich bin: 0.4
Hallo ich bin: 0.5
Hallo ich bin: 0.6
Hallo ich bin: 0.7
Hallo ich bin: 0.8
Hallo ich bin: 0.9


Ich habe nun schon irgendwo gelesen, dass cout selbst irgendwie rundet und die Ergebnisse daher so Ausschauen.

Leider hat sich meine 0.8 wie Ihr sehen könnt nicht selber vorgestellt... . Woher weiß ich denn nun welchen genauen Wert ich abfragen muss bzw. wie runde ich auf eine Kommastelle?

Vielen Dank

Mathias
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
07.09.2008, 23:55 Uhr
Hans
Library Walker
(Operator)


Hi,

nur mal ganz kurz: Gebrochene Datentypen, also float, double und long double auf Gleichheit zu testen geht fast immer schief. U.a. weil intern auch Rundungsfehler auftreten. Stattessen sollte man testen, ob sich die Zahl in einem festgelegten Bereich (Intervall) befindet. Also etwa:

C++:
if ((i>0.7999) || (i<0.8001))
   // i ist ungefähr 0.8



Das Thema mit der Genauigkeit, und wie man gebrochene Datentypen testet, ob sie in einem bestimmten hatten wir hier auch schon mal ausführlicher, deshalb mal die Suchfunktion bemühen, die sollte noch mehr dazu liefern. - Ebenso zum Thema runden und wie man die Genauigkeit bei der Ausgabe festlegt. Da war was mit cout.precesion(2), oder so ähnlich, um die Ausgabe auf zwei Stellen zu begrenzen. Damit kenn ich mich bei C++ aber nicht aus, weshalb die Experten (beefy, ao, Flosoft, etc.) mich korrigieren mögen, wenn ich dazu jetzt Unfug geschrieben habe.

Gute N8,
Hans
--
Man muss nicht alles wissen, aber man sollte wissen, wo es steht. Zum Beispiel hier: Nachdenkseiten oder Infoportal Globalisierung.

Dieser Post wurde am 08.09.2008 um 00:04 Uhr von Hans editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
005
08.09.2008, 12:55 Uhr
xXx
Devil


www.cplusplus.com/reference/iostream/ios_base/precision.html sollte zum Thema Anzeige weiterhelfen (und ja die genannte Funktion war die richtige ) Natürlich geht auch hier der dazu passende stream-manipulator: www.cplusplus.com/reference/iostream/manipulators/setprecision.html
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
006
08.09.2008, 15:13 Uhr
ao

(Operator)



Zitat von Hans:
Stattessen sollte man testen, ob sich die Zahl in einem festgelegten Bereich (Intervall) befindet. Also etwa:

C++:
if ((i>0.7999) || (i<0.8001))
   // i ist ungefähr 0.8





Etwas allgemeiner wäre das z.B. so zu lösen:

C++:
bool isequal_absolute (float x, float c, float eps)
{
    return (fabs (x - c) <= eps); // "absolute Gleichheit": Vergleich der Differenz mit eps
}

bool isequal_relative (float x, float c, float eps)
{
    return (fabs ((x - c) / c) <= eps); // "relative Gleichheit": Differenz wird vor dem Vergleich auf c normiert
}


Dabei ist
x der Wert, der auf Gleichheit getestet wird,
c der Wert, mit dem x verglichen wird
eps die maximale Abweichung, die für das Ergebnis "gleich" noch zugelassen wird.

Es ist wichtig, das Epsilon sorgfältig zu wählen und mit Bedacht zu entscheiden, ob es relativ oder absolut betrachtet werden soll - das kann großen Einfluss aufs Ergebnis haben.

Bei der ersten Implementierung (isequal_absolute) wird das Epsilon-Intervall absolut betrachtet, d.h. mit eps = 1E-3 liefert ein Vergleich von 1E-4 und 1E-5 "gleich". Argument hierfür: "Wenn ich auf Millisekunden genau messe, interessieren mich 10 oder 100 Mikrosekunden überhaupt nicht."

Bei der zweiten Implementierung (isequal_relative) ist das Intervall relativ zu sehen, d.h. mit eps = 1E-3 liefert ein Vergleich von 1E4 und 1E5 "ungleich". Argument hierfür: "Was sich um den Faktor 10 unterscheidet, kann nicht als gleich gelten."

Beides kann richtig sein - der Programmierer muss entscheiden.

Dieser Post wurde am 08.09.2008 um 15:15 Uhr von ao editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
007
08.09.2008, 20:40 Uhr
berniebutt



@ Mathias: Du hast noch einige Kenntnis- und Erfahrungslücken!
1. Gleitkommazahlen (float etc.) sind nicht ungenau, eher sehr genau.
2. Willst Du mit cout eine festgelegte Anzahl von n Nachkommastellen haben, so genügt ein
vorangestelltes << setprecision(n)
3. Die Variablen i, j, k, ... werden üblicherweise als Integer für Laufvariable und Indizes
verwendet, nicht für Gleitkommazahlen oder anderes.
4. Ein direkter Vergleich zweier Gleitkommazahlen geht immer schief, vor allem dann wenn
sie berechnet worden sind. Man fragt deshalb die Gleichheit etwa so ab:
if(fabs(zahl1-zahl2) < 1.e-5)
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 <     [ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: