Autor Beitrag
nullplan001
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 212

Win 2000 Professional, Debian Linux 4.0 (Etch,Stable)
Pascal (FreePascal 2.0.2, TurboPascal 7.0), C(++) (G++/GCC 3.4.2 + MinGW), Java (JDK 1.5.0_07), PHP (PHP 5.1.4)
BeitragVerfasst: Mo 21.08.06 11:51 
Hi all,
ich dachte mir, ich bringe heute mal eine Funktion online, die vor allem Einsteigern das Leben erleichtern dürfte, eine Potenzfunktion, also eine Funktion, die x^y berechnet und zurückgibt. Natürlich ist so eine Funktion in modernen Pascal-Compilern wie bspw. FreePascal schon implementiert (Da nutzt man den Operator **). Allerdings gibt es möglicherweise noch Leute, die mit TurboPascal arbeiten, oder dieser Operator ist in Delphi nicht enthalten. Das kann ich nicht sagen.
Die Funktion beachtet alle Spezialfälle, die mir eingefallen sind, die Logarithmen nicht abkönnen, oder die unberechenbar sind, wie bspw. 0^0 oder log(-x). Wieso mich die Logarithmen kratzen? Weil ich über die die Potenz berechne: x^y = z^(y * log_z x) (LaTeX-Schreibweise) Außerdem werden Dinge wie 0^x = 0, x^0 = 1 und x^1 = x, die fest definiert sind, vorweg genommen (womit man sich die ganz großen (und theoretisch langsamen) Floating-Point-Operationen e^x und ln x spart.
ACHTUNG: Datentyp aller Parameter sowie des Endergebnisses ist Double. TurboPascal-Nutzer müssen die Floating-Point-Emulation anstellen ({$N+}), oder einen Co-Processor einbauen, um diesen Datentyp nutzen zu können. Andere müssen das möglicherweise auch tun, bei manchen ist es allerdings schon Standard-Einstellung.
Vielleicht sollte zum Code-Verständnis noch etwas zu meiner Version der ungarischen Notation gesagt werden: Primitive Datentypen (und da zähle ich String dazu, auch wenn es technisch keiner ist) haben einen Buchstaben vor dem Namen. Und zwar:

  • i - Für Integers (byte, word, longword, shortint, integer, longint)
  • f - Für Floating Point-Variablen (real, single, double, ...)
  • c - Für Characters (char)
  • s - Für Strings (string, ansistring, shortstring, widestring)

Für komplexe Datentypen, die nur Daten eines primitiven Datentypen enthalten, gibt es zwei Buchstaben: Den ersten für den Datentype (a für Array, s für Set) und den zweiten für den Datentyp, aus dem er besteht, also einen von oben.
Zu guter letzt gibt es noch die komplexen Datentypen, die mehr als einen primitiven Datentyp enthalten, die erhalten einen Buchstaben vorneweg (r für record, o für object) und Klassen (c ist ja schon weg) kriegen zwei (cl).
So, der Worte sind genug gewechselt, ich lasse euch nun Taten sehn. Siehe Anhang. (Kompiliert mit FreePascal 2.0.2 ohne weiteres, Anpassungen könnten nötig sein bei anderen Compilern.)
Ach so, noch etwas: Fehlercode ist nicht, wie sonst üblich -1, sondern -pi, und zwar weil -1 als echtes Ergebnis sehr viel wahrscheinlicher ist, als -pi (wieso sollte jemand (-pi)^1 berechnen wollen?)
Einloggen, um Attachments anzusehen!
_________________
Ich fahr' nicht selber, weil ich festgestellt habe: ich fahre zu emotional. Bin 180 gefahren wo 30 erlaubt war... -- Jürgen von der Lippe


Zuletzt bearbeitet von nullplan001 am Mo 21.08.06 13:07, insgesamt 1-mal bearbeitet
Jakob Schöttl
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 929
Erhaltene Danke: 1


Delphi 7 Professional
BeitragVerfasst: Mo 21.08.06 12:18 
Schön, aber was ich nicht versteh, bzw. nicht weiß:

1) Ist der negative Wert von pi ein Symbol für Undefiniert?
2) Wenn man Exit als Parameter einen Wert mitgibt, ist das dann Result der Funktion?
nullplan001 Threadstarter
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 212

