Eigentümlichkeiten

􊨖lgola und die Typographie

Die Zeiten haben sich geändert: Anstatt mit Terminals zu arbeiten, die nur Zeilen von meist achtzig diktengleichen Zeichen zuließen, kann man sich heutzutage Rechner bedienen, deren Monitore eine hervorragende Auflösung haben. Deshalb kann man nicht nur auf die diktengleiche Schrift verzichten, sondern auch Schriften mit Serifen nutzen, ohne dadurch die Lesbarkeit zu mindern.

Für das Arbeiten mit 􊨖lgola wird deshalb die 􊨖lfons Font Familie zur Verfügung gestellt, die eine einheitliche Darstellung von 􊨖lgola-Quellen erlaubt. Damit soll insbesondere auch gewährleistet werden, dass jede 􊨖lgola-Quelle unabhängig vom Betriebssystem exakt gleich dargestellt werden kann.

Leerzeichen und Formatierung

Was in anderen Programmiersprachen oft ignoriert wird, spielt unter 􊨖lgola eine Große Rolle: das Leerzeichen. Im Gegensatz zu vielen Programmiersprachen ist 􊨖lgola also nicht ganz „formatfrei“. Das führt dazu, dass etwas unter Umständen mit einer Warnung oder gar einer Fehlermeldung geahndet wird, was in anderen Programmiersprachen egal ist.

So dürfen etwa vor Satzzeichen – wie es auch bei normalen Texten üblich ist – keine Leerzeichen stehen und nach einem Komma oder Semikolon muss ein Leerzeichen oder Zeilenumbruch folgen.

Leerzeichen

Unter 􊨖lgola stehen unterschiedlich große Leerzeichen zur Verfügung. Damit soll es insbesondere den Technikern sowohl im Quelltext als auch in den Kommentaren möglich sein, Formeln gut aussehend zu schreiben. Dazu gehört etwa das kleine Leerzeichen, mit sich lange Zahlen sinnvoll gruppieren lassen. Wenn einem die IDE diese Aufgabe nicht automatisch beim Tippen abnimmt, muss peinlich genau auf deren korrekten Einsatz geachtet werden.

assert 1 234 567 890 = 12345 67890
assert 1 000 000 000 = 1.0 × 10⁹

Leerzeichen spielen aber nicht nur optisch sondern ggf. auch semantisch eine Rolle. Das ist insbesondere dann wichtig, wenn es um die Prezedenz von Teilausdrücken geht. So ist es möglich, wie in der Mathematik und Informatik oft üblich, den Operator einer Multiplikation durch eine Juxtaposition zu ersetzen. Hierbei taucht allerdings das Problem der Auswertereihenfolge auf, das durch die Angabe von Leerzeichen eindeutig aufgelöst werden kann.

assert (x + 1) (x + 2)/(x + 3) (x + 4) ≠ (x + 1)(x + 2) / (x + 3)(x + 4)
assert (x + 1)(x + 2) / (x + 3)(x + 4) = ((x + 1)(x + 2)) / ((x + 3)(x + 4))
assert (x + 1) (x + 2)/(x + 3) (x + 4) = (x + 1) ((x + 2) / (x + 3)) (x + 4)

Tabulatoren

Neben dem Einrücken von Blöcken erlauben die Tabulatoren, wie es deren Name eigentlich vermuten lässt, eine tabellarische Darstellung von Text. Das bedeutet, dass mit Hilfe von Tabulatoren Anweisungen und Ausdrücke sinnvoll ausgerichtet werden können und so Zusammenhänge einfacher zu erkennen sind. Dabei ist in einer 􊨖lgola-Umgebung tasächlich jeweils nur ein Tabulatur nötig um den nachfolgenden Text auszurichten.

a:← veryLongFunctionName(1)Langer Funktionsname
very_long_name:← f(2)Langer Variablenname

Operatoren

Verschieben von Bits

