N'Abend zusammen!
Ich weiß, der Titel ist behämmert, aber ich weiß nicht, wie ich es
formulieren soll. Also ich habe eine Funktion, welche eine Berechnung
durchführt und dazu eine Variable übergeben bekommt und halt das
Ergebnis zurückgibt.
Jetzt soll, wenn die übergebene Variable beispielsweise kleiner Null
ist, ein Fehler zurückgegeben werden.
1
if(variable<0.0)
2
{
3
return-1;
4
}
5
6
...Berechnung...
7
8
returnErgebnis
So, nun ist es ja so, dass ein Float meist keine wirklich gerade Zahl
ist - z.B. 9,999999999E-1 oder so.
Wenn ich jetzt den Rückgabewert der Funktion überprüfen wollen würde mit
1
if(Rueckgabewert==-1)
2
{
3
...
4
}
dann könnte das ja daneben gehen, oder nicht? Oder klappt das immer?
Sollte man stattdessen Bereiche abfragen wie 0,9 < x < 1,1?
Über eine Erleuchtung wäre ich dankbar.
Hans schrieb:> ein Fehler zurückgegeben werden
Für Fehler von Float Funktionen bietet sich der Wer NaN oder +/-INF an
(je nach gewünschter Aussage), auf welchen man auch mit entsprechenden
Funktionen prüfen kann, siehe hier:
http://www.gnu.org/s/hello/manual/libc/Infinity-and-NaN.html
Alternativ kann man bei Floats auch noch das Sign prüfen.
Ok! Danke schonmal für die Antworten! Das hilft mir sehr. Ich habe vier
Fälle von Rückgabewerten als Fehlerkennzeichnung. Alle negativen Werte
sind Fehler, ein positiver Wert oder halt auch 0 sind gültige Rückgaben.
Wenn ich also return
-1
-2
-4
-8
mache, dann brauche ich mir keine Sorgen machen?
sollte im Normalfall gehen.
Ich würde es dennoch lieber mit Vergleichen machen
if (return < -3.0)
{
} else if (return < -2.0)
{
}
else if (return < -1.0)
{
}
else if (return < -0.0)
{
}
else {
}
Johann L. schrieb:> [...]> Explizit so durch 0 zu dividieren ist zwar unvertraut, aber das wird> selbst in Bibliotheksquellen gemacht um NaNs zu erzeugen; libgcc WIMRE.
Und fuer seine vier moeglichen Fehler-Rueckgabewerte soll er dann vier
verschiedene Arten von NANs nutzen, oder wie? ;)
Und gibt's eigentlich einen Grund dafuer, dass hier jeder zweite mit
"0.0" anstelle von "0" vergleicht?
Gruss,
Volker
Moment, ich glaube nicht, dass C das Gleitkommasystem spezifiziert. Es
mag sein, dass es in Deinem Falle funktioniert, generell verlassen würde
ich mich darauf nicht.
Volker Schulz schrieb:> Und gibt's eigentlich einen Grund dafuer, dass hier jeder zweite mit> "0.0" anstelle von "0" vergleicht?
Weil es an dieser Stelle völlig wurscht ist. Die 0 wird vom Compiler
sowieso auf 0.0 umgecastet. Aber mich als Programmierer erinnert die 0.0
daran, dass ich es hier mit Floating Point Werten zu tun habe.
Hans schrieb:> Das ist auch so eine Sache...macht es einen Unterschied, 1.0> zurückzugeben und darauf zu prüfen, oder einfach nur 1?
Nein.
Wo soll der Unterschied sein. Der Return Wert der Funktion ist als float
spezifiziert. Also wird auch ein float zurückgegeben. Ob du diesen float
nun als 1.0 (ist je eigentlich ein double) oder 1 schreibst ist egal. In
beiden Fällen wird ein float mit dem Wert 1.0 zurückgegeben. Ob das
Floating Point Format die 1.0 exakt darstellen kann oder nicht, ist in C
nicht weiter spezifizert, sondern hängt vom verwendeten Floating Point
Format ab. Seit einigen Jahren hat man es praktisch nur noch mit dem
IEEE 754 Format zu tun, welches ganze Zahlen bis zu einer gewissen Größe
exakt darstellen kann.
Handelt es sich wie bei dir um im Quellcode angegebene ganze Zahlen und
weiß man, dass IEEE 754 benutzt wird, kann man es riskieren einen
exakten Vergleich zu benutzen. Aber in dem Moment, in dem Werte
berechnet werden, ist ein Vergleich auf Gleichheit tödlich.
2 Gleitkommawerte a und b werden so auf Gleichheit verglichen
if( fabs( a - b ) < Epsilon )
sind gleich
und selbst dann, stellt einen die Wahl eines vernünftigen Epsilons noch
oft genug vor Probleme. Aber alles andere ist Murks. Und selbst dann,
wenn man Epsilon berücksichtigt, ist man noch lang nicht aus dem
Schneider.
http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html
Hans schrieb:> dann könnte das ja daneben gehen, oder nicht?
Ja, deswegen prüft man floats immer gegen eine Fehlergrenze.
also z.B. Vergleich auf == 1.0
if (fabs(Float - 1.0) < 1e-6)
{
}
Anja schrieb:> Ja, deswegen prüft man floats immer gegen eine FehlergrenzeLothar Miller schrieb:> Ich würde einen float nie auf Gleichheit prüfenKarl Heinz Buchegger schrieb:> 2 Gleitkommawerte a und b werden so auf Gleichheit verglichen
Generell mag das wohl stimmen, die Frage ist doch eher, ob der Compiler
eine Konstante auf unterschiedliche Weise kodieren kann, den nur dann
wird der Vergleich schief gehen. NaN und +/- INF sind solche scheinbaren
Konstanten, bei einfache Zahlen mit Ausnahme der 0 (welche positiv oder
negativ sein kann) ist die Kodierung aber festgelegt und der Vergleich
muß klappen, eventuell findet ja jemand in der IEEE Spec einen Hinweis
der mich widerlegt, ist schon länger her das ich das lesen durfte ;)
Volker Schulz schrieb:> vier verschiedene Arten von NANs nutzen, oder wie?
Naja es gibt schon einige mögliche Kodierungen für NaN...
... prinzipiell ist aber der Weg, ich gebe verschiedene (float) Werte
zurück die verschiedene Fehler repräsentieren wenn die Funktion auch ihr
Ergebnis als float zurück gibt sowieso kaputt.
Wenn man das wirklich machen will, sollte man im Fehlerfall NaN
zurückgeben, und der Funktion einen int pointer mitgeben welcher dann
den Status repräsentiert. (Ähnlich wie von Matthias Lipinsky
vorgeschlagen)
Läubi .. schrieb:> ... prinzipiell ist aber der Weg, ich gebe verschiedene (float) Werte> zurück die verschiedene Fehler repräsentieren wenn die Funktion auch ihr> Ergebnis als float zurück gibt sowieso kaputt.
Ich stimme zu, es ist bei Rueckgabewerten generell jedoch gaengige
Praxis und spaetestens bei Funktionen, die ueber irgendwelche
Interfacegrenzen hinweg aufgerufen werden auch einfacher.
In diesem speziellen Fall wuerde ich, auch wenn es nicht die eleganteste
Loesung ist, dem TO raten den Rueckgabewert (wenn negativ) zu runden und
dann die entsprechenden vergleiche anzustellen. Da kann dann nichts mehr
schiefgehen. ;)
Volker
Läubi .. schrieb:> Generell mag das wohl stimmen, die Frage ist doch eher, ob der Compiler> eine Konstante auf unterschiedliche Weise kodieren kann, den nur dann> wird der Vergleich schief gehen. NaN und +/- INF sind solche scheinbaren> Konstanten, bei einfache Zahlen mit Ausnahme der 0 (welche positiv oder> negativ sein kann) ist die Kodierung aber festgelegt und der Vergleich> muß klappen, eventuell findet ja jemand in der IEEE Spec einen Hinweis> der mich widerlegt, ist schon länger her das ich das lesen durfte ;)
Nein, nein.
Da hast du schon recht.
Bei IEEE 754 ist es möglich, ganze Zahlen (bis zu einer gewissen Größe)
exakt darzustellen.
Das Problem: C verlangt nicht, dass IEEE 754 benutzt werden muss.
Das ist das eine, das andere ist, dass eine Berechnung, sagen wir mal
sum = 0.0;
for( i = 0; i < 10; ++i )
sum += 0.1;
obwohl mathematisch 1.0 raus kommen müsste, auf einem Computer seher
nicht exakt 1.0 ergibt. OK, bei diesem ganz speziellen Fall vielleicht,
weil der Compiler eventuell schlau genug ist, das so zu optimieren, aber
ihr wisst schon was ich meine: Ich kann das Beispiel beliebig umformen,
so dass der Compiler eben nicht mehr optimieren kann. Und dann fällt man
mit direkten solchen Vergleiche auf die Schnauze.
Ich kann nur das PDF empfehlen, dass ich weiter oben verlinkt habe. Das
zeigt, wie man bei Floating Point ganz leicht zu widersprüchlichen
Ergebnissen kommen kann.
Auch wenn das, was du vorhast, wohl funktionieren wird....
Es ist Murks.
Wenn eine Funktion einen float-Rückgabewert liefert, dann sollte man den
nicht ohne guten Grund für etwas anderes wie "hat geklappt/hat nicht
geklappt" mißbrauchen.
NAN oder +/- INF wäre noch vertretbar, wenn es eine gewisse
matrhematische Entsprechung hat (acos(1.5)), aber du willst ja den
Rückgabewert für mehrere Fehlermeldungen zweckentfremden.
Entweder machst du den Rückgabe zu einer int oder enum, mit der du den
(Miß-) Erfolg signalisierst, und lieferst im Erfolgsfall über einen
übergebenen Zeiger den Wert, oder umgekehrt (Ergebnis als Rückgabewert,
Zeiger auf int/enum für Fehlermeldung).
Das alles in einem Rückgabewert zu vermatschen, ist
Hinterhofprogrammierung, zu der man sich damit deutlich bekennt.
Wenn ich so etwas in einem Programm sehe, nehme ich den Rest meist schon
nicht mehr ernst.
Klaus Wachtler schrieb:> [...]> Das alles in einem Rückgabewert zu vermatschen, ist> Hinterhofprogrammierung, zu der man sich damit deutlich bekennt.> Wenn ich so etwas in einem Programm sehe, nehme ich den Rest meist schon> nicht mehr ernst.
So Hinterhof-Klitschen wie Microsoft, Apple, The PHP Group, Creative
Labs, AMD, ... machen sowas. Wer kann deren Code schon richtig ernst
nehmen? Oder diese Hinterhof Linux-Experten, die Libs fuer Videotreiber
oder aehnliches schreiben. Voellig daneben finde ich auch beispielsweise
den Rueckgabewert von sprintf: "On success, the total number of
characters written is returned. [...] On failure, a negative number is
returned." Wer macht denn sowas?!? Igitt!
Volker
Also Leute, vielen Dank für eure Rückmeldungen.
Ich habe es nun folgendermaßen gelöst: Ich werte zuerst die Eingabe aus
und setze eine Status-Variable, die vor dem eigentlichen Ergebnis
abgeholt wird:
1
if(!(status))
2
{
3
holeErgebnis;
4
}
5
else
6
{
7
wertestatusaus...
8
}
Tritt ein Status anders als 0 auf, wird auch keine Berechnung
durchgeführt. Wichtig ist dabei natürlich, dass der Status immer vorab
abgeholt wird, da das Ergebnis sonst ein vorheriges widerspiegeln
könnte.
Karl Heinz Buchegger schrieb:> Und dann fällt man> mit direkten solchen Vergleiche auf die Schnauze.
Wie gesagt, im allgemeinem Fall stimme ich 100% zu, hier im speziellem
geht es aber nicht um Berechnungen o.ä. sonder darum, das eine Funktion
einen konstanten Wert zurückliefert (-1, -2, -3 ...) und im Code
ebenfalls auf diese Konstanten Werte verglichen wird.
Das sollte nach meinem Verständnis (auch wenn nicht gefordert ist das
IEEE 754 genutzt wird) keine Probleme bereiten.
Anderes Beispiel (Zahlen beliebig gewählt):
1
if(5f==5f){
2
//tu was
3
}
Hier würde ich erwarten das eine "alway true" warnung o.ä. vom Compiler
kommt, oder ob dies ein "undefined behaviour" ist, da der Compiler
(durch den C-Standard) die Erlaubnis hat die beiden Konstanten
unterschiedlich zu kodieren.
Läubi .. schrieb:> Karl Heinz Buchegger schrieb:>> Und dann fällt man>> mit direkten solchen Vergleiche auf die Schnauze.>> Wie gesagt, im allgemeinem Fall stimme ich 100% zu, hier im speziellem> geht es aber nicht um Berechnungen o.ä. sonder darum, das eine Funktion> einen konstanten Wert zurückliefert (-1, -2, -3 ...) und im Code> ebenfalls auf diese Konstanten Werte verglichen wird.> Das sollte nach meinem Verständnis (auch wenn nicht gefordert ist das> IEEE 754 genutzt wird) keine Probleme bereiten.
Da hast Du (nach meinem Verstaendnis) natuerlich recht. "Da draussen im
echten Leben" wird aber auch der negative Fehler-Rueckgabewert schon mal
"berechnet" um mehrere Fehler gleichzeitig abbilden zu koennen.
error=0;
if(fehler1) error+=1;
if(fehler2) error+=2;
if(fehler3) error+=4;
[...]
return error*-1
Volker
Volker Schulz schrieb:> "Da draussen im> echten Leben"
Wie schon von mir weiter oben geschrieben, würde ich solch eine Art der
Fehlerbehandlung auch im echten Leben strickt ablehnen, das sind nämlich
genau die "genialen" Einfälle wo man sich zwei Minuten Tiparbeit sparen
wollte oder "nur mal eben schnell" einen Fehler signalisieren welche
einem dann später den ganzen Tag versauen ;)
Es wird nämlich immer einen Dummen (meist man selbst) geben der dann
doch einfach sowas macht:
1
floatx=z*berechne(y)+1;
und leider erst viel zu spät mitkriegt, dass das Ergebnis komischerweise
manchmal falsch ist, aber nicht wenn z gleich -42... ;-P
Hans schrieb:> Ich werte zuerst die Eingabe aus> und setze eine Status-Variable, die vor dem eigentlichen Ergebnis> abgeholt wird
Klingt etwas verworren, zeig doch einfach mal deine Funktionsdefinition,
man kann das z.B. so lösen:
Volker Schulz schrieb:> Da hast Du (nach meinem Verstaendnis) natuerlich recht. "Da draussen im> echten Leben" wird aber auch der negative Fehler-Rueckgabewert schon mal> "berechnet" um mehrere Fehler gleichzeitig abbilden zu koennen.>> error=0;> if(fehler1) error+=1;> if(fehler2) error+=2;> if(fehler3) error+=4;> [...]> return error*-1>> Volker
Super Idee. Muss ich mir merken.
Läubi .. schrieb:> Volker Schulz schrieb:>> "Da draussen im>> echten Leben"> Wie schon von mir weiter oben geschrieben, würde ich solch eine Art der> Fehlerbehandlung auch im echten Leben strickt ablehnen, das sind nämlich> genau die "genialen" Einfälle wo man sich zwei Minuten Tiparbeit sparen> wollte oder "nur mal eben schnell" einen Fehler signalisieren welche> einem dann später den ganzen Tag versauen ;)
Hehe. Haengt natuerlich von mehr als einem Umstand ab. Mir erleichtern
solche Arten der Fehlerbehandlung taeglich die Arbeit: Wenn ich ein
Handle per API hole, ist es schlichtweg bequem (und m.E. auch sinnvoll)
wenn ich direkt am (negativen) Rueckgabewert den Fehler "ablesen" kann.
Und das auch dann, wenn das Ziel eines Zeigers eventuell schon nicht
mehr existiert. Nicht selten hatte ich auch schon Spezifikationen auf
dem Tisch, in denen explizit gefordert wurde, dass ein Objekt noch vor
Auswertung der Rueckgabewerte "disposable" sein muss (oder sich sogar
selbst "disposen" muss), mit Pointern kommt man da im Zweifel auch nicht
weit. Ich bin auch schon mit der Qualitaetssicherung aneinander geraten,
weil ich (auf zugegebenermassen schmalbruestigen Systemen) die
Bitbreiten von Variablen / Registern nicht in groesstmoeglichem Umfang
ausgenutzt habe... Es gibt diesbezueglich also durchaus
verschiedenartige Ansaetze und Ansichten, die ich nicht ohne Weiteres in
"Hinterhof" und "Nicht Hinterhof" unterteilt wissen moechte. ;)
Volker