Zahlenbereiche und spezielle Werte in R (Inf, NaN, NA und NULL)

Gezeigt wird, wie man in R die Zahlenbereiche für die Modi integer und double feststellen kann und wie man Zahlen in Potenz-Schreibweise eingeben kann. Ausführlich werden die Ausnahme-Werte Inf (Infinity), NaN (Not a Number), NA (Not Available) und NULL vorgestellt.

Einordnung des Artikels

Einführung

Irgendwann muss sich jeder Programmierer damit beschäftigen, wieviel Speicherplatz für eine Variable zur Verfügung steht und wie groß die entsprechenden Zahlenbereiche für die unterschiedlichen Datentypen sind. Mit diesem Thema verknüpft ist immer die Frage:

Was passiert, wenn ein zulässiger Zahlenbereich überschritten wird?

Das Verhalten ist bei nahezu jeder Programmiersprache anders.

In diesem Abschnitt werden die Zahlenbereiche für double und integer gezeigt und die besonderen Werte. Damit sind — grob und etwas ungenau gesagt — symbolische Konstanten gemeint, die anzeigen sollen, dass eine Berechnung nicht wie vorgesehen durchgeführt werden kann. Eine Übersicht über die in R verwendeten besonderen Werte zeigt die folgende Tabelle:

Wert Beschreibung
Inf Infinity: zeigt an, dass der Zahlenbereich von double überschritten wird
NaN Not a Number: zeigt an, dass eine Berechnung nicht durchgeführt werden kann
NA Not Available (oder Missing Value): zeigt an, dass ein Wert nicht verfügbar ist (zum Beispiel ein fehlender Wert in einer Messreihe)
NULL leeres Objekt (zum Beispiel der "Datentyp" einer Funktion ohne Rückgabewert)

Hinter den besonderen Werten verbergen sich logische Konstante, die angeben, ob eine Zahl unendlich ist oder nicht und so weiter. Später wird dann gezeigt, dass NA und NULL nicht nur als Stellvertreter für Zahlen sondern auch für andere Datentypen eingesetzt werden können.

Für den Anfänger sind diese besonderen Werte meist nur verwirrend, da mit ihnen zahlreiche Spitzfindigkeiten verbunden sind. Wer aber ernsthafte Programme schreibt, muss wissen wie er mit ihnen umgeht und kommt an einem gründlichen Studium ihres Verhaltens nicht vorbei.

An einigen Stellen werden bereits Vektoren behandelt, obwohl diese erst später erklärt werden. Bei einem ersten Durchgang durch dieses Kapitel können diese Abschnitte übersprungen werden; abgesehen von der Syntax sollten sie aber jetzt schon verständlich sein.

Notation von Potenzen

Bevor diskutiert wird, welche Zahlenbereiche von den verschiedenen Datentypen abgedeckt werden, soll noch gezeigt werden, wie Potenzen geschrieben werden können. Das folgende Skript zeigt die unterschiedlichen Schreibweisen.

# verschiedene Potenz-Schreibweisen

# diese oder die wissenschaftliche Schreibweise sind empfehlenswert
q <- 1.6 * 10^(-19)
q			# 1.6e-19

# Klammern im Exponenten müssen nicht gesetzt werden: Die Lesbarkeit wird aber erschwert
q <- 1.6 * 10^-19
q			# 1.6e-19

# wissenschaftliche Schreibweise kann verwendet werden
q <- 1.6e-19
q			# 1.6e-19

# auch mit großem E
q <- 1.6E-19
q			# 1.6e-19

# Potenz kann auch durch ** ersetzt werden: schwerer lesbar, da leicht mit * zu verwechseln
q <- 1.6 * 10**(-19)
q			# 1.6e-19

# geht auch: noch schwerer lesbar
q <- 1.6*10**-19
q			# 1.6e-19