Während die Funktionen shiftLeft und shiftRight noch einergermaßen sebsterklärend sind, sind es die dazugehörigen Operatoren << bzw. >>, wie sie in C-basierten Sprachen üblich sind, schon nicht mehr. Unter 􊨖lgola wurden diese durch die typographisch etwas ansprechenderen Symbole ⪡ bzw. ⪢ eingetauscht.

Bezogen darauf, dass ein Verschieben der Multiplikation bzw. Division mit der korrespondierenden Zweierpotenz gleichgesetzt ist, ergibt sich hier leider ein kleines Problem. Die Operatoren ⪡ und ⪢ gehören nämlich zu den relationalen Operatoren, was Gelegentlich zu Irritierungen führen kann, denn leider gilt

ab + ca ⋅ 2b + c

Die relationalen Operatoren werden nämlich nachrangig zu dem Additionsoperatoren ausgeführt:

ab + c   =   a ⪡ (b + c)

Damit dieses subtile Problem nicht auftreten können, bietet 􊨖lgola zusätzlich die Operatoren ⋉ und ⋊ für das Verschieben nach links bzw. rechts an.

xk := a ⋅ 2k,    ak := a ÷ 2k

Und damit kann auch die gewünschte Auswertereihenfolge gewährleistet werden.

ab + c = a ⋅ 2b + c,    ab + c = a ÷ 2b + c

Die Operatoren ⋉ und ⋊ sind nicht assoziativ, so dass der Übersetzer bei Ausdrücken wie abc wie auch bei der Division immer Klammern erzwingt.

Division

Bei der ganzzahligen Division entsteht das Problem der Rundung. Besonders bei den modernen Prozessoren, die meistens gegen 0 runden, tritt der unschöne Effekt auf, dass um den Nullpunkt eine Unregelmäßigkeit auftritt. Unter 􊨖lgola wird das Problem dadurch gelöst, dass für die unterschiedlichen Anwendungsfälle jeweils eine eigene Division mit passender Bestimmung der Divisionsrests definiert wird.

Der Standard unter 􊨖lgola ist eine Division, bei der das korrekte, eventuell nicht ganzzahlige Ergebnis gegen −∞ gerundet wird. Darüber hinaus werden noch Versionen angeboten, die gegen +∞, zur nächsten (im Zweifelsfall geraden) Zahl und schließlich auch gegen 0 runden. Zusätzlich wird noch die sogenannte euklidische Division angeboten, die sehr schöne Eigenschaften im Zusammenhang mit Zweierpotenzen hat. Mit Ausnahme der Division mit Rundung gegen 0 sind alle Divisionsvarianten auch um den Nullpunkt und über die negativen Zahlen hinweg regelmäßig.

VarianteRestEffektRundung gegen
quot(x, y),   x ÷ ymod(x, y)x / y−∞
group(x, y)pad(x, y)x / y+∞
ratio(x, y)residue(x, y)round(x / y)nächste (im Zweifel gerade) Ganzzahl
ediv(x, y),   xyemod(x, y)sgn(y) ⋅ ⌊x / |y|⌋−sgn(y) ⋅ ∞
tdiv(x, y)trem(x, y)trunc(x / y)0

So lässt sich quot dazu benutzen, um etwa nummerierte Elemente einer Gruppe zuzuordnen. Wieviele solcher Gruppen nötig sind, lässt sich dann mit group bestimmen. Mit pad lässt sich dann berechnen, wieviele Elemente nötig sind, um die letzte Gruppe mit y Elementen „voll“ zu machen.

Die euklidische Variante ediv ist wie angedeutet deswegen so interessant, weil sie zwar – wie auch quot – für eine Zweierpotenz y = 2k das selbe Ergebnis liefert wie das korrespondierende Bit-weise Verschieben um k Stellen nach rechts; sich aber darüber hinaus emod über ein Bit-weises Und mit einer auf der Zweierpotenz basierenden Maske implementieren lässt:

ediv(x, 2k)=xk
emod(x, 2k)=x ⊙ (2k − 1)

