# Maschinenzahlen



```{julia}
for x ∈ ( 3, 3.3e4,  Int16(20), Float32(3.3e4), UInt16(9) )
    @show x sizeof(x) typeof(x)
    println()
end
```

## Ganze Zahlen *(integers)*

Ganze Zahlen werden grundsätzlich als Bitmuster fester Länge gespeichert. Damit ist der Wertebereich endlich. 
**Innerhalb dieses Wertebereichs** sind Addition, Subtraktion, Multiplikation und die Ganzzahldivision mit Rest 
exakte Operationen ohne Rundungsfehler. 

Ganze Zahlen gibt es in den zwei Sorten `Signed` (mit Vorzeichen) und `Unsigned`, die man als Maschinenmodelle  für ℤ bzw. ℕ auffassen kann.


### *Unsigned integers*
```{julia}
subtypes(Unsigned)
```
UInts sind Binärzahlen mit  n=8, 16, 32, 64 oder 128 Bits Länge und einem entsprechenden Wertebereich von
$$
0 \le x < 2^n 
$$
Sie werden im *scientific computing* eher selten verwendet. Bei hardwarenaher Programmierung dienen sie z.B. dem Umgang mit Binärdaten und Speicheradressen. Deshalb werden sie von Julia standardmäßig auch als Hexadezimalzahlen (mit Präfix `0x` und Ziffern `0-9a-f`) dargestellt.

```{julia}
x = 0x0033efef
@show x typeof(x) Int(x)

z = UInt(32)
@show z typeof(z);

```

### *Signed Integers* 
```{julia}
subtypes(Signed)
```
*Integers* haben den Wertebereich
$$
-2^{n-1} \le x < 2^{n-1}  
$$


In Julia sind ganze Zahlen standardmäßig 64 Bit groß:
```{julia}
x = 42
typeof(x)
```
Sie haben daher den Wertebereich: 
$$
-9.223.372.036.854.775.808 \le x \le 9.223.372.036.854.775.807
$$

Negative Zahlen werden im Zweierkomplement dargestellt:

$x \Rightarrow -x$ entspricht: _flip all bits, then add 1_

Das sieht so aus:

::: {.content-visible when-format="html"}
![Eine Darstellung des fiktiven Datentyps `Int4`](../images/Int4.png){width=50%}
:::

::: {.content-visible when-format="pdf"}
![Eine Darstellung des fiktiven Datentyps `Int4`](../images/Int4.png){width=50%}
:::

Das höchstwertige Bit zeigt das Vorzeichen an. Sein „Umkippen“ entspricht allerdings nicht der Operation `x → -x`.

:::{.callout-important}
Die gesamte Ganzzahlarithmetik ist eine __Arithmetik modulo $2^n$__

```{julia}
x = 2^62 - 10 + 2^62
```
```{julia}
x + 20
```
Keine Fehlermeldung, keine Warnung! Ganzzahlen fester Länge liegen nicht auf einer Geraden, sondern auf einem Kreis!

:::

Schauen wir uns ein paar *Integers* mal an:


```{julia}
using Printf

for i in (1, 2, 7, -7, 2^50, -1, Int16(7), Int8(7))
    s = bitstring(i)
    t = typeof(i)
    @printf "%20i %-5s ⇒  %s\n" i t s 
end 
```

... und noch etwas mehr *introspection*:


```{julia}
typemin(Int64), typemax(Int64)
```

```{julia}
typemin(UInt64), typemax(UInt64)
```

```{julia}
typemin(Int8), typemax(Int8)
```

## Arithmetik ganzer Zahlen

#### Addition, Multiplikation
Die Operationen `+`,`-`,`*` haben die übliche exakte Arithmetik **modulo $2^{64}$**.

#### Potenzen `a^b`

