FAQ:Gleitkomma-Probleme

[ FAQ in de.comp.lang.c ]


Diese FAQ bezieht sich in ihrer Gänze auf den inzwischen nicht mehr aktuellen ISO-C Standard 9899:1990, vielfach auch als C90 bezeichnet. Der seit Dezember 1999 existierende neue ISO 9899:1999 Standard (oder auch C99) wird nicht berücksichtigt.


[ Inhalt ][ Index ][ ZurÜck ][ Weiter ]


Frage 15.1: Meine Gleitkommarechnungen zeigen ein eigenartiges Verhalten und/oder führen auf unterschiedlichen Rechnern zu unterschiedlichen Ergebnissen.

Antwort: Bei Problemen sollte zunächst sichergestellt werden, dass <math.h> mit

	#include <math.h>

eingebunden wird, und dass alle Funktionen, die 'double' zurückgeben auch korrekt deklariert sind.

Wenn sich das Problem nicht so einfach lösen läßt, erinnere Dich, dass die von den meisten Digitalrechnern verwendeten Gleitkommaformate zwar eine relativ gute, aber keine exakte Simulation der Arithmetik für reelle Zahlen ermöglichen. Gleitkommaunterlauf, Fortpflanzung von Rundungsfehlern und andere Anomalien führen oft zu Problemen.

Gleitkommaergebnisse sind nie exakt, und es ist praktisch *immer* ein Fehler, Gleitkommawerte auf Gleichheit zu prüfen.

Diese Probleme hat C mit anderen Sprachen gemeinsam. Gleitkommasemantik ist üblicherweise definiert als "wie auch immer der jeweilige Prozessor es macht", andernfalls hätte ein Compiler für einen Rechner mit dem 'falschen' Modell extrem aufwendige Emulationen einzubinden.

Dieser Artikel kann keine Liste der Fallgruben im Bereich der Gleitkommaarithmetik bzw. der sicheren Umgehungen derselben sein. Gute Programmierlehrbücher sollten entsprechende Einführungen enthalten.

References: EoPS Sec. 6 pp. 115-8.


Frage 15.2: Ich arbeite mit trigonometrischen Funktionen, habe 'math.h' geladen, erhalte aber "undefined: _sin" u.ä. als Fehlermeldung.

Antwort: Wird die Mathe-Bibliothek dazugelinkt? Unter Unix z.B. ist '-lm' als Linkeroption erforderlich, und zwar am Ende der Linker Kommandozeile. (s.a. 12.14)


Frage 15.3: Wie runde ich Gleitkommazahlen?

Antwort: Eine einfache Lösung, die aber nur für positive Zahlen korrekt arbeitet ist:

	(int)(x + 0.5)

Auch für negative Zahlen funktioniert

	(int)floor (x + 0.5)

(letztere Antwort stammt aus einer Diskussion in d.c.l.c).


Frage 15.4: Wie teste ich auf IEEE NaN und andere spezielle Werte?

Antwort: Viele Systeme mit hochwertigen IEEE Gleitkomma-Implementierungen stellen entsprechende Tests zur Verfügung (z.B. ein Makro isnan()), um sauber mit diesen 'Werten' umzugehen; die Numerical C Extensions Group (NCEG) erarbeitet formelle Standards für derartige Hilfmittel. Ein grober aber i.a. erfolgreicher Test auf NaN ist

	#define isnan(x) ((x) != (x))

wenngleich ein eifriger Optimizer den Ausdruck u.U. wegoptimiert.


Frage 15.5: Mein Turbo C Programm stürzt mit der Meldung "floating point formats not linked" ab.

Antwort: Einige Compiler für Kleinrechner, einschließlich Turbo C (und Ritchies ursprünglichem PDP-11 Compiler), binden keine Gleitkommaunterstützung ein, wenn es so aussieht, als ob sie nicht benötigt wird. Insbesondere benötigen die Versionen von printf und scanf, die ohne Gleitkommaunterstützung auskommen, indem sie auf die Behandlung von %e, %f und %g verzichten, weniger Speicher. Bei Turbo C scheint die Entscheidung über die Notwendigkeit der Einbindung von Gleitkommacode unzuverlässig zu sein. Deshalb ist manchmal ein (sonst überflüssiger) Aufruf einer Gleitkomma-Bibliotheksfunktion nötig, um dem Erkennungsmechanismus auf die Sprünge zu helfen.


Frage 15.6: Wie wird eine Variable nach IEEE-Gleitkommaformat im Rechner dargestellt?

Antwort: Diese Frage läßt sich ohne Kenntnis von CPU, Compiler und BS nicht beantworten. IEEE-754 (der FP-Standard) definiert nur die Bedeutung der Bits in ihrer logischen (nicht physikalischen) Reihenfolge. IEEE-754 definiert 3 Gleitkommatypen : single, double und double-extended. Single und double-Typen sind 32 bzw. 64 Bit lang, double-extended ist nicht vollständig definiert und wird von Compilerherstellern derzeit meist mit einer Länge von 80 Bit implementiert. Es ist für die interne Darstellung in Koprozessoren konzipiert worden und soll hier nicht weiter behandelt werden.

Das 32/64bit Format sieht wie folgt aus:

	     VZ  Charakteristik   Mantisse
	    I  I       //       I       //        I
	32:  31   ....   ...  23 22 ...     ...  0
	64:  63   ....   ...  52 51 ...     ...  0

Das höchstwertige Bit gibt das Vorzeichen der Zahl an (wie üblich : 0 <=> '+' ; 1 <=> '-' ).

Die Charakteristik entspricht einem derart geshifteten Exponenten, dass der Wertebereich von 0 bis 2^N - 1 reicht. Hierbei zeigen die Grenzwerte Unterlauf bzw. NaN an. Ansonsten gilt:

32: Char. = Exp + 127 64: Char. = Exp + 1023

Die Mantisse liege in normalisierter Form vor, d.h. 1.0 <= M < 2.0, - 1.00...00 <= M <= 1.11...11 . Diese Information hält man fest, läßt das höchstwertige Bit jedoch wegfallen. Dadurch hat man 1 Bit Auflösung gewonnen, was im Falle des 'single'-Formats den Unter schied zwischen sicheren 6 bzw. 7 Dezimalstellen bedeutet.

Die Auflösung beträgt 24 bzw 53 bit; dies entspricht 7 bzw. 15 signifikanten Dezimalstellen (Bitte nicht mit Nachkommastellen verwechseln!).

[ Inhalt ][ Index ][ ZurÜck ][ Weiter ]


[ FAQ Logo ]   © 1997-2004 Jochen Schoof (joscho@bigfoot.de)
Diese Version wurde am 14. März 2004 erzeugt. Sie wird zukünftig nicht weiter gepflegt.