Für das Runden gegen 0, das hier nur der Vollständigkeit halber aufgeführt wird, ergeben sich – wegen der Asymmetrie um den Nullpunkt – leider keinerlei brauchbare Eigenschaften; die Funktionen sind nur für Hardware-nahes Programmieren vorhanden, um bei Bedarf einen direkten Zugang zur Hardware zu ermöglichen.

Name(x, y) ↦ …(+5, +2)(−5, +2)(+5, −2)(−5, −2)
. / .x/y2,5−2,5−2,52,5
quotx/y2−3−32
modx − (⌊x/y⌋ ⋅ y)11−1−1
groupx/y3−2−23
pad(⌈x/y⌉ ⋅ y) − x11−1−1
ratiornd(x/y)2−2−22
residuex − (rnd(x/y) ⋅ y)1−11−1
edivsgn(y) ⋅ ⌊x/|y|⌋2−3−23
emodx − (⌊x/y⌋ ⋅ y)∣1111
tdivtr(x/y)2−2−22
tremx − (tr(x/y) ⋅ y)1−11−1

Zahlen

Das Bemühen, bestimmte Sachverhalte möglichst genau ausdrücken zu können, erstreckt sich auch für Zahlen. Dabei spielen natürlich die Fest- und Gleitkommazahlen mit ihren Problemen eine besondere Rolle.

Exakte Zahlen

Sieht man eine Zahl, so lässt sich dieser oft nicht ansehen, ob diese exakt ist. Beispielsweise ist bei einer Umrechnung von Zoll nach Zentimetern nicht klar, ob die 2,54 cm/in genau oder möglicherweise nur für die aktuelle Berechnung genau genug sind. Desgleichen gilt für die Lichtgeschwindigkeit, wenn diese mit 299 792 458 m/s angegeben wird.

CM_PER_INCH := 2.5𝟒Einheit: cm/in.
SPEED_OF_LIGHT := 299 792 45𝟖Einheit: m/s.

Wird unter 􊨖lgola die letzte Ziffer einer Zahl fett notiert, dann bedeutet dies, dass diese exakt ist. So ist dem Leser sofort klar, dass ein Zoll exakt 2,54 cm lang und die Lichtgeschwindigkeit tatsächlich – nämlich definitionsgemäß – 299 792 458 m/s ist.

Darüber hinaus verhindert der Übersetzer, dass eine exakte Zahl zugewiesen wird, wenn das nicht die gewünschte Genauigkeit hat.

x : 𝔽₁₂₈ ← 0.𝟏↯ Kann mit Basis 2 nicht exakt dargestellt werden.
d : 𝔻₃₂ ← 0.𝟏Funktioniert aber mit Basis 10.

Ungenaue Zahlen

Das Behandeln von Zahlen in einem Rechner ist wegen dessen Endlichkeit zwangsläufig ein Problem und der Umgang mit irrationalen Zahlen ist deswegen naturgemäß schwierig. Um wenigstens andeuten zu können, dass es sich um eine Zahl handelt, die, so wie sie geschrieben steht, genauer sein könnte, gibt es unter 􊨖lgola die Fortsetzungspunkte.

π := 3.14159265…Genau genug für 𝔽₃₂ aber nicht für 𝔽₆₄.
e := 2.718281828…Genau genug für 𝔽₃₂ aber nicht für 𝔽₆₄.

Diese Schreibweise mit den Fortsetzungspunkten gibt dem Übersetzer die Möglichkeit, abhängig vom Kontext bestimmen zu können, ob die gegebene Genauigkeit ausreichend ist oder ob eine diesbezügliche Warnung ausgegeben werden muss. Obige Definitionen von π und e sind dementsprechend für die Verwendung als 𝔽₁₆ oder 𝔽₃₂ ausreichend, führen aber zu einer Warnung, wenn diese im Kontext eines 𝔽₆₄, 𝔽₁₂₈ oder vergleichbarem Format genutzt werden sollen.