- Potenzen `a^n` werden für natürliche Exponenten `n` ebenfalls modulo $2^{64}$ exakt berechnet. 
- Für negative Exponenten ist das Ergebnis eine Gleitkommazahl.
- `0^0` ist [selbstverständlich](https://en.wikipedia.org/wiki/Zero_to_the_power_of_zero#cite_note-T4n3B-4) gleich 1.
```{julia}
(-2)^63, 2^64, 3^(-3), 0^0 
```
- Für natürliche Exponenten wird [*exponentiation by squaring*](https://de.wikipedia.org/wiki/Bin%C3%A4re_Exponentiation) verwendet, so dass z.B. `x^23` nur 7 Multiplikationen benötigt: 
$$
x^{23} = \left( \left( (x^2)^2 \cdot x \right)^2  \cdot x \right)^2  \cdot x 
$$

#### Division

- Division `/` erzeugt  eine Gleitkommazahl.

```{julia}
x = 40/5
```
#### Ganzzahldivision und Rest

- Die Funktionen `div(a,b)`,  `rem(a,b)`  und `divrem(a,b)` berechnen den Quotient der Ganzzahldivision, den dazugehörigen Rest *(remainder)* bzw. beides als Tupel.  
- Für `div(a,b)` gibt es die Operatorform `a ÷ b` (Eingabe: `\div<TAB>`) und für `rem(a,b)` die Operatorform `a % b`.  
- Standardmäßig wird bei der Division „zur Null hin gerundet“, wodurch der dazugehörige Rest dasselbe Vorzeichen wie der Dividend `a` trägt:  

```{julia}
@show divrem( 27, 4)
@show ( 27 ÷  4,  27 %  4)   
@show (-27 ÷  4, -27 %  4 )
@show ( 27 ÷ -4,  27 % -4);
```

- Eine von `RoundToZero` abweichende  Rundungsregel kann bei den Funktionen als optionales 3. Argument angegeben werden. 
- `?RoundingMode` zeigt die möglichen Rundungsregeln an. 
- Für die Rundungsregel `RoundDown` („in Richtung  minus unendlich"), wodurch der dazugehörige Rest dasselbe Vorzeichen wie der Divisor `b` bekommt, gibt es auch die Funktionen `fld(a,b)` *(floored division)* und `mod(a,b)`:

```{julia}
@show divrem(-27, 4, RoundDown)
@show (fld(-27,  4), mod(-27,  4))
@show (fld( 27, -4), mod( 27, -4));
``` 

Für alle Rundungsregeln gilt:
```
div(a, b, RoundingMode) * b + rem(a, b, RoundingMode) = a
```

#### Der Datentyp `BigInt`

Der Datentyp `BigInt` ermöglicht Ganzzahlen beliebiger Länge. Der benötigte Speicher wird dynamisch allokiert.

Numerische Konstanten haben automatisch einen ausreichend großen Typ:

```{julia}
z = 10
@show typeof(z)
z = 10_000_000_000_000_000    # 10 Billiarden
@show typeof(z)
z = 10_000_000_000_000_000_000  # 10 Trillionen
@show typeof(z)
z = 10_000_000_000_000_000_000_000_000_000_000_000_000_000   # 10 Sextilliarden
@show typeof(z);
```

Meist wird man allerdings den Datentyp `BigInt` explizit anfordern müssen, damit nicht modulo $2^{64}$ gerechnet wird:

```{julia}
@show 3^300        BigInt(3)^300;
```

*Arbitrary precision arithmetic* kostet einiges an Speicherplatz und Rechenzeit.

Wir vergleichen den Zeit- und Speicherbedarf bei der Aufsummation von 10 Millionen Ganzzahlen als `Int64` und als `BigInt`. 

```{julia}
# 10^7 Zufallszahlen, gleichverteilt zwischen -10^7 und 10^7 
vec_int = rand(-10^7:10^7, 10^7)

# Dieselben Zahlen als BigInts
vec_bigint = BigInt.(vec_int)
```

Einen ersten Eindruck vom Zeit- und Speicherbedarf gibt das `@time`-Macro:

```{julia}
@time x = sum(vec_int)
@show x typeof(x)
```
```{julia}
@time x = sum(vec_bigint)
@show x typeof(x);
```

Durch die Just-in-Time-Compilation von Julia ist die einmalige Ausführung einer Funktion wenig aussagekräftig. Das Paket `BenchmarkTools` stellt u.a. das Macro `@benchmark` bereit, das eine Funktion mehrfach aufruft und die Ausführungszeiten als Histogramm darstellt.

:::{.ansitight}
```{julia}
using BenchmarkTools

@benchmark sum($vec_int)
```


```{julia}
@benchmark sum($vec_bigint)
```
:::
Die `BigInt`-Addition ist mehr als  30 mal langsamer. 



:::{.content-hidden unless-format="xxx"}
Die folgende Funktion soll die Summe aller Zahlen von 1 bis n mit der Arithmetik des Datentyps T berechnen.
Auf Grund der *type promotion rules* reicht es für `T ≥ Int64` dazu aus, die Akkumulatorvariable mit einer Zahl vom Typ T zu initialisieren.   
```{julia}
function mysum(n, T)
    s = T(0)
    for i = 1:n
        s += i    
    end
    return s
end
```

Einen ersten Eindruck vom Zeit- und Speicherbedarf gibt das `@time`-Macro:

```{julia}
@time x = mysum(10_000_000, Int64)
@show x typeof(x);
```

```{julia}
@time x = mysum(10_000_000, BigInt)
@show x typeof(x);
```

Durch die Just-in-Time-Compilation von Julia ist die einmalige Ausführung einer Funktion wenig aussagekräftig. Das Paket `BenchmarkTools` stellt u.a. das Macro `@benchmark` bereit, das eine Funktion mehrfach aufruft und die Ausführungszeiten als Histogramm darstellt.

:::{.ansitight}
```{julia}
using BenchmarkTools

@benchmark mysum(10_000_000, Int64)
```


```{julia .myclass}

@benchmark mysum(10_000_000, BigInt)
```


Die Berechnung von $\sum_{n=1}^{10000000} n$  dauert auf meinem PC mit den Standard-64bit-Integern im Schnitt 2 Nanosekunden, in *arbitrary precision arithmetic* über eine Sekunde, wobei auch noch fast 500MB Speicher allokiert werden. 

:::
:::


## Gleitkommazahlen

Aus _floating point numbers_  kann man im Deutschen  **[Gleit|Fließ]--[Komma|Punkt]--Zahlen** machen 
und tatsächlich kommen alle 4 Varianten in der Literatur vor. 

In der Numerischen Mathematik spricht man auch gerne von **Maschinenzahlen**.


### Grundidee 

- Eine „feste Anzahl von Vor- und Nachkommastellen“ ist für viele Probleme ungeeignet.
- Eine Trennung zwischen „gültigen Ziffern“ und Größenordnung wie in der wissenschaftlichen Notation
    $$ 345.2467 \times 10^3\qquad 34.52467\times 10^4\qquad \underline{3.452467\times 10^5}\qquad 0.3452467\times 10^6\qquad 0.03452467\times 10^7$$ 
  ist wesentlich flexibler. 
- Zur Eindeutigkeit legt man fest: Die  Darstellung mit genau einer Ziffer vor dem Komma ist die __Normalisierte Darstellung__.
- Bei Binärzahlen `1.01101`: ist diese Ziffer  immer gleich Eins und man kann auf das Abspeichern dieser Ziffer verzichten.


:::{.callout-note }
 
## Maschinenzahlen

Die Menge der Maschinenzahlen  $𝕄(b, m, e_{min}, e_{max})$ ist charakterisiert durch die verwendete Basis $b$,  die Mantissenlänge $m$ und den Wertebereich des Exponenten $\{e_{min}, ... ,e_{max}\}$.   

In unserer Konvention hat die Mantisse einer normalisierten Maschinenzahl eine Ziffer (der Basis $b$) ungleich Null vor dem Komma und $m-1$ Nachkommastellen. 

Wenn $b=2$ ist, braucht man daher nur $m-1$ Bits zur Speicherung der Mantisse normalisierter Gleitkommazahlen. 

Der Standard  IEEE 754, der von der Mahrzahl der modernen Prozessoren und Programmiersprachen implementiert wird,   definiert 

- `Float32` als $𝕄(2, 24, -126, 127 )$ und 
- `Float64` als $𝕄(2, 53, -1022, 1023 ).$

:::

### Aufbau von `Float64` nach [Standard IEEE 754](https://de.wikipedia.org/wiki/IEEE_754)


::: {.content-visible when-format="html"}
![Aufbau einer `Float64` (Quelle:^[Quelle: <a href="https://commons.wikimedia.org/wiki/File:IEEE_754_Double_Floating_Point_Format.svg">Codekaizen</a>, <a href="https://creativecommons.org/licenses/by-sa/4.0">CC BY-SA 4.0</a>, via Wikimedia Commons ])
](../images/1024px-IEEE_754_Double_Floating_Point_Format.png)
:::

::: {.content-visible when-format="pdf"}
![Aufbau einer `Float64`  \mysmall{(Quelle: \href{https://commons.wikimedia.org/wiki/File:IEEE_754_Double_Floating_Point_Format.svg}{Codekaizen}, \href{https://creativecommons.org/licenses/by-sa/4.0}{CC BY-SA 4.0}, via Wikimedia Commons)}
](../images/1024px-IEEE_754_Double_Floating_Point_Format.png){width="70%"}
:::


- 1 Vorzeichenbit $S$
- 11 Bits für den Exponenten, also $0\le E \le 2047$
- die Werte $E=0$ und $E=(11111111111)_2=2047$ sind reserviert für die Codierung von Spezialwerten wie
$\pm0, \pm\infty$, NaN _(Not a Number)_ und denormalisierte Zahlen.
- 52 Bits für die Mantisse $M,\quad 0\le M<1$, das entspricht etwa 16 Dezimalstellen
- Damit wird folgende Zahl dargestellt:
$$ x=(-1)^S \cdot(1+M)\cdot 2^{E-1023}$$

Ein Beispiel:
```{julia}
x = 27.56640625
bitstring(x)
```
Das geht auch schöner:

```{julia}
function printbitsf64(x::Float64)
    s = bitstring(x)
    printstyled(s[1], color = :blue, reverse=true)
    printstyled(s[2:12], color = :green, reverse=true)
    printstyled(s[13:end], color=:red, bold=true, reverse=true)
    print("\n")
end

printbitsf64(x)
```
und wir können S (in blau),E (grün) und M (rot)  ablesen:
$$
\begin{aligned}
S &= 0\\
E &= (10000000011)_2 = 2^{10} + 2^1 + 2^0 = 1027\\
M &= (.1011100100001)_2 =  \frac{1}{2} + \frac{1}{2^3} + \frac{1}{2^4} + \frac{1}{2^5} + \frac{1}{2^8} + \frac{1}{2^{12}}\\
x &=(-1)^S \cdot(1+M)\cdot 2^{E-1023}
\end{aligned}
$$


... und damit die Zahl rekonstruieren:

```{julia}
x = (1 + 1/2 + 1/8 + 1/16 + 1/32 + 1/256 + 1/4096) * 2^4
```

- Die Maschinenzahlen 𝕄 bilden eine endliche, diskrete Untermenge von ℝ. Es gibt eine kleinste und eine größte Maschinenzahl und abgesehen davon haben alle x∈𝕄 einen Vorgänger und Nachfolger in 𝕄.
- Was ist der Nachfolger von x in 𝕄? Dazu setzen wir  das kleinste Mantissenbit von 0 auf 1. 
- Die Umwandlung eines Strings aus Nullen und Einsen in die entsprechende Maschinenzahl ist z.B. so möglich:


```{julia}
ux = parse(UInt64, "0100000000111011100100010000000000000000000000000000000000000001", base=2)
reinterpret(Float64, ux)
```

Das kann man in Julia allerdings auch einfacher haben:

```{julia}
y = nextfloat(x)
```

Der Vorgänger von x ist:

```{julia}
z = prevfloat(x)
println(z)
printbitsf64(z)
```


## Maschinenepsilon

- Den Abstand zwischen `1` und dem Nachfolger `nextfloat(1)` nennt man [**Maschinenepsilon**](https://en.wikipedia.org/wiki/Machine_epsilon). 
- Für `Float64` mit einer Mantissenlänge von 52 Bit, ist  $\epsilon=2^{-52}$.

```{julia}
@show nextfloat(1.) - 1   2^-52   eps(Float64);
```

- Das Maschinenepsilon ist ein Maß für den relativen Abstand zwischen den Maschinenzahlen und quantifiziert die Aussage: „64-Bit-Gleitkommazahlen haben eine Genauigkeit von etwa 16 Dezimalstellen.“
- Das Maschinenepsilon ist etwas völlig anderes  als die kleinste von Null verschiedene Gleitkommazahl:

```{julia}
floatmin(Float64)
```

- Ein Teil der Literatur verwendet eine andere Definition des Maschinenepsilons, die halb so groß ist. 
$$
\epsilon' = \frac{\epsilon}{2}\approx 1.1\times 10^{-16}
$$ 
ist der maximale relative Fehler, der beim Runden einer reellen Zahl auf die nächste Maschinenzahl entstehen kann.
- Da  Zahlen aus dem Intervall $(1-\epsilon',1+\epsilon']$ auf die Maschinenzahl $1$ gerundet werden, kann man $\epsilon'$ auch definieren als: *die größte Zahl, für die in der Maschinenzahlarithmetik noch gilt: $1+\epsilon' = 1$.*  

Auf diese Weise kann man das Maschinenepsilon auch berechnen:

:::{.ansitight}

```{julia}
Eps = 1
while(1 != 1 + Eps)
    Eps /= 2
    println(1+Eps)
end
Eps
```

oder als Bitmuster:

```{julia}
Eps=1
while 1 != 1 + Eps
    Eps/= 2
    printbitsf64(1+Eps)
end
Eps
```

:::

Die größte und die kleinste positive normalisiert darstellbare Gleitkommazahl eines Gleitkommatyps kann man abfragen:

```{julia}
@show floatmax(Float64)
printbitsf64(floatmax(Float64))

@show floatmin(Float64)
printbitsf64(floatmin(Float64))
```





## Runden auf Maschinenzahlen

- Die Abbildung rd: ℝ $\rightarrow$  𝕄 soll zur nächstgelegenen darstellbaren Zahl runden. 
- Standardrundungsregel: _round to nearest, ties to even_   
  Wenn man genau die Mitte zwischen zwei Maschinenzahlen trifft *(tie)*, wählt man die, deren letztes Mantissenbit 0 ist. 
- Begründung: damit wird statistisch in 50% der Fälle auf- und in 50% der Fälle abgerundet und  so ein „statistischer Drift“ bei längeren Rechnungen vermieden.   

##  Maschinenzahlarithmetik

Die Maschinenzahlen sind als Untermenge von ℝ nicht algebraisch abgeschlossen. Schon die Summe zweier Maschinenzahlen wird in der Regel keine Maschinenzahl sein. 

:::{.callout-important}
Der Standard IEEE 754 fordert, dass die Maschinenzahlarithmetik das *gerundete exakte Ergebnis* liefert:

Das Resultat muss gleich demjenigen sein, das bei einer exakten Ausführung der entsprechenden Operation mit anschließender Rundung entsteht.
$$
 a \oplus b = \text{rd}(a + b)
$$ 
Analoges muss für die Implemetierung der Standardfunktionen wie 
wie `sqrt()`, `log()`, `sin()` ... gelten: Sie liefern ebenfalls die Maschinenzahl, die dem exakten Ergebnis am nächsten kommt.
:::


Die Arithmetik ist *nicht assoziativ*:

```{julia}
1 +  10^-16 + 10^-16 
```

```{julia}
1 + (10^-16 + 10^-16)
```

Im ersten Fall (ohne Klammern) wird von links nach rechts ausgewertet:
$$
\begin{aligned}
1 \oplus 10^{-16} \oplus 10^{-16} &=
 (1 \oplus 10^{-16}) \oplus 10^{-16} \\  &= 
\text{rd}\left(\text{rd}(1 + 10^{-16}) + 10^{-16} \right)\\
&= \text{rd}(1 + 10^{-16})\\
&= 1
\end{aligned}
$$

Im zweiten Fall erhält man:
$$
\begin{aligned}
1 \oplus \left(10^{-16} \oplus 10^{-16}\right) &=
\text{rd}\left(1 + \text{rd}(10^{-16} + 10^{-16}) \right)\\
&= \text{rd}(1 + 2*10^{-16})\\
&= 1.0000000000000002
\end{aligned}
$$


Es sei auch daran erinnert, dass sich selbst „einfache“ Dezimalbrüche häufig nicht exakt als Maschinenzahlen darstellen lassen:

$$
\begin{aligned}
(0.1)_{10} &= (0.000110011001100110011001100...)_2 = (0.000\overline{1100})_2 \\
 (0.3)_{10} &= (0.0100110011001100110011001100..)_2 = (0.0\overline{1001})_2 
 \end{aligned}
 $$


```{julia}
printbitsf64(0.1)
printbitsf64(0.3)
```

Folge:

```{julia}
0.1 + 0.1 == 0.2
```

```{julia}
0.2 + 0.1 == 0.3
```

```{julia}
0.2 + 0.1
```

Bei der Ausgabe einer Maschinenzahl muss der Binärbruch in einen Dezimalbruch entwickelt werden. Man kann sich auch mehr Stellen dieser Dezimalbruchentwicklung anzeigen lassen: 
```{julia}
using Printf
@printf("%.30f", 0.1)
```

```{julia}
@printf("%.30f", 0.3)
```
Die Binärbruch-Mantisse einer Maschinenzahl kann eine lange oder sogar unendlich-periodische Dezimalbruchentwicklung haben. Dadurch 
sollte man sich nicht  eine „höheren Genauigkeit“ suggerieren lassen! 

:::{.callout-important}
Moral: wenn man `Float`s auf Gleichheit testen will, sollte man fast immer eine dem Problem angemessene realistische Genauigkeit `epsilon` festlegen und 
darauf testen:

```julia
epsilon = 1.e-10

if abs(x-y) < epsilon
   # ...
end
```
:::

## Normalisierte und Denormalisierte Maschinenzahlen

Zum Verständnis nehmen wir ein einfaches Modell: 

- Sei 𝕄(10,4,±5)  die Menge der Maschinenzahlen zur Basis 10 mit 4 Mantissenstellen (eine vor dem Komma, 3 Nachkommastellen) und dem Exponentenbereich -5 ≤ E ≤ 5.
- Dann ist die normalisierte Darstellung (Vorkommastelle ungleich 0)
  von z.B. 1234.0  gleich  1.234e3 und von  0.00789 gleich 7.890e-3.
- Es ist wichtig, dass die Maschinenzahlen bei jedem Rechenschritt normalisiert gehalten werden. Nur so wird die Mantissenlänge voll ausgenutzt und die Genauigkeit ist maximal.
- Die kleinste positive normalisierte Zahl in unserem Modell ist `x = 1.000e-5`. Schon `x/2` müsste auf 0 gerundet werden. 
- Hier erweist es sich für viele Anwendungen als günstiger, auch denormalisierte *(subnormal)* Zahlen zuzulassen und `x/2` als `0.500e-5` oder `x/20` als `0.050e-5` darzustellen. 
- Dieser *gradual underflow* ist natürlich mit einem Verlust an gültigen Stellen und damit Genauigkeit verbunden.


Im `Float`-Datentyp werden solche *subnormal values* dargestellt durch ein Exponentenfeld, in dem alle Bits gleich Null sind:

```{julia}
#| echo: false
#| output: false
flush(stdout)
```


:::{.ansitight}
```{julia}
using Printf

x = 2 * floatmin(Float64)   # 2*kleinste normalisierte Gleitkommazahl > 0

while x != 0
    x /= 2
    @printf "%14.6g  " x
    printbitsf64(x)
end
```
:::

## Spezielle Werte

Die Gleitkommaarithmetik kennt einige spezielle Werte:

```{julia}
for x ∈ (NaN, Inf, -Inf, -0.0)
    @printf "%6g  " x
    printbitsf64(x)
end
```

- Ein Exponentenüberlauf *(overflow)* führt zum Ergebnis `Inf` oder `-Inf`.
```{julia}
2/0, -3/0, floatmax(Float64) * 1.01, exp(1300)
```
- Damit kann weitergerechnet werden:

```{julia}
-Inf + 20, Inf/30, 23/-Inf, sqrt(Inf),  Inf * 0, Inf - Inf
```
- `NaN` *(Not a Number)* steht für das Resultat einer Operation, das undefiniert ist. Alle weiteren Operationen mit `NaN` ergeben ebenfalls `NaN`.

```{julia}
0/0, Inf - Inf, 2.3NaN, sqrt(NaN)
```   

- Da `NaN` einen undefinierten Wert repräsentiert, ist es zu nichts gleich, nichtmal zu sich selbst. Das ist sinnvoll, denn wenn zwei Variablen `x` und `y` als `NaN` berechnet wurden, sollte man nicht schlußfolgern, dass sie gleich sind. 
- Zum Testen auf `NaN` gibt es daher die boolsche Funktion `isnan()`.

```{julia}
x = 0/0
y = Inf - Inf
@show x==y   NaN==NaN  isfinite(NaN) isinf(NaN) isnan(x) isnan(y);  
``` 

- Es gibt eine „minus Null“. Sie signalisiert einen Exponentenunterlauf *(underflow)* einer betragsmäßig zu klein gewordenen *negativen* Größe. 

```{julia}
@show 23/-Inf   -2/exp(1200)    -0.0==0.0;
```

## Mathematische Funktionen

Julia verfügt über die [üblichen mathematischen Funktionen](https://docs.julialang.org/en/v1/manual/mathematical-operations/#Rounding-functions)  
`sqrt, exp, log, log2, log10, sin, cos,..., asin, acos,..., sinh,..., gcd, lcm, factorial,...,abs, max, min,...`,

darunter z.B. die [Rundungsfunktionen](https://de.wikipedia.org/wiki/Abrundungsfunktion_und_Aufrundungsfunktion)

- `floor(T,x)` = $\lfloor x \rfloor$
- `ceil(T,x)`  = $\lceil x \rceil$

```{julia}
floor(3.4),   floor(Int64, 3.5),   floor(Int64, -3.5)
```

```{julia}
ceil(3.4),    ceil(Int64, 3.5),    ceil(Int64, -3.5)
```

Es sei noch hingewiesen auf `atan(y, x)`, den [Arkustangens mit 2 Argumenten](https://de.wikipedia.org/wiki/Arctan2), Er ist in anderen Programmiersprachen oft als Funktion mit eigenem Namen *atan2* implementiert. 
Dieser löst das Problem der Umrechnung von kartesischen in Polarkoordinaten ohne lästige Fallunterscheidung. 

- `atan(y,x)`  ist Winkel der Polarkoordinaten von (x,y) im Intervall $(-\pi,\pi]$. Im 1. und 4. Quadranten ist er also gleich `atan(y/x)`

```{julia}
atan(3, -2),    atan(-3, 2),    atan(-3/2)
```


##  Umwandlung  Strings $\Longleftrightarrow$ Zahlen

Die Umwandlung ist mit den  Funktionen `parse()` und `string()` möglich.

```{julia}
parse(Int64, "1101", base=2)
```

```{julia}
string(13, base=2)
```

```{julia}
string(1/7)
```

```{julia}
string(77, base=16)
```

Zur Umwandlung der numerischen Typen ineinander kann man die Typnamen verwenden. Typenamen sind auch Konstruktoren:


```{julia}
x = Int8(67)
@show x   typeof(x);
```

```{julia}
z = UInt64(3459)
```


```{julia}
y = Float64(z)
```



## Literatur

- D. Goldberg, [What Every Computer Scientist Should Know About Floating-Point Arithmetic](http://www.validlab.com/goldberg/paper.pdf)
- C. Vuik, [Some Disasters caused by numerical errors](http://ta.twi.tudelft.nl/users/vuik/wi211/disasters.html)