Zeile 4: Für Eingaben wird empfohlen entweder die in Zeile 4 gezeigte Schreibweise zu verwenden oder die wissenschaftliche Schreibweise aus Zeile 12 (letztere wird von vielen wissenschaftlichen Taschenrechnern und auch in vielen anderen Programmiersprachen verwendet).

Zeile 5: Für Ausgaben wird stets die wissenschaftliche Schreibweise (mit kleinem e) verwendet.

Zeile 8: Eine negative Potenz muss nicht in Klammern gesetzt werden — für die bessere Lesbarkeit ist es aber zu empfehlen.

Zeile 12 und 16: Die wissenschaftliche Schreibweise kann auch für Eingaben verwendet werden; dabei ist es egal, ob ein kleines e oder ein großes E verwendet wird. Klammern müssen auch hier nicht bei negativen Potenzen gesetzt werden.

Zeile 20 und 24: Möglich, aber nicht empfehlenswert, ist die Schreibweise mit dem doppelten Produktzeichen für die Potenz.

Die Zahlenbereiche für "integer" und "double"

Es sollte klar sein, dass zum Abspeichern von Zahlen nicht beliebig viel Speicherplatz zur Verfügung gestellt werden kann. Es ist aber nicht möglich, hier allgemeingültige Angaben zu machen, wo die Grenzen für ganze Zahlen ("integer") und Gleitkommazahlen ("double") liegen. Denn diese Grenzen sind im Prinzip plattformabhängig, stimmen inzwischen aber auf fast allen Plattformen mit den hier gezeigten Werten überein. Plattformabhängig heißt, dass die Zahlenbereiche vom Betriebssystem und dem Rechner — 32 Bit oder 64 Bit — abhängen können.

Im Zweifelsfall kann man die Grenzen der Zahlenbereiche abfragen, nämlich indem man die Variable .Machine ausgibt (Zeile 1):

.Machine
# $double.xmin
# [1] 2.225074e-308
# 
# $double.xmax
# [1] 1.797693e+308
#
# $integer.max
# [1] 2147483647

Hinter der Variable .Machine verbirgt sich eine Liste — die Erklärung von Listen erfolgt in einem eigenen Abschnitt. Die für diesen Abschnitt relevanten Einträge der Liste sind im Skript oben zu sehen:

Zeile 2: Die (betragsmäßig) kleinste Gleitkommazahl in Normaldarstellung, die man von null unterscheiden kann; kleinere Zahlen werden null gesetzt.

Zeile 5: Die größte Gleitkommazahl in Normaldarstellung, größere Zahlen werden mit Inf bezeichnet.

Zeile 8: Die größte ganze Zahl, die den Speichermodus "integer" haben kann; größere Zahlen werden als NA (not available) bezeichnet. Die Zahl 2147483647 stimmt mit 231-1 überein, sie beträgt in etwa 2.1e+9 (also etwas mehr als 2 Milliarden).

Die Variable .Machine enthält noch deutlich mehr Konstanten, die hier nicht erklärt werden.

Das folgende Skript zeigt eine Spitzfindigkeit, die man bei der Verwendung von Zahlen immer beachten muss — und die in vielen anderen Programmiersprachen nicht vorkommt:

n <- 2147483647

storage.mode(n)
# "double"

n <- as.integer(n)
storage.mode(n)
# "integer"

n <- n + 1
n
# 2147483648

storage.mode(n)
# "double"

n <- as.integer(n)
# Warning message:
#   NAs introduced by coercion to integer range

storage.mode(n)
# "integer"

n
# NA

Zeile 1: Die Zahl n wird als die maximal mögliche ganze Zahl definiert.

Zeile 3: Ohne weitere Anweisung wird sie — das default-Verhalten in R — als "double" abgespeichert.

Zeile 6 und 7: Der Speichermodus wird in "integer" verwandelt und sofort abgefragt.

Zeile 10: Addiert man zu n die Zahl 1, wird der Zahlenbereich für "integer" verlassen; dies führt aber zu keiner Fehlermeldung, da durch die Anweisung in Zeile 10 n als Gleitkommazahl ("double") abgespeichert wird (Zeile 14) — auch wenn dafür keine ausdrückliche Anweisung gegeben wird.

