Es geht hier nicht um dynamische oder statische Typsisierung, sondern eher darum, was von den Programmiersprachen an Funktionalität für numerische Typen angeboten wird. Irgendeine Form der Typisierung (dynamisch oder statisch) ist natürlich erforderlich, damit das funktionieren kann. Im Extremfall reicht vielleicht sogar der Ansatz, dass durch die Funktionsnamen unterschieden wird, als welche Typen die Eingabeparameter zu interpretieren sind, was z.B. bei Assemblersprachen praktisch durchgängig so üblich ist.
Was ich bei numerischen Typen sehr wichtig finde, ist dass man die Formeln, die die Berechnung beschreiben, so hinschreiben kann, wie sie sind, also z.B.
oder
a = b * c + d * e
oder gerne auch
a := b (*) c (+) d (*) e
statt
a = (b * c) + (d * e),
denn „Punktrechnung vor Strichrechnung“ lernt man in der Grundschule und das sollte zumindest bei Leuten, die sich mit Quelltexten von Software beschäftigen, im Erwachsenenalter sitzen. Damit will ich nicht sagen, dass man jeweils alle Präzendenzhierarchiestufen aller verwendeten Programmiersprachen kennen sollte, aber diese her schon. Smalltalk zwingt einen zu diesen Klammern.
Noch schlimmer ist Java, wo man für die meisten numerischen Typen etwa so etwas schreiben muss:
a = b.multiply(c).add(d.multiply(e));
Da erkennt kaum jemand die Formel wieder und Fehler sind vorprogrammiert…
Für Software, die in größerem Stil Dinge tut, die mit den primitiven numerischen Typen nicht machbar sind, ist Java nicht geeignet. Andere JVM-Programmiersprachen haben Operatorüberladung und erlauben es, mit geringerem Fehlerrisiko solche Dinge zu entwickeln. Eventuell kann auch ein Präprozessor eingesetzt werden, der eine Java-ähnliche Sprache mit einer Form von Operatorüberladung in Java übersetzt.
Was vielleicht auch geht, ist statt Infix auf Postfix- oder Präfixnotation zu wechseln. Ob man da die Formeln noch erkennt, ist ein bißchen eine Gewohnheits-, Geschmacks- und vielleicht auch Fähigkeitsfrage, wobei es sicher gute SW-Entwickler gibt, die einfach nicht den Blick dafür haben, Infix-Notation oder Postfix-Notation zu erkennen. Das sieht dann mit Präfixnotation in Lisp etwa so aus:
(setq a (+ (* b c) (* d e)))
oder mit Postfix-Notation, die man von alten HP-Taschenrechnern oder von Forth oder von der Druckersprache Postscript kennen könnte etwa so:
&a b c * d e * + =
oder etwa so:
b ENTER C * ENTER d ENTER e * + STO a
Angeblich soll das verständlicher sein als die Infixnotation, sagen manche HP-Taschenrechner-Fans aus den 80er Jahren.
In diesem Blogbeitrag über Zahlen steht ein bißchen dazu, auf was sich numerische Typen beziehen können. Eine 1:1-Zuordnung ist in der Regel nicht möglich, weil selbst „unbegrenzte“ Ganzzahlen wie bei Ruby oder Lisp in Wirklichkeit durch den Speicher des Rechners begrenzt sind. Aber man kann doch eine realistische Approximation erkennen.
Ruby | Perl | Python | Java | Scala | Clojure | Elixir | C | C++ | |
, | – | – | ? | – | – | ? | ? | (unsigned long eingeschränkt da, Überlauf) | (unsigned long eingeschränkt da, Überlauf) |
nativ (Integer, BigNum) | BigInt | ? | (BigInteger eingeschränkt wegen fehlendem Operator-Überladen) | BigInteger | nativ | nativ | (externe Libraries, z.B. GMP, eingeschränkt brauchbar wegen fehlendem Operator-Überladen) | externe Libraries, z.B. GMP | |
Rational | Math::BigRat | Fraction | (externe Libraries, eingeschränkt brauchbar wegen fehlendem Operator-Überladen) | externe Libraries, eventuell Aufnahme in Standardlibrary geplant. | nativ | ? | (externe Libraries, z.B. GMP, eingeschränkt brauchbar wegen fehlendem Operator-Überladen) | externe Libraries, z.B. GMP | |
Float, LongDecimal, BigDecimal | ja | ? | Double, BigDecimal | Double, BigDecimal | ? | ? | double | double | |
Complex | ? | ? | externe Libraries ohne Operatorüberladung mühsam und fehleranfällig. | ? | ? | ? | complex | ? |
-adische Zahlung und endliche Körper sind wohl schon zu sehr in der Mathematik angesiedelt und selten nativ in Programmiersprachen zu finden, außer in Programmiersprachen, die gezielt eine mathematiklastige Clientel ansprechen. Dabei sind gerade endliche Körper noch recht wichtig für Kryptographie und Codierungstheorie und werden dort natürlich auch benutzt.
Am schmerzhaftesten ist wohl immer noch, dass so wenige Programmiersprachen einen guten ganzzahligen numerischen Typ in die Sprache integriert haben, der es erlaubt, mit beliebig langen Zahlen (im Rahmen von Hauptspeicher) umzugehen.
Programmiersprachen, die so etwas wie Rational als numerischen Typ kennen, lassen ein gängiges Problem erkennen. Erfahrungsgemäß werden bei längeren Rechnungen Nenner und Zähler sehr sehr groß und die Berechnung entsprechend langsam. Dass man gut kürzen kann, ist eher die Ausnahme. Daran sollte man denken und entsprechende Performance- und Lasttests machen, um zu sehen, ob man in Laufzeit- und Speicherprobleme gerät.