float auf Gleichheit testen erlaubt?

#4870832
Lesenswert?

Das Problem ist, dass 1.0 eine Konstante vom Typ double ist. Diese wird 
bei der Zuweisung nach x zu float konvertiert. Beim Vergleich auf 
Gleichheit wird x wieder zu double konvertiert, dabei kann aber die bei 
der Konvertierung zu float verlorene Genauigkeit nicht zurückgewonnen 
werden. Nutze 1.0f und der Vergleich wird klappen.

Siehe auch hier:
http://stackoverflow.com/a/9850710
#4870841
Lesenswert?

Peter D. schrieb:
> Ich will ein float auf Gleichheit mit dem Initialisierungswert testen.
> In AVR-GCC funktioniert es, aber ist das immer so oder ist das Undefined
> behavior?
>
> Hier mal ein Beispiel:
>
1
> float x = 1.0;
2
> // ... some code
3
>   if( x == 1.0 ){
4
>     // ... some code
5
>   }
6
>

Das ist eine lange Geschichte:

https://randomascii.wordpress.com/2012/06/26/doubles-are-not-floats-so-dont-compare-them/

In diesem Beispiel ist in x (float) der gewandelte Wert von 1.0 
(double). In (x == 1.0) wird x wieder in ein double impilizit gewandelt. 
Solange bei der ersten Wandlung kein Fehlrepräsentation stattgefunden 
hat, ergibt der ==-Operator "true".

Wird aber im ersten "some code" etwa berechnet, dann sollte man einen 
epsilon-Vergleich machen.
#4871005
Lesenswert?

Be S. schrieb:
> Das Problem ist, dass 1.0 eine Konstante vom Typ double ist. Diese wird
> bei der Zuweisung nach x zu float konvertiert. Beim Vergleich auf
> Gleichheit wird x wieder zu double konvertiert, dabei kann aber die bei
> der Konvertierung zu float verlorene Genauigkeit nicht zurückgewonnen
> werden.

Naja, was soll bei 1.0 an Genauigkeit verloren gehen? Im verlinkten 
Beitrag wird 0.7 verwendet, wo das von dir gesagte gilt. Aber 1.0 ist in 
float und double exakt darstellbar, so dass bei der Konvertierung nichts 
verloren geht. Man muss sich aber bewusst sein, dass das nur für 
bestimmte Werte gilt und nicht allgemeingültig ist.
Gast #4871012
Lesenswert?

Ich würde == Tests mit Floats immer vermeiden.

nimm einfach mal
x = 0.4 + 0.6;
Das sollte ja x == 1.0 ergeben.

Aber ist das wirklich 1.0? Es könnte auch 0.99999999 sein. Oder 
1.00000001

Ich verzeichte auf solche Vergleiche und bin damit immer gut gefahren.
#4871042
Lesenswert?

Bei Fließkommazahlen (egal ob float oder double) kommt es in jeder 
nicht-trivialen Anwendung zu Rundungs- oder Darstellungsfehlern. Ein 
simpler Vergleich auf "absolute" Gleichheit wird also 
höchstwahrscheinlich öfter fehlschlagen, als dir lieb ist.

Besser so:
1
#define EPSILON 0.0001
2

3
float val1, val2;
4

5
...
6

7

8
if (abs(val1 - val2) < EPSILON)
9
{
10
    /*
11
     * "so gut wie"-gleich
12
     */
13
    ...
14
}
15
else
16
{
17
    /*
18
     * unterschiedlich
19
     */
20
}

Der Wert von EPSILON ist abhängig davon, was Du bei deiner Anwendung 
noch als "gleich genug" betrachten möchtest.
Gast #4871071
Lesenswert?

Früher, als ich noch bei der Vermessung war, ohne Internet und ohne 
fremde Hilfe, habe ich einfach die Zahlen in Zeichenketten verwandelt 
und diese verglichen. Damit lässt sich leicht die Genauigkeit festlegen.
Hat immer funktioniert.
Programmiert wurde in Basic und Turbopascal.
#4871078
Lesenswert?

