Dies ist eine alte Version des Dokuments!


Gleitkommazahlen

Bislang dachte ich, dass alles was können ist Rechnen und das obendrein noch richtig schnell aber nachdem ich jetzt angefangen habe eine Programmiersprache zu lernen (C) musste ich feststellen, dass das so nicht stimmt und war zugegebenermaßen völlig überrascht, wie oft sich ein Computer tatsächlich verrechnet. Ein Wunder, dass die Dinger nicht ständig abstürzen.

Das Problem über das ich gestolpert bin, sind Berechnungen mit Gleitkommazahlen, auch Float genannt. Fragt man nun einen Computer ob »0,1 + 0,1 + 0,1« gleich »0,3« oder »1,1 * 1,1« gleich »1,21« ist, sagt er Nein. Klingt komisch, ist aber so:

float_1.c

#include <stdio.h>
 
int main( void ) { 
  printf( "%d\n", 1.1 * 1.1       == 1.21 );
  printf( "%d\n", 0.1 + 0.1 + 0.1 == 0.3  );
}

Dieses Programm vergleicht mit dem Vergleichsoperator »==« ob die Gleichungen in der printf() Funktion wahr oder falsch sind, wobei eine »1« als Rückgabe die Gleichung als »wahr«, bzw. eine »0« die Gleichung als »falsch« identifiziert:

$ ./float_1 
0
0

Da hat mein Weltbild schon angefangen zu wanken aber was ist dabei schlief gelaufen? Schauen wir uns mal an, was dann die og Rechnungen für ein Ergebnis liefern:

float_2.c

#include <stdio.h>
 
float f = 0.1 + 0.1 + 0.1;
float e = 1.1 * 1.1;
int main( void ) { 
  printf( "%.9f\n", f );
  printf( "%.9f\n", e );
}

Dieser Code gibt die Summen in der Variablendeklaration mit neun Nachkommastellen aus:

$ ./float_2 
0.300000012
1.210000038

Das erklärt zumindest mal das überraschende Ergebnis der Vergleichsoperation im ersten Programm aber befriedigend ist erst mal nicht. Das Ganze geht sogar noch einen Schritt weiter, lassen wir den Rechner mal nicht rechnen, sondern uns nur die interne Darstellung der beiden Zahlen »0,1« und »1,1« ausgeben:

float_3.c

#include <stdio.h>
 
float f = 0.1;
float e = 1.1;
int main( void ) {
  printf( "%.9f\n", f );
  printf( "%.9f\n", e );
}

$ ./float_3 
0.100000001
1.100000024

Ich fand, dass schreit nach einer Erklärung und ich habe dieses Thema in der Newsgroup »de.comp.lang.c« zur Diskussion gestellt. Also warum ist das so? Die kurze Antwort ist: Das liegt an der Art, wie intern Gleitkommazahlen dargestellt werden. Die lange Antwort aber ist gar nicht so leicht zu verstehen und beim ersten Überfliegen der Erklärung dachte ich, dass schon ein paar Semester Mathematik nötig wären, um das in letzter Konsequenz zu verstehen.

Kurz gefasst kann man festhalten, dass Gleitkommazahlen intern als Ganzzahl dargestellt werden. Ergibt die Umrechnung der Gleitkommazahl keine Ganzzahl, ist sie nicht exakt als Binärzahl darstellbar und es kommt zu oben gezeigten Umrechnungsfehlern. Nehmen wir unser og Beispiel »0,1« und rechnen das mal in eine Ganzzahl um. Es wird zuerst die Anzahl der Nachkommastellen als Exponent zur Basis 2 errechnet. »0,1« hat eine Nachkommastelle → »21 = 2«. Nun wird die Gleitkommazahl mit diesem ermittelten Wert multipliziert und wenn das Ergebnis dieser Multiplikation keine ganze Zahl ergibt, kommt es zu dem oben gezeigten Effekt → »0,1 * 2 = 0,2«.

Schauen wir mal wie es mit »1,1« ausschaut: »21 * 1,1 = 1,21«, was auch keine ganze zahl ergibt. Schauen wir uns mal eine Gleitkommazahl an, bei der das aber funktioniert: »1.998046875« hat neun Nachkommastellen, was »29 = 512« ergibt, multipliziert man nun »1.998046875« mit »512« hat man das Ergebnis »1023«. Das ist eine ganze Zahl und wird dementsprechend korrekt dargestellt:

float_4.c

#include <stdio.h>
 
float f =1.998046875;
int main( void ) {
  printf( "%.70f\n", f );
}

$ ./float_4 
1.9980468750000000000000000000000000000000000000000000000000000000000000

it/float.1322406399.txt.gz (17851 views) · Zuletzt geändert: 2011/11/27 16:06 von wikisysop
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0