Win 2000 Professional, Debian Linux 4.0 (Etch,Stable)
Pascal (FreePascal 2.0.2, TurboPascal 7.0), C(++) (G++/GCC 3.4.2 + MinGW), Java (JDK 1.5.0_07), PHP (PHP 5.1.4)
BeitragVerfasst: Mo 21.08.06 13:06 
user profile iconbokaj hat folgendes geschrieben:
1) Ist der negative Wert von pi ein Symbol für Undefiniert?

user profile iconnullplan001 hat folgendes geschrieben:

Ach so, noch etwas: Fehlercode ist nicht, wie sonst üblich -1, sondern -pi, und zwar weil -1 als echtes Ergebnis sehr viel wahrscheinlicher ist, als -pi (wieso sollte jemand (-pi)^1 berechnen wollen?)

user profile iconbokaj hat folgendes geschrieben:
2) Wenn man Exit als Parameter einen Wert mitgibt, ist das dann Result der Funktion?

Unter FreePascal schon. Ich weis nur von Turbo Pascal, dass das da nicht geht, aber ich hatte keinen Bock, statt "exit(-pi)" einen ellenlangen Absatz
ausblenden Delphi-Quelltext
1:
2:
3:
4:
begin 
  {$IFDEF DELPHI} result {$ELSE} potenz {$ENDIF} := -pi
  exit;
end;

zu schreiben. Womit ich auch schon die Umstellung des ganzen erklärt hätte. Da ich auf meinem System zwei Pascal-Compiler nutze (FPC für kommandozeilen-basierte Programme, Chrome für .NET-basierte Programme wegen WinForms. WinForms sind einfacher zu handhaben als das WinAPI. Aber das nur am Rande.) und beide diese Funktion unterstützen, habe ich nicht daran gedacht, dass das auch von manchen kompilern nicht unterstüzt wird. Na gut, ich mache es nochmal neu.
Tschö,
nullplan

_________________
Ich fahr' nicht selber, weil ich festgestellt habe: ich fahre zu emotional. Bin 180 gefahren wo 30 erlaubt war... -- Jürgen von der Lippe
Jakob Schöttl
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 929
Erhaltene Danke: 1


Delphi 7 Professional
BeitragVerfasst: Di 22.08.06 09:35 
Das mit -pi hab ich übersehen. Sorry, ich muss zugeben, hab nicht alles gelesen.

Zum 2): ich weiß also nicht, ob Result immer den Soll-Wert hat oder?
nullplan001 Threadstarter
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 212

Win 2000 Professional, Debian Linux 4.0 (Etch,Stable)
Pascal (FreePascal 2.0.2, TurboPascal 7.0), C(++) (G++/GCC 3.4.2 + MinGW), Java (JDK 1.5.0_07), PHP (PHP 5.1.4)
BeitragVerfasst: Di 22.08.06 10:38 
Was meinst du mit Soll-Wert? 0^0 ist nunmal nicht definiert, dafür gibt's ein -pi zurück. Prüfe das Ergebnis ab, bevor du es verwendest. Bspw.:
ausblenden Delphi-Quelltext
1:
2:
pot := potfun(a,b);
if (pot = -pi) and (a <> -pi) then writeln(stderr,'You sucker attempted an unallowed operation');