Markus F. schrieb:
> Der Wert von EPSILON ist abhängig davon, was Du bei deiner Anwendung
> noch als "gleich genug" betrachten möchtest.

Nö, sondern vom Vergleichswert.
Es ist ein Unterschied, ob ich kV (1e3) oder nA (1e-9) vergleichen will.
Man müßte also auf eine prozentuale Abweichung vergleichen und das artet 
in Schreibarbeit aus, d.h. wird unleserlich:
1
#define INIT_VAL 1.0
2
#define TOLERANCE 0.1 // +/-0.1%
3
  if((x < (INIT_VAL * (1.0 + TOLERANCE / 100.0)))
4
    && (x > (INIT_VAL * (1.0 - TOLERANCE / 100.0)))){

Es handelt sich konkret um einen Skalierungsfaktor, d.h. die Variable 
wird nicht berechnet, sondern nur verschieden gesetzt.
Gast #4871274
Lesenswert?

Natürlich kann man float-Zahlen auf Gleichheit testen!

...aber

1. Die interne Darstellung beinhaltet ein paar Pferdefüße.
2. Wird nicht gerade - unsinnigerweise - mit Ganzzahlen gearbeitet, so 
ist
   es fast unmöglich, dass irgendwo z.B. 1 herauskommt.

Der gute, alte Bleistift:

1 / 3 = Oops;

gefolgt von:

if ( ( Oops * 3 ) == 1 )

schlägt fast immer fehl:-)

Deshalb sollte man möglichst immer einen Bereich abfragen. Auch die 
Größe des Bereiches ist oft einen zweiten Blick wert.

Noch was zu 1.
Viele, für das "Auge" gerade aussehende Zahlen, sind in der float- bzw. 
double-Darstellung, krumm und schief.
Moderator Persönliche Seite #4875030
Lesenswert?

PittyJ schrieb:
> Ich würde == Tests mit Floats immer vermeiden.

Mit einer Ausnahme: der Wert 0.0, wenn er durch Zuweisung oder
Initialisierung (auch impliziter) entstand (also nicht durch
eine Berechnung) ist garantiert immer exakt darstellbar und kann
daher auch auf Gleichheit getestet werden.

Die 1.0 ist in den heutzutage üblichen Zahlendarstellungen auch
exakt darstellbar, aber das ist m. E. nicht garantiert.
#4875074
Lesenswert?

Peter D. schrieb:
> Ich will ein float auf Gleichheit mit dem Initialisierungswert testen.

Edit:

Falls man Angst vor dem Literal hat und unterschiedliche Compiler 
verwendet, dann kann man...
1
extern float init_x ;
2
// ...
3
float init_x = 1.0 ;
4
// ...
5
float x = init_x ;
6
// ... some code
7
  if( x == init_x ){
8
    // ... some code
9
  }
...machen. Wobei "1.0" wie im Eingangsposting schon steht nur ein 
Beispiel ist.
Gast #4875081
Lesenswert?

Man könnte die Werte auch so wählen, dass keine Nachkommastellen 
vorkommen. Die 1.0 mit 10 (oder 100, je nach gewünschter Genauigkeit) 
multiplizieren und dann mit int-Werten arbeiten. Vor der Ausgabe kann 
man dann ja einen cast zu double machen und durch 10.0 teilen.
Gast #4877110
Lesenswert?

Peter D. schrieb:
> Ich will ein float auf Gleichheit mit dem Initialisierungswert testen.

In diesem speziellen Fall kannst du das brutal binär machen, also alle 4 
Bytes auf Gleichheit testen. Braucht keinerlei echte GK-Arithmetik, gilt 
aber nur für den Fall, daß du tatsächlich einen float im RAM mit 
dessen eigenem Ursprung im Flash oder EEPROM vergleichst, also exakt mit 
den 4 Bytes, die du zuvor in den RAM geladen hast.

W.S.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren