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:
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
Hallo Peter,
Was passiert, wenn der Wert nicht als float darstellbar ist?
Evtl. Runden oder mit einem epsilon testen.
Ist zwar keine Antwort auf deine Frage, ...
Adib.
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
>floatx=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.
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.
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.
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
floatval1,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.
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.
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.
Peter D. schrieb:> Nö, sondern vom Vergleichswert.
ich ging davon aus, daß der TE eine einfache Vergleichsmöglichkeit in
seiner Anwendung haben wollte und keine allgemeingültige, mathematisch
korrekte (für viele praktische Anwendungsfälle aber "overengineerte")
Library schreiben möchte.
Vielleicht hab' ich mich ja auch getäuscht.
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.
Eric B. schrieb:> Peter D. schrieb:>> if((x < (INIT_VAL * (1.0 + TOLERANCE / 100.0)))>> && (x > (INIT_VAL * (1.0 - TOLERANCE / 100.0)))){>> (x < BLAH) && (x > BLAH) ? Das wird auf jedem Fall immer FALSE liefern> :-)
1.0 + TOLERANCE wird für jeden Fall, in dem TOLERANCE != 0 ist, auf
jeden Fall einen anderen Wert liefern als 1.0 - TOLERANCE. ;-)
@ Amateur (Gast)
>Viele, für das "Auge" gerade aussehende Zahlen, sind in der float- bzw.>double-Darstellung, krumm und schief.
Was eben daran liegt, daß "das Auge" meist mit Dezimalzahlen zu tun hat,
floats aber binär zur Basis 2 kodiert werden.
PittyJ schrieb:> Aber ist das wirklich 1.0? Es könnte auch 0.99999999 sein. Oder> 1.00000001
Beim GCC bestimmt nicht. Float liefert günstigstenfalls 7 Stellen.
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.
wieso überhaupt auf Gleichheit testen?
Gerade bei float müsste sich doch ein Wertebereich besser machen
>99,999% && < 100,001%
wir wissen doch das es nie einschliesslich bis auf die letzte
Dezimalstelle gleich wird, oder man muss gleich Ganzzahlrechnung machen.
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
externfloatinit_x;
2
// ...
3
floatinit_x=1.0;
4
// ...
5
floatx=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.
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.
Zu was gibt es Wahrheitswerte true , false?
Wenn einer eine Null ist, ist er es in integer, long oder float.
Es ist möglich, allesmögliche auf Gleichheit zu prüfen. Es ist eben
gleich, wenn es gleich ist. Aber es ist nicht einerlei.
guest schrieb:> Eric B. schrieb:>>> if((x < (INIT_VAL * (1.0 + TOLERANCE / 100.0)))>>> && (x > (INIT_VAL * (1.0 - TOLERANCE / 100.0)))){>>>> (x < BLAH) && (x > BLAH) ? Das wird auf jedem Fall immer FALSE liefern>> :-)>> schon richtig nur steht hier:>> (x < BLAH) && (x > BLUB)
Ah, richtig. Ich hatte da wohl 'ne halbe Tomate auf den Augen :-S
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.