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 | |
2 | |
3 | |
4 | |
5 | |
|
Anzeige
|
float auf Gleichheit testen erlaubt?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
Gast
#4870837
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: >
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. Peter D. schrieb: > ist das Undefined behavior? Das auf keinen Fall. D.h. beim AVR-GCC funktioniert es, weil dort float und double identisch sind. Mit 1.0f wäre es dann Compiler unabhängig. 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
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:
Der Wert von EPSILON ist abhängig davon, was Du bei deiner Anwendung noch als "gleich genug" betrachten möchtest. Keine absoluten Epsilonvergleiche (s.a. das Paper was ich oben schon verlinkt habe). Hier nochmal die kurze Version: http://floating-point-gui.de/errors/comparison/ Oder hier: http://en.cppreference.com/w/cpp/types/numeric_limits/epsilon
Gast
#4871071
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:
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. 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 :-)
Gast
#4871274
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. ;-)
Gast
#4871588
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) @ 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.
Gast
#4873296
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. float x = 1.0;
float y = 1.0;
// ... some code
if( x == y ){
// ... some code
}
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...
...machen. Wobei "1.0" wie im Eingangsposting schon steht nur ein Beispiel ist.
Gast
#4875081
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
Gast
#4877110
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 schreibenBitte melde dich an, um einen Beitrag zu schreiben. |
Anzeige
|