Oder so ähnlich. Den Text musst du ja nicht so übernehmen ;-) . Die zusätzliche Prüfung ist noch drin, damit nicht ein echtes Ergebnis verworfen wird. (Wie gesagt, momentan fällt mir nur (-pi)^1 ein, wo -pi regulär rauskommen würde.
Tschö,
nullplan

_________________
Ich fahr' nicht selber, weil ich festgestellt habe: ich fahre zu emotional. Bin 180 gefahren wo 30 erlaubt war... -- Jürgen von der Lippe
Jakob Schöttl
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 929
Erhaltene Danke: 1


Delphi 7 Professional
BeitragVerfasst: Di 22.08.06 10:53 
Ich mein mit Soll-Wert das Result der funktion, das rauskommen soll.

Ich mein, wenn du einen Wert als Parameter bei Exit übergibts, dann ist das bei FreePascal eben das Result der Funktion. Aber du hast doch gesagt, bei Delphi geht das nicht so.
nullplan001 Threadstarter
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 212

Win 2000 Professional, Debian Linux 4.0 (Etch,Stable)
Pascal (FreePascal 2.0.2, TurboPascal 7.0), C(++) (G++/GCC 3.4.2 + MinGW), Java (JDK 1.5.0_07), PHP (PHP 5.1.4)
BeitragVerfasst: Di 22.08.06 11:04 
Ja, bei FPC ist das dann das Result der Funktion. Bei Delphi muckt bloß der Compiler rum (dürfte was von "ungleicher Parameteranzahl" sagen). Darum habe ich die entsprechenden Stellen im Code ja auch ersetzt. Sieht zwar jetzt blöder aus, funktioniert aber.
Tschö,
nullplan

_________________
Ich fahr' nicht selber, weil ich festgestellt habe: ich fahre zu emotional. Bin 180 gefahren wo 30 erlaubt war... -- Jürgen von der Lippe
Jakob Schöttl
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 929
Erhaltene Danke: 1


Delphi 7 Professional
BeitragVerfasst: Di 22.08.06 11:17 
achso, hab nicht gewusst, das du es noch geändert hast
alzaimar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: Di 22.08.06 11:51 
Ehrlich gesagt, finde ich es nicht besonders hübsch, bei einem Fehler einen obskuren Wert zu liefern. Wenn ich nun in einer etwas komplexeren Berechnung genau PI berechnen will, kann ich das nicht. Ich muss also immer prüfen, ob nicht doch -PI rauskommt. Das ist doch Quark.

Verwende Exceptions oder Laufzeitfehler. Punkt.
Stelle eine zweite Funktion zur Verfügung, z.B.
ausblenden Delphi-Quelltext
1:
Function TryPower (a,b : Extended; Var aResult : Extended) : Boolean					

die nur dann True liefert, wenn ein Wert berechnet werden konnte.

_________________
Na denn, dann. Bis dann, denn.
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Di 22.08.06 13:39 
Oder du gibst NAN zurück. Ist in Math.pas definiert, glaub ich. Das kommt definitiv nirgendwo raus, wird auch von Delphi manchaml benutzt für ungültige Werte.

Aber die Lösung mit Exteptions gefällt mir auch besser.

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
alzaimar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: Di 22.08.06 13:47 
An NAN dachte ich auch. Da diese Routine im Kontext einer Bibliothek sicherlich Anwendung findet, wird man eine orthogonale Fehlerbehandlung (Exceptions, TryXXXXX und Runtime-Fehler, NAN o.ä.) implementieren.

NAN ist aber mathematisch korrekt, weil das Ergebnis eben bei entsprechenden Parametern 'undefiniert' (Not A Number) ist.

_________________
Na denn, dann. Bis dann, denn.
nullplan001 Threadstarter
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 212

Win 2000 Professional, Debian Linux 4.0 (Etch,Stable)
Pascal (FreePascal 2.0.2, TurboPascal 7.0), C(++) (G++/GCC 3.4.2 + MinGW), Java (JDK 1.5.0_07), PHP (PHP 5.1.4)
BeitragVerfasst: Di 22.08.06 17:59 
Siehste, wieder was gelernt. Ich wusste z.B. nicht, dass es eine Definition für NaN gibt. Ich dachte, das wäre auf Java beschränkt gewesen. Tja, so kann man sich irren. Ich füge mal dem Quelltext eine Version History hinzu, damit dann auch jeder sieht, dass die Geschichte mit -pi erledigt ist.
Aber mal so im Vertrauen: Welche Exception hätte ich denn nehmen sollen? na gut, da hätte ich nachgucken müssen. Bei Runtime errors das gleiche: Welche Nummer hätten's gern? Aber die Variante mit Try finde ich nur bei procedures Empfehlenswert: Mit einer Funktion kann man sehr viel machen. Ich müsste sie aber zu einer procedure umbauen (Ausgabe per var-Parameter) und dann noch die Sache mit dem richtigen exit einbauen. Nee, danke.
Ich hab' gerade nachgeguckt: Ja, es gibt in der Unit math eine Konstante NaN. Ich bin jedoch nicht sicher, ob es die in Turbo Pascal gibt. Und ich habe keine Lust, jetzt noch Turbo Pascal zu installieren. 1. ist mein Diskettenlaufwerk ziemlich FUBAR und löscht alle Disketten, die ich einlege, auf zuverlässige Weise: Es zerstört den Plastikfilm. Und 2. liegt die installierte version auf einem anderen Rechner, der noch nicht hichgefahren wurde. Und nur wegen einer Info fahre ich ihn auch nicht hoch. Ich hoffe einfach mal, dass TP standardmäßig das Symbol TP definiert. Wenn nicht, muss das die TP-Miderheit von Hand machen. Ich bastel jetzt.
Tschö,
nullplan
P.S.: Äh... ich habe da ein kleineres Problem bei der Umsetzung obiger Idee: Ich kann nicht herausfinden, welcher Datentyp NaN ist (Es ist schließlich als 0.0 / 0.0 definiert). Das allein ist ja nicht schlimm, aber wenn ich versuche dem Funktionsresultat NaN zuzuweisen erhalte ich:
ausblenden Compiler-Ausgabe
1:
potenz.pas(23,111) Error: range check error while evaluating constants					

Die Zeile sieht so aus:
ausblenden Delphi-Quelltext
1:
{$IFDEF DELPHI} result {$ELSE} potfun {$ENDIF} := {$IFDEF TP} -pi {$ELSE} NaN {$ENDIF};					

Auch das casten von NaN auf double hat nichts gebracht. (Das war bei einem ähnlichen Fehler meine Rettung gewesen. Da wollte der Compiler den Parameter cw_usedefault partout nicht als Parameter in der Funktion CreateWindow zulassen. Ein Typecast änderte die Meinung.)
Kann ich da was gegen machen? Und, wenn ja, was?
tia und tschö,
nullplan

_________________
Ich fahr' nicht selber, weil ich festgestellt habe: ich fahre zu emotional. Bin 180 gefahren wo 30 erlaubt war... -- Jürgen von der Lippe


Zuletzt bearbeitet von nullplan001 am Di 22.08.06 19:02, insgesamt 1-mal bearbeitet
alzaimar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: Di 22.08.06 18:54 
user profile iconnullplan001 hat folgendes geschrieben:

Aber mal so im Vertrauen: Welche Exception hätte ich denn nehmen sollen?

Entweder eine eigene Exception-Klasse definieren, oder eine ERangeException, das würde -glaube ich- sogar passen.

user profile iconnullplan001 hat folgendes geschrieben:
na gut, da hätte ich nachgucken müssen. Bei Runtime errors das gleiche: Welche Nummer hätten's gern?

Die, die im Kontext der Bibliothek passt. Du stellst doch sowieso nur ein Codeschnipsel zur Verfügung. Wie ich schon sagte, müsste die Fehlerbehandlung bzw. die Definition, wie Fehler zu behandeln sind, im Kontext der Bibliothek erfolgen, in der das Schnipsel eingesetzt wird.
user profile iconnullplan001 hat folgendes geschrieben:
Aber die Variante mit Try finde ich nur bei procedures Empfehlenswert:

Wieso soll eine Funktion nicht auch eine Exception werfen können? Exceptions sind nichts 'Schlimmes', sondern eine verdammt geniale Möglichkeit der Programmflußkontrolle.

Stell einfach 2 Routinen zur Verfügung: Die Bequeme, die eben wegballert (so wie der Compiler das dann kompiliert), und eine, die es nicht macht, weil sie vorher alles Mögliche abprüft. Z.B. sollte das Ergebnis im Zahlenbereich des Funktionstypen liegen, den sonst bekommst du genau die Exception, nach der Du händeringend ("Welche RTE hätten's den gerne?") Gesucht hast.

Früher, bevor der blöde Compiler sowas schon im Ansatz angemeckert hat, schrieb man einfach:
ausblenden Delphi-Quelltext
1:
2:
If ParameterOutOfRange Then
  Result := 0/0


Soetwas Ähnliches müsstest Du doch auch hinbekommen.

Der jeweilige Compiler schmeisst dann ja logischerweise die richtige Exception bzw. einen LZF(Laufzeitfehler). "Division by Zero" trifft es zwar nicht ganz, ist aber legitim, finde ich.

Und für Programmiersprachen, die keine Exceptionbehandlung mit Try..Except besitzen, bietest du eben die zweite 'bulletproof' Möglichkeit an:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
If Not TryPower (a,b,r) Then
  Writeln('Hupsa, da stimmt was nicht')
Else
  Writeln(a,'^',b,' = ',r);

_________________
Na denn, dann. Bis dann, denn.
Allesquarks
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 510

Win XP Prof
Delphi 7 E
BeitragVerfasst: Di 22.08.06 19:10 
Ich hab mich ehrlichgesagt immer gefragt wie das mit der Zuweisung 0/0 funktionieren soll. Wenn das so einfach ist ist das toll.
Hauptursache (meines Nachdenkens) war damalsm, dass der Coprozessor eine explizite DArstellung für NAN hat und ich nicht ganz gesehen habe warum 0/0 immer dazu führt und warum man das deklarieren muss. Hier denke ich könnte das aber Lösung für alle denkbaren Pascal Versionen sein (zumindest für x86). Indem man statt -pi (das ist wirklich nicht gut eine definierte Zahl zu nehmen wo doch extra eine NAN definiert ist) immer den Binärcode von NAN in den Coprozessor laden, dass ist dann zumindest so wie die Hardwareentwickler sich die Benutzung vorgestellt haben.
nullplan001 Threadstarter
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 212

Win 2000 Professional, Debian Linux 4.0 (Etch,Stable)
Pascal (FreePascal 2.0.2, TurboPascal 7.0), C(++) (G++/GCC 3.4.2 + MinGW), Java (JDK 1.5.0_07), PHP (PHP 5.1.4)
BeitragVerfasst: Di 22.08.06 19:12 
user profile iconalzaimar hat folgendes geschrieben:

user profile iconnullplan001 hat folgendes geschrieben:
Aber die Variante mit Try finde ich nur bei procedures Empfehlenswert:

Wieso soll eine Funktion nicht auch eine Exception werfen können?

Typischer fall von "aneinander vorbeigeredet". Mit Try meinte ich die Variante mit TryPower, wo am Ende ein Bool-Wert rauskommt. Ich gebe nicht so gerne Ergebnisse über var-Parameter raus (was ich in dem Fall ja müsste), weil es den Nutzer ja einschränkt. Ich meine, eine Funktion kann ich auch als Funktionsparameter übergeben. Bspw.:
ausblenden Delphi-Quelltext
1:
writeln(pow(a,b));					

Da nutze ich pow als Parameter. Bei Varparametern muss ich das Ergebnis erst noch zwischenspeichern und das kostet Zeit, Nerven und Speicherplatz (gut, letzterer ist natürlich ziemlich billig geworden, trotzdem nutze ich oft Speicherplatz-Sparmaßnahmen. Schon aus Prinzip. Und um einer Microsoft-isierung meines Codes entgegenzuwirken. Sicher ist eine double-Variable wie ein Tropfen in einem Meer, aber sie es mal so: Das ganze Meer besteht aus Tropfen. In Maßen genossen passiert sicherlich nichts Schlimmes, aber wenn Speicherplatzverschwendung in Massen auftritt, dann ist der RAM dicht, dann hilft nur noch ein Reboot.)
Tja, trotzdem bleibt obiges Problem.
Tschö,
nullplan
P.S.:@Allesquarks: Falls du es nicht glauben solltest: Guck mal hier.

_________________
Ich fahr' nicht selber, weil ich festgestellt habe: ich fahre zu emotional. Bin 180 gefahren wo 30 erlaubt war... -- Jürgen von der Lippe