π : 𝔽₁₆ = 3.141592653Genauigkeit von 𝔽₁₆ eigentlich nicht ausreichend.

Die Schreibweise ist auch tatsächlich nötig, weil es – wie auch bei den ganzen Zahlen – nicht möglich ist, eine Zuweisung zu machen, die zu einem Informationsverlust führt.

Gerundete Zahlen

Müssen Zahlen für die verfügbare Genauigkeit passend gemacht werden, so müssen sie gerundet werden. Gelegentlich ist es essenziell zu wissen, ob eine zu rundende Zahl bereits gerundet wurde und wie das deren Darstellung beeinflusst. Soll beispielsweise die Zahl 1,2345 (kaufmännisch) auf drei Nachkommastellen gerundet werden, so ergibt sich 1,235. Wird diese nun nochmals gerundet, so ergibt sich 1,24. Damit hat sich das angezeigte Ergebnis merklich verschlechtert. Das kann vermieden werden, indem bei einem Runden, die Qualität der Rundung angegeben wird.

Wird eine Zahl aufgerundet, so erhält sie einen Punkt unter der letzten Ziffer, der andeutet, dass die Zahl eigentlich niedriger als angegeben ist; wird sie abgerundet, so kommt der Punkt über die letzte Ziffer, eben um anzudeuten, dass die dargestellte Zahl zu groß ist.

round(“1.2345”, 3) ≈ “1.235̣”
round(“1.235̣”, 2) ≈ “1.23̣”
round(round(“1.2345”, 3), 2) = round(“1.2345”, 2)

Damit ist gewährleistet, dass auch nach mehreren Rundungen das selbe Ergebnis erziehlt wird. Darüber hinaus können Konstanten entspechend annotiert werden, so dass nötige Grenzen eindeutig erkannt werden können.

Darüber hinaus kann mit der Punktnotation derjenige Wert angegeben werden, der sich am nächsten an der angegebenen Zahl befindet. Wird nämlich nach einer Zahl gesucht, die echt kleiner als eine gegebene Grenze ist, so ist dies oft gar nicht so einfach.

one : 𝔽₃₂ ← 1.0
f₁ : 𝔽₃₂ ← 0.999 999 98
assert f₁ = oneNicht intuitiv.
f₃ : 𝔽₃₂ ← 0.999 999 97
assert f₂ ≠ one
f : 𝔽₃₂
f ← 1.
assert f ≡ 0.999 999 94
f ← 1.0
assert f ≡ 1.0
f ← 1.
assert f ≡ 1.000 000 1

Sollen etwa die Gleitkommazahlen g im geschlossenen Intervall [0 ; 1] auf die ganzen Zahlen {0, … , 255} abgebildet werden, so müsste das eigentlich, um eine möglichst gute Gleichverteilung erzielen zu können, durch g ⋅ 256 bewerkstelligt werden. Allerdings taucht hier das Problem auf, dass sich für g = 1 auch der Wert 256 ergeben kann, was natürlich außerhalb des gewünschten Wertebereichs liegt.