Zeile 11 und 14: n kann fehlerfrei ausgegeben werden und der Speichermodus ist tatsächlich "double".

Zeile 17: Versucht man n ausdrücklich als "integer" abzuspeichern, erhält man eine Warnung.

Zeile 21 und 24: Der Speichermodus von n ist zwar auf "integer" gesetzt, aber die Ausgabe von n zeigt NA.

Inf, NaN und NA

Infinity: Inf

In einigen Beispielen zur Verwendung der wissenschaftlichen Funktionen war bereits zu erkennen: Inf wird immer dann angezeigt, wenn der Zahlenbereich von "double" überschritten wird. Auch wenn Inf natürlich als Abkürzung für Infinity steht, darf man nicht erwarten, dass es die Eigenschaften besitzt, die dem Unendlich-Symbol ∞ in der Mathematik zugeschrieben werden. So wird die Ausgabe von Inf zwar mit einem Vorzeichen versehen, man kann damit aber keine typischen Grenzwerte aus der Mathematik berechnen, wie das folgende Beispiel zeigt:

x <- 0
y <- 1 / x
y
# Inf

y <- -1 / x
y
# -Inf

Aus der Mathematik ist aber bekannt: Lässt man x gegen 0 gehen, so hängt der Grenzwert der Funktion f(x) = 1/x davon ab, ob man sich mit x von der positiven oder negativen Seite an 0 annähert; dieses Verhalten wird oben nicht richtig wiedergegeben.

In vielen Fällen wird aber der mathematisch richtige Grenzwert angegeben, zum Beispiel bei der Logarithmus-Funktion:

x <- 0
log(x)
# -Inf

Es ist sogar möglich eine Variable mit dem Wert Inf (oder -Inf) zu initialisieren und damit weiterzurechnen:

z <- Inf

1 / z
# 0

z^2
# Inf

z / Inf
# NaN

Bei der letzten Berechnung in Zeile 9 entsteht NaN (Not a Number): die Division ∞ / ∞ ist nicht definiert und das Ergebnis kann nicht sinnvoll mit Inf ausgedrückt werden. Die Eigenschaften von NaN werden im folgenden Unterabschnitt erklärt.

Not a Number: NaN

Durch eine Ausgabe von Inf soll angedeutet werden, dass der zulässige Zahlenbereich von double überschritten wurde. Dagegen soll NaN (Not a Number) zeigen, dass eine Berechnung — aus welchen Gründen auch immer — nicht durchgeführt werden konnte und dass auch Inf oder -Inf das Ergebnis nicht sinnvoll darstellen können. Diese Gründe können vielfältig sein, um einige Beispiele zu nennen:

Das folgende Skript zeigt einige Beispiele:

0 / 0
# NaN

x <- Inf
y <- -Inf

x * y				# Grenzwert sinnvoll
# -Inf

x + y				# Grenzwert nicht mehr sinnvoll
# NaN

x / y				# Grenzwert nicht mehr sinnvoll
# NaN

sqrt(-5)
# NaN

log(-5)
# NaN

z <- NaN
z
# NaN

2 * z
# NaN

Man sieht in dem Skript auch, dass man einer Variable den Wert NaN zuordnen und damit weiterrechnen kann — allerdings ergibt sich immer wieder NaN.

Not Available: NA

Mit den "Zahlen" Inf und NaN soll angezeigt werden, dass die Zahlenbereiche überschritten werden oder eine Berechnung nicht durchgeführt werden kann. Deutlich unterscheiden davon muss man NA (Not Available):

Wegen des letzten Punktes wird statt Not Available besser Missing Value gesagt.

Beispiel: Es wird eine Temperaturmessreihe aufgenommen. Dazu wird stündlich die Temperatur gemessen und die Messwerte werden in einem Vektor abgespeichert. Wie soll man einen fehlenden Messwert behandeln?

