Problem mit Ausgabe mit >=
-
Hallo zusammen,
ich habe ein kleines Programm geschrieben, in welchem in einer for-Schleife ein Zahlenbereich durchschritten wird, und nur eine Ausgabe ausgegeben werden soll, wenn ein entsprechender Wert zwischen zwei (inklusiven!) Schranken liegt.
Je nachdem, mit wievielen Nachkommastellen ich ausgeben lasse, wird jedoch die obere Schranke für geringe Nachkommastellen mit ausgegeben (da hier anscheinend gerundet wird, glaube ich), wenn ich hingegen viele Nachkommastellen ausgeben lasse wird als höchster Wert 0,00395 ausgegeben, es sollte jedoch eigentlich 0,004 sein. Woran liegt das? Ich habe verschiedene Veränderungen in meinem Code vorgenommen und kann es nicht erklären. Hier der Code:
#include <stdio.h> #include <math.h> int main(){ double LOW=0.0030; double HIGH=0.0040; double STEP=0.00005; int i; int imax=200; double z(int i){ return (i*STEP); } FILE *NEW; NEW=fopen("DATA.txt", "w"); for(i=0; i<=imax; i++){ if((z(i)>=LOW) && (z(i)<=HIGH)){ fprintf(NEW," %.5f\n", z(i)); } } fclose(NEW); return 0; }
Noch eine Sache: Ich bin Programmier-Neuling und mache mir über den Programmierstil erstmal weniger Gedanken, auch wenn manche Leute es nicht mögen, möchte ich hierbei gerne ">=" und "<=" verwenden.
Vielen Dank im voraus!
-
Ich habe keine Ahnung, was Du meinst.
Wenn ich das Programm laufen lasse, erhalte ich eine Datei mit Zahlen drin. Und sind die jetzt falsch? Was hast Du denn erwartet? Gib mal ein Beispiel, dass den Unterschied, der Dich irritiert verdeutlicht.Ausserdem habe ich Dir schon gesagt, dass Du die Warnungen in Deinem Compiler aktivieren sollst: Du definierst z.B.
z()
innerhalb vonmain()
- solche inneren Funktionen gibt es nicht in C.
-
Okay, ich erkläre es etwas deutlicher:
Letztendlich möchte ich eine Datei haben, in der die Zahlen 0.0030, 0.00305, 0.0031 ... 0.00395, 0.0040 stehen. Letztendlich laufen die Zahlen jedoch nur bis 0.00395, obwohl ich ein Kleiner-gleich verwendet habe. Die Zahl 0.0040 wird dann nicht mehr angezeigt, warum?
Und eine blöde Frage: Ich kompiliere mit cygwin. Wie aktiviere ich da die Warnungen? Bisher hat immer alles (trotz Definition einer Funktion in main() ) geklappt.
-
Wahrscheinlich ist 0.004 (als Gleitkommazahl) in Deiner Implementierung von C kleiner als 0.004. Das liegt daran, dass Gleitkommazahlen nicht alle Zahlen exakt darstellen koennen.
Wenn Du also 0.004 ausgibst (mit vielen Nachkommastellen), sollte also irgendwas mit 0.003... rauskommen.Bei mir ist der Wert etwas größer als 0.004 - aber der Fehler tritt erst irgendwo bei der 20. Nachkommastelle auf.
cygwin? Keine Ahnung: benutzt Du dann den gcc?!
Dann müsstest Du noch-Wall -pedantic
zu den Flags hinzufügen. Aber da gibt's sicher Leute, die das besser wissen.
-
2D-Mann schrieb:
Die Zahl 0.0040 wird dann nicht mehr angezeigt, warum?
wie sieht deine ausgabe dieser zeile aus?
printf("%d", 0.00005 * 80 == 0.004);
-
Furble Wurble schrieb:
Wahrscheinlich ist 0.004 (als Gleitkommazahl) in Deiner Implementierung von C kleiner als 0.004. Das liegt daran, dass Gleitkommazahlen nicht alle Zahlen exakt darstellen koennen.
Wenn Du also 0.004 ausgibst (mit vielen Nachkommastellen), sollte also irgendwas mit 0.003... rauskommen.Bei mir ist der Wert etwas größer als 0.004 - aber der Fehler tritt erst irgendwo bei der 20. Nachkommastelle auf.
Das ist auch nicht direkt das Problem. 0.004 ist zwar als double etwas größer als 0.004, aber 0.00005 ist eben auch etwas größer als das mathematische 0.00005. Und 80 Mal dieser Wert ist dann nochmals größer als die double-Repräsentation von 0.004.
Siehe:
http://www.binaryconvert.com/index.html
(Und für Fortgeschrittene: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html)@Threadersteller: Es ist zwar nicht festgelegt, wie Fließkommazahlen intern genau funktionieren, aber praktisch jeder existierende Computer auf der Welt benutzt das Format, welches in dem Link verwendet wird. Zahlen werden dabei im Computer intern in einer Binärrepräsentation gespeichert. Diese Repräsentation benutzt eine endliche Anzahl von Stellen, da der Computer logischerweise nicht unendlich viele Stellen speichern kann. Bei double sind dies in der Regel 53 Stellen (plus ein paar Tricksereien). Im Binärsystem können aber viele Zahlen nicht mit endlich vielen Stellen exakt dargestellt werden, die im Zehnersystem mit endlich vielen Stellen ausgeschrieben werden können. So wie im Zehnersystem beispielsweise 1/3 nicht mit endlich vielen Stellen ausgeschrieben werden kann (im Dreiersystem wäre das wiederum ganz einfach: 0.1). Das hat was mit den Primfaktoren zu tun, weil in der 10 eine 2 und eine 5 stecken, in der Basis des Binärsystems (2) aber nur eine 2. Daher kann man im Binärsystem nur so Zahlen wie 1/2, 1/4, 1/8, usw. und Vielfache und Kombinationen* davon exakt darstellen.
Wenn du nun solche Zahlen benutzt, dann wird intern die nächstmögliche Darstellung benutzt. Und dann passiert eben so etwas wie hier, da 80 Mal die nächstbeste Darstellung von 0.00005 eben nicht mehr 0.004 ergibt und auch nicht die nächstbeste Darstellung von 0.004.
Vorschlag: Rechne so lange es geht mit Ganzzahlen. Ganzzahlen können auch im Zweiersystem allesamt exakt dargestellt werden
. Also so etwas wie
for (i = 1; i <= 80; ++i) printf("%f ", i * 0.00005);
*: Der aufmerksame Leser erkennt, dass eine Kombination solcher Werte auch automatisch ein Vielfaches eines anderen Wertes ist, wie z.B. 0.875 = 1/2 + 1/4 + 1/8 = 7 * 1/8. Das ist kein Zufall, sondern hat auch etwas mit der Zifferndarstellung zu tun.
-
Bei Fließkommazahlen gibt es keine Gleichheit. Deshalb kann man >= wie > und <= wie < ansehen.
-
oenone schrieb:
Bei Fließkommazahlen gibt es keine Gleichheit. Deshalb kann man >= wie > und <= wie < ansehen.
Zahlen die sich exakt darstellen lassen sind auch gleich. Um bei 0.875 zu bleiben:
7 * 1./8 == .875
.
-
Danke für die vielen informativen Antworten!
Dass 0.004 als double größer ist als die "echte" Zahl 0.004 ist sehr ärgerlich. Die >= bzw. <= Operatoren kann man für kleine Werte also getrost in den Müll werfen? Ich habe es jetzt letztendlich so gemacht, dass ich in der if-Bedingung LOW durch LOW-STEP/2 ersetzt habe und das gleiche analog für HIGH. Ist zwar umständlicher, klappt aber.
-
Furble Wurble schrieb:
oenone schrieb:
Bei Fließkommazahlen gibt es keine Gleichheit. Deshalb kann man >= wie > und <= wie < ansehen.
Zahlen die sich exakt darstellen lassen sind auch gleich. Um bei 0.875 zu bleiben:
7 * 1./8 == .875
.Ich verbessere meine Aussage:
Bei berechneten Fließkommazahlen gibt es keine Gleichheit.
Für triviale Fälle gibt es wohl gleiche Werte, aber wenn die Werte vorher (möglicherweise in mehreren Schritten) berechnet wurden, ist es sehr unwahrscheinlich.
@2D-Mann:
Genaue Werte bekommst du nur durch Ganzzahlen. Du kannst aber auch "großzügige" Grenzen benutzen, z.B. 0.0040001 (üblicherweise +/- Epsilon oder Delta)
-
oenone schrieb:
Ich verbessere meine Aussage:
Bei berechneten Fließkommazahlen gibt es keine Gleichheit.
Woher weiß denn der Prozessor, welche berechnet sind?
Das ist einfach Unsinn.
Genaue Werte bekommst du nur durch Ganzzahlen.
Nein, auch durch Zweierpotenzen.
2D-Mann schrieb:
Dass 0.004 als double größer ist als die "echte" Zahl 0.004 ist sehr ärgerlich.
Nein, eigentlich nicht. Das ist hier auch gar nicht das Problem. SeppJ hat das schon beschrieben, deshalb will ich es nicht wiederholen.
Die >= bzw. <= Operatoren kann man für kleine Werte also getrost in den Müll werfen?
Warum? Wär doch echt blöd, wenn du <= meinst, aber aufgrund von dummen Tipps < schreibst, und dann ist es doch "zufällig" mal eine Gleichheit.