Das Problem lässt sich am einfachsten lösen, indem das ganzzahlige, geschlossene Intervall [0 ; 255] zunächst durch das halboffene Intervall von Gleitkommazahlen [0 ; 256[ ersetzt wird. Damit benötigt die verteilende Multiplikation den größten Gleitkommawert, der echt kleiner ist als 256:

function convert(g : 𝔽₆₄[0 ; 1]) → ₃₂{0, … , 255} is
returng256̣Wobei 256̣ < 256.0 ist.
end

Dabei ist entscheidend, dass diese Punkt-Notation unabhängig von der aktuellen Genauigkeit den nächsten kleineren oder größeren Nachbarn liefert.

Unsichere Zahlen

Im Zusammenhang mit empirisch ermittelten Daten kann es interessant sein, die zugrunde liegende Ungenauigkeit zu dokumentieren. Diese Möglichkeit ist dadurch gegeben, dass diese in Klammern an die Zahl angefügt werden kann.

NA := 6.022140857(74) ⋅ 10²³Einheit: mol−1

Brüche

Wichtig ist hier, dass 2⅓ = 2 + ⅓ und nicht etwa 2 ⋅ ⅓ ist. Um diesbezüglich Irrtümer zu vermeiden sollte deswegen auch immer, wenn der Operator nicht explizit angegeben ist (Juxtaposition), a statt a geschrieben werden.

Periodische Zahlen

Neben der Bruchdarstellung lassen sich gebrochen rationale Zahlen auch als periodische Zahlen angeben. Das hat den Vorteil, dass der exakte Wert unabhängig von der Genauigkeit der verfügbaren Zahlen dokumentiert werden kann.

assert 0. = ⅓
assert 0.1̅4̅2̅8̅5̅7̅ = ⅐
assert 0. = ⅑
assert 0. = 1

Sexagesimale Zahlen†

Etwas ungewöhnlich ist der Umgang mit sexagesimalen Zahlen zur Basis 60. Diese kommen aber insbesondere bei Zeit- und Gradangaben vor. Ein Stunde etwa hat 60 Minuten und eine Minute hat 60 Sekunden. Gleichermaßen wird ein Grad in 60 Minuten eingeteilt, die ebenfalls 60 Sekunden haben.

Zeitangaben

Bei einer Zeitangabe werden die einzelnen Teile durch einen Doppelpunkt getrennt. Die erste Einheit beschreibt dabei immer die Stunden, die zweite die Minuten und die letzte die Sekunden. Die Angabe der Sekunden und eventuelle Bruchteile davon sind optional. Stunden, Minuten und der Hauptteil der Sekunden müssen aber immer zweistellig mit führenden Nullen angegeben werden. Die Stunden sind dabei auf [0 ; 23], die Minuten [0 ; 59] und die Sekunden auf [0 ; 59] beschränkt. Darüber hinaus ist für die Beschreibung des Tagesendes 24:00:00 und für einen Tag mit Schaltsekunde die Zeitangabe 23:59:60 zulässig.

11:22:33.44411 Uhr 22 Minuten 33 Sekunden und 444 Tausendstelsekunden
11:22:3311 Uhr, 22 Minuten, 33 Sekunden
11:2211 Uhr 22 Minuten
12:00Mittag
00:00Mitternacht (am Anfang des Tages)
24:00Mitternacht (nur am Ende des Tages)
23:59:59Letzte Sekunde des Tages
23:59:60Letzte Sekunde eines Tages mit Schaltsekunde

Im Zusammenhang mit Einheiten dürfen bei Angabe einer Dauer einzelne Bestandteile weggelassen und oben angegebene Gültigkeitsbereiche überschritten werden – soweit dies sinnvoll ist.

assert 277:46:40 􊀡 = 1 000 000 􊀬
assert 2:26:40 􊀡 = 146:40 􊀦􊀢􊀧 = 8 800 􊀬
assert 1:23.45 􊀦􊀢􊀧 = 83 450 􊀦􊀬

Gradangaben

(50° 56′ 29.1″, 6° 57′ 29.3″)Kölner Dom
(−33° 51′ 25.4″, 151° 12′ 52.2″)Sidney Opera House
(−13° 09′ 46.5″, −72° 32′ 41.9″)Machu Picchu
assert −13° 09′ 46.5″ ≈ −13.162916°

Vorsicht ist bei Gradangaben geboten, wenn diese mit trigonometrischen Funktionen verwendet werden, denn dort wird zwischen Grad- und Bogenmaß unterschieden. Da die für das Bogenmaß definierte Einheit rad das Verhältnis zweier Längen (mit der Einheit m/m) beschreibt, werden standardmäßig alle trigonometrischen Funktion im Bogenmaß berechnet.

assert sin 180° ≠ sin 180
assert sin 180° = 𝟎Exakt!
assert sin 180 ≈ −0.80115263573383
assert sin π ≈ 1.2246467991473532 × 10⁻¹⁶Leider nur nah dran!