Man hat daher den Platzhalter NA für fehlende Daten geschaffen, der jeden beliebigen Datentyp ersetzen kann. Und es gibt zahlreiche Funktionen für die Weiterverarbeitung von Daten, die ein Argument besitzen, das besagt, ob NA-Werte berücksichtigt werden sollen oder nicht.

Als Beispiel hierfür sei die Funktion sum() gezeigt; mit ihr können beliebig viele Zahlen addiert werden und man kann das Argument na.rm setzen (rm = remove):

a <- 1
b <- 2
c <- NA

sum(a, b, c, na.rm = TRUE)
# 3

sum(a, b, c, na.rm = FALSE)
# NA

sum(a, b, c)
# NA

Man erkennt an diesem Beispiel:

Zeile 5 und 6: Für na.rm = TRUE werden nur die sinnvollen Werte berücksichtigt (die NA-Werte werden eliminiert).

Zeile 8 und 9: Belässt man NA in den zu addierenden Zahlen, so kann kein sinnvoller Wert für die Summe angegeben werden und es wird NA zurückgegeben.

Man kann das Ergebnis NA auch umgekehrt interpretieren: hat die Berechnung einer Summe NA ergeben, so muss mindestens ein Summand gleich NA gewesen sein.

Zeile 11 und 12: Der default-Wert für na.rm ist FALSE. Setzt man das Argument nicht, verbleiben die NA-Werte in der Summe.

Durch die bisherigen Erklärungen zu NA ist vermutlich der Eindruck entstanden, dass NA nur anstelle einer Zahl angegeben werden kann. Dies ist natürlich falsch: denn eine Messreihe kann aus logischen Werten oder Zeichen bestehen und auch dabei können fehlende Werte auftreten.

Wie in der Einführung schon gesagt wurde, wird NA wie eine logische Konstante behandelt und per default ist NA vom Modus "logical". Für andere fundamentale Datentypen ("character", "double", "integer") gibt es entsprechende Konstanten, die man aus dem folgenden Skript ablesen kann:

# default-Verhalten:
rm(x)
x <- NA

storage.mode(x)
# [1] "logical"
mode(x)
# [1] "logical"

# "character":
x <- NA_character_

mode(x)
# [1] "character"

# "double":
x <- NA_real_

mode(x)
# [1] "numeric"

storage.mode(x)
# [1] "double"

# "integer":
x <- NA_integer_

storage.mode(x)
# [1] "integer"

# Vektoren:
v <- c(5, NA)

mode(v)
# [1] "numeric"
storage.mode(v[2])
# [1] "double"

Man erkennt an diesem Skript:

Das letzte Skript erweckt den Eindruck als müsste man immer — je nachdem, welcher Datentyp gerade vorliegt, — die richtige Konstante für NA auswählen. In der Praxis ist dies nicht der Fall. Und um anzudeuten, warum man einfach NA verwenden kann, wurde das Beispiel mit dem Vektor v aufgenommen (Zeile 32 bis 37): Üblicherweise wird NA eingesetzt, um auf fehlende Werte in Messreihen hinzuweisen — wobei Messreihen meist als Vektoren dargestellt werden. In Zeile 32 wird ein Vektor mit den zwei Komponenten 5 und NA gebildet. Da 5 per default als double interpretiert wird, besitzt der gesamte Vektor den Modus "numeric". Und sowohl der gesamte Vektor als auch seine zweite Komponente den Speichermodus "double".

Man sieht an diesem Beispiel, dass es im Normalfall reicht, NA anzugeben und dass dies intern mit dem richtige Datentyp interpretiert wird.

Funktionen zum Testen auf Inf, NaN und NA (is-dot-Funktionen)

Es gibt zu jedem der Werte Inf, NaN und NA eine sogenannte is-dot-Funktion, die ein gegebenes Objekt x untersucht und bei Übereinstimmung den logischen Wert TRUE, andernfalls FALSE, zurückgibt:

a <- 1
b <- 2
c <- 3

x <- Inf
y <- NaN
z <- NA

is.finite(a)			# TRUE
is.finite(x)			# FALSE
is.finite(-x)			# FALSE
is.finite(y)			# FALSE
is.finite(z)			# FALSE

is.infinite(a)			# FALSE
is.infinite(x)			# TRUE
is.infinite(y)			# FALSE
is.infinite(z)			# FALSE

is.nan(b)				# FALSE
is.nan(y)				# TRUE
is.nan(z)				# FALSE

is.na(c)				# FALSE
is.na(x)				# FALSE
is.na(y)				# TRUE: Auch NaN wird wie ein fehlender Wert behandelt!
is.na(z)				# TRUE

Man beachte — auch wenn die Namen es suggerieren &mdsah;, dass die Funktionen is.finite(x) und is.infinite(x) nicht einfach die Verneinung voneinander sind.

An dieser Stelle mag es noch nicht einleuchten, warum diese Funktionen so wichtig sind: man kann sich den Wert einer Variable ausgeben lassen und sieht dann, ob er mit Inf, NaN beziehungsweise NA übereinstimmt. Dieses Argument greift zu kurz: Später wird man eigene Programme schreiben, die bei Auftreten eines dieser Ausnahme-Wertes geeignet reagieren müssen. Man wird dann den Ablauf des Programmes über die oben gezeigten Abfragen steuern.

Das Skript oben erweckt den Eindruck, dass die is-dot-Funktionen als Eingabewert ein Objekt von einem fundamentalen Datentyp besitzen und einen logischen Wert zurückgeben. Dies ist zwar nicht falsch, aber nicht allgemeingültig.

Im Allgemeinen ist der Eingabewert ein Vektor x, der dann komponentenweise untersucht wird. Der Rückgabewert ist ein logischer Vektor mit der Länge von x: es wird komponentenweise angegeben, ob ein spezieller Wert vorliegt.

Im folgenden Beispiel wird ein Vektor mit den zwei Komponenten 5 und NA gebildet (Zeile 1) und untersucht, ob er mit NA übereinstimmt (Zeile 3). Die Ausgabe (Zeile 4) ist ein Vektor mit zwei Komponenten:

v <- c(5, NA)

is.na(v)
# [1] FALSE  TRUE

NULL

Die NULL ist ein spezielles "Objekt", das anzeigen soll, dass kein Objekt — oder ein leeres Objekt — vorliegt, obwohl man vielleicht ein sinnvolles Objekt erwartet hat. Man sollte NULL aber von NA (Not Available) unterscheiden: Ein leeres Objekt soll anders gekennzeichnet werden als ein fehlendes Objekt; auf Letzteres kann zugegriffen werden wie auf das dort zu erwartende Objekt und es kann jederzeit mit einem sinnvollen Wert überschrieben werden.

Eine wichtige Anwendung von NULL sind Funktionen mit optionalen Argumenten: wird ein optionales Argument nicht gesetzt, wird es intern auf NULL gesetzt oder es wird — sofern vorgesehen — ein default-Wert verwendet.

Typische Beispiele für das Auftreten von NULL sind:

Typische Gegenbeispiele sind:

Um festzustellen, ob ein fragliches Objekt x tatsächlich nicht existiert, gibt es die Funktion is.null(). Das folgende Skript zeigt einige Tests auf NULL:

rm(x)
x
# Error: object 'x' not found
str(x)
# Error: object 'x' not found
is.null(x)
# Error: object 'x' not found

x <- 5
is.null(x)
# FALSE

z <- str(x)
# num 5
is.null(z)
# TRUE

v <- vector(mode = "double", length = 0)
is.null(v)
# FALSE
str(v)
# num(0)

attributes(v)
# NULL

Zeile 1 bis 7: Das Objekt x wird aus dem Arbeitsbereich gelöscht. Seine Ausgabe oder Verwendung in anderen Funktionen führen zu einer Fehlermeldung.

Zeile 9 - 11: x wird mit 5 initialisiert, es ist natürlich ungleich NULL.

Zeile 13 - 16: Die Funktion str() besitzt keinen Rückgabewert. Der Aufruf der Funktion erzeugt die Ausgabe in Zeile 14. Abspeichern des Rückgabewertes in der Variable z liefert NULL.

Zeile 18 - 22: Es wird ein double-Vektor v der Länge null erzeugt; er stimmt nicht mit NULL überein. Die Ausgabe von str(v) zeigt, dass er vom numerischen Typ und der Länge null ist.

Zeile 24 und 25: Für den Vektor wurden keine Attribute vereinbart, folglich liefert deren Ausgabe NULL.

Zusammenfassung

Zahlenbereiche

Die folgende Tabelle zeigt die wichtigsten Konstanten, die die Zahlenbereiche festlegen:

Wert Beschreibung
.Machine Liste, die zahlreiche Konstanten enthält (wie zum Beispiel Speicherplatz der numerischen Datentyp); wichtig sind insbesondere die folgenden Konstanten
double.xmin 2.225074e-308: (betragsmäßig) kleinste Gleitkommazahl in Normaldarstellung
double.xmax 1.797693e+308: größte Gleitkommazahl in Normaldarstellung; größere Zahlen werden mit Inf bezeichnet
integer.max 2 147 483 647: größte ganze Zahl im Speichermodus "integer"; größere Zahlen werden als NA (not available) bezeichnet

Besondere Werte

Wert Beschreibung
Inf Infinity: zeigt an, dass der Zahlenbereich von double überschritten wird
NaN Not a Number: zeigt an, dass eine Berechnung nicht durchgeführt werden kann
NA Not Available (oder Missing Value): zeigt an, dass ein Wert nicht verfügbar ist (zum Beispiel ein fehlender Wert in einer Messreihe)
NA_character_ Not Available (oder Missing Value) für Zeichen oder Zeichenketten (Modus "character")
NA_real_ Not Available (oder Missing Value) für Gleitkommazahlen (Modus "numeric", Speichermodus "double")
NA_integer_ Not Available (oder Missing Value) für ganze Zahlen (Modus "numeric", Speichermodus "integer")
NULL leeres Objekt (zum Beispiel der "Datentyp" einer Funktion ohne Rückgabewert)
?is.finite Weitere Informationen zu Inf und NaN im Paket base: unter is.finite
?NA Weitere Informationen zu NA im Paket base: unter NA
?NULL Weitere Informationen zu NULL im Paket base: unter NULL

Funktionen zum Testen auf spezielle Werte (is-dot-Funktionen)

Die folgende Tabelle zeigt die is-dot-Funktionen, die den Eingabewert x darauf testen, ob er mit Inf, NaN, NA beziehungsweise NULL übereinstimmt. Wie oben angedeutet wurde, ist der Eingabewert im Allgemeinen ein Vektor x, der komponentenweise untersucht wird. Rückgabewert ist ein logischer Vektor mit der Länge von x.

Funktion Beschreibung
is.finite(x) Ergibt TRUE für eine Zahl im gültigen Zahlenbereich, andernfalls FALSE; auch für Inf, NA und NaN wird FALSE zurückgegeben.
is.infinite(x) Ist nicht einfach die Verneinung von is.finite(x) : Liefert TRUE wenn ein gültiger Zahlenbereich überschritten wird; liefert FALSE für Zahlen im gültigen Zahlenbereich und für NA und NaN.
is.nan(x) Testet, ob ein numerischer Wert x mit NaN übereinstimmt; dieser Test sollte nicht mit == beziehungsweise identical() durchgeführt werden.
is.na(x) Testet, ob x mit NA übereinstimmt (dabei muss x keine Zahl sein); liefert TRUE für NaN, aber FALSE für Inf.
is.null(x) Liefert nur TRUE, falls x gleich NULL ist, sonst immer FALSE.