OverflowExecption in PointF-Array

  • C#

Es gibt 16 Antworten in diesem Thema. Der letzte Beitrag () ist von TheVBTutorialsVB.

    OverflowExecption in PointF-Array

    Hallo Forum,

    da ich momentan keinen Funktionenparser zur Verfügung habe (ich aber versuchen möchte Funktionen zu zeichnen) rechne ich einzelne Punkte des Graphen aus und Zeichne eine Kurve durch DrawCurve(). Das funktioniert bei quadratischen Funktionen o.ä sehr gut, allerdings steigen die Werte bei Exponentioalfunktionen so schnell und stark, dass ich einen Overflow-Execption kriege (nicht bei der Rechnung selbst, sondern wenn ich Zeichne!). Das verwirrt mich deshalb, da ich vorher überprüfe ob der y-Wert größer/kleiner ist als float.Min/MaxValue. Wie kann es zu einem OverFlowExecption kommen, wenn ich zu große Elemente gar nicht im Array aufnehme?

    C#-Quellcode

    1. public void ExponentialFunction(double a, double b)
    2. {
    3. Graphics device = Graphics.FromImage(BMP);
    4. List<PointF> points = new List<PointF>();
    5. Pen pen = new Pen(Brushes.Black, 1.5f);
    6. float x = (SurfaceSize.Width) / 2; //Der Punkt aus den Variablen x und y (x|y) entspricht meinem Urspung.
    7. float y = (SurfaceSize.Height) / 2;
    8. for (float i = -StartEndX; i < StartEndX; i += 0.1f) //StartEndX gibt an bei welchem X-Wert ich anfange zu zeichnen, und bei welchem ich aufhöre und entspricht der Breit meiner Form durch die Skala die ich verwende
    9. {
    10. if(((float)(-a * Scala * Math.Pow(b, i) + y) > float.MinValue) && ((float)(-a * Scala * Math.Pow(b, i) + y) < float.MaxValue))
    11. points.Add(new PointF(i * Scala + x, (float)(-a * Scala * Math.Pow(b, i) + y))); //Punkte werden nur hinzugefügt, wenn es keinen Overflow gibt
    12. }
    13. device.DrawCurve(pen, points.ToArray()); //Overflowexecption?!?
    14. }


    LG

    Hi
    dividiere besser durch 2f, damit du die exakte Hälfte bekommst. Ansonsten bekommst du bei der Hälfte der Werte ein um 0.5 falsches Ergebnis.
    Mein Tipp wäre, das rekursiv zu lösen oder besser iterativ über eine Instanz der Stack-Klasse.
    Hier habe ich sowas mal vorgestellt: Einfaches Plotten von Funktionen
    Das arbeitet rekursiv, führt aber zu einem Stapelüberlauf bei hohen Steigungen oder "wahrgenommenen" Unstetigkeiten.
    Der Algorithmus dazu war, glaube ich, wie folgt für das zu plottende Intervall [xStart, xEnd], die als Delegat zur Verfügung gestellte Funktion f (mit float f(float)) und ein Schwellwert epsilon (oder delta oder was auch immer):

    - Zeichne (xStart, f(xEnd))
    - Rufe Algorithmus A mit Intervall [xStart, xEnd] auf (der die gleichen Parameter hat, wie obiger Algorithmus)

    Algorithmus A
    - Berechne y1 = f(x1), y2 = f(x2)
    - Wenn der Abstand zwischen (x1, y1) und (x2, y2) nicht unterhalb von epsilon liegt, führe A erneut aus für [xStart, xEnd] = [xStart, x1 + (x2 - x1) / 2]
    - Zeichne (x1, y2)

    Beachte, dass jeder Pixel mehrfach gemalt werden kann, d.h. wenn die Farbe des Graphen
    $C_g$
    = (a, r, g, b) ist, solltest du erst in eine Bitmap rendern (was ich nicht gemacht habe) und dort Blending auf Alpha so implementieren, dass es maximal a annimmt (nur signifikant, falls du Blending einbaust, um eine "glatte Kurve" zu erhalten).
    Die iterative Lösung wäre:
    - Setze x1 = xStart und x2 = xEnd
    - Berechne y1 = f(x1), y2 = f(y2)
    - Erstelle Stack S
    - Füge dem Stapel (x2, f(x2)) hinzu
    - Zeichne (x1, y1)

    Wiederhole:
    - Solange der Abstand von (x1, y1) und dem obersten Stapelelement kleiner delta ist, zeichne es, setze (x1, x2) auf es und lösche es
    - Wenn der Stapel leer ist, verlasse die Schleife
    - (Ansonsten) setze x2 auf x1 + (x2 - x1) / 2 und füge (x2, f(x2)) dem Stapel hinzu

    Die iterative Lösung braucht zwar vmtl. etwas länger, läuft dafür aber nicht über (außer der verfügbare Speicher ist voll). Besser wäre es, eine Beschränkung der Iterationen zu verwenden und nach diesen an der Stelle fortzufahren (x1 := x2), an der es gescheitert ist.

    Es gibt aber noch weitaus komplexere, praktischere Algorithmen für sowas.

    Viele Grüße
    ~blaze~
    @~blaze~
    Auf Performance kommt es mir nicht zwangsläufig an, aber die iterative Variante klingt doch schon mal ganz gut. Ich bin bis jetzt immer Func und Delegate aus dem Weg gegangen, was ist der Zweck dieser?

    Delegaten sind im Prinzip Referenzen zu bestimmten Methoden. Ruft man einen Delegaten auf, so wird die Methode, auf die er verweist, aufgerufen.
    Der Vorteil ist, dass man mit ihm verschiedene Methoden aufrufen kann, ohne jedes mal eine eigene Definition einzuführen.
    An unserem Beispiel des Plottens lässt sich das gut illustrieren: Der Delegat kann z.B.
    x² darstellen:

    C#-Quellcode

    1. Func<float, float> xsq = (x) => x * x

    oder x³:

    C#-Quellcode

    1. Func<float, float> xcb = (x) => x * x * x

    oder beliebige andere Funktionen.

    Wie du siehst, könntest du sowohl xsq, als auch xcb an die Funktion geben und sie würde geplottet, rein auf Basis des Delegaten.
    Beide Delegaten, die ich aufgezeigt habe, sind übrigens Lambda-Ausdrücke, d.h. man schreibt die Definition der Methode direkt hin, anstatt sie erst in eine Methode auszulagern.
    Um eine Methode

    C#-Quellcode

    1. float Sin(float x)
    2. {
    3. return (float)Math.Sin(x);
    4. }

    als Delegat zu nutzen, würde man einfach nur den Namen hinschreiben - ohne Klammern:

    C#-Quellcode

    1. Func<float, float> sinx = Sin;


    Die Garantie, dass der Funktionsaufruf kompatibel mit der Methode ist, wird über die Signatur gelöst. Deshalb kann man z.B. double Math.Sin(double) nicht in Func<float, float> werfen, welches als Signatur quasi float(float) hat.

    Delegaten bieten quasi nur die Signatur an, was man reinwirft, muss diese Signatur haben und was man reinwirft ist ein Verweis auf die Methode, die aufzurufen ist. Der Compiler baut für Lambdaausdrücke übrigens auch einfach nur einen Typ und eine Methode.
    Ein typisches Beispiel für Delegaten sind übrigens Events: Jedes Event ist quasi eine Liste von Delegaten (diese Listen von Delegaten nennt man auch multicast delegates), die aufgerufen werden, sobald das Event ausgeführt wird. Jeder Handler ist ein Delegat.

    Viele Grüße
    ~blaze~
    Ah okay, ich verstehe. Scheint weder schwer noch aufwändig, vielleicht hätte ich es nicht rauszögern sollen. ^^ Dann versuche ich das alles morgen mal umzusetzen und melde mich falls ich es nicht hinbekommen sollte.

    @TheVBTutorialsVB Die Darstellung weiß nix von float, sie weiß eher was von int.
    Vielleicht siehst Du Dir mal die Werte an, bei denen die Overflow-Exception zuschlägt. Vielleicht ist das eher int.MaxValue / .MinValue oder die Hälfte davon.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    TheVBTutorialsVB schrieb:

    Probleme mit Integer
    kann es geben, wenn die Anzahl der Elemente zu groß wird, und die ist mit Integer begrenzt.
    Mach mal n Screenshot von der aufgeklappten Meldung.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @RodFromGermany

    Was ich hätte erwähnen sollen (mein Fehler, entschuldige) ist, dass die Funktion bei einem Aufruf ExponentialFunction(1, 2) keine Probleme verursacht. Da die Schleife unabhängig von den Parametern ist bedeutet dies, dass die Schleife immer gleich lang läuft und somit immer gleich viele Elemente im Array vorhanden sind. Es ist also nicht möglich (außer ich übersehe etwas), dass es hier zu einem Überlauf kommt.

    @TheVBTutorialsVB Das ist OK.
    Mach mal ein Bild von der Stelle, wo es zum Overflow kommt.
    Wenn ich mir Deinen 1. Post ansehe

    TheVBTutorialsVB schrieb:

    C#-Quellcode

    1. device.DrawCurve(pen, points.ToArray()); //Overflowexecption?!?
    kann es sein, dass Deine Daten schlecht konditioniert sind.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @RodFromGermany

    Ich dachte zusätzlich zur Fehlerstelle sind die Werte der Variablen auch sinnvoll und hier noch die letzten Werte des Arrays, da wird der Überlauf glaube ich deutlich (erklärt mir aber nicht wieso es dazu kommt).
    Bilder
    • 11.png

      46,71 kB, 1.151×671, 173 mal angesehen
    • 12.png

      17,33 kB, 1.147×280, 173 mal angesehen

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „TheVBTutorialsVB“ ()

    1. Das Forum bietet über [Erweiterte Antwort] die Möglichkeit, Bilder hochzuladen. Bitte nutzen.
    2. Hab noch nicht alles erfasst, aber
    if(((float)(-a * Scala * Math.Pow(b, i) + y) > float.MinValue) && ((float)(-a * Scala * Math.Pow(b, i) + y) < float.MaxValue))
    ist doch insofern murks, da Du 1. einen Wert erzeugst, 2. den in ein Float umwandelst und diese dann 3. mit Float.Min und Float.Max vergleichst. Du zündest die Bombe bei Punkt 2 und fragst danach ab, ob die Bombe hochgegangen ist. Klar kommt da m.E. eine OverflowException.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    @VaporiZed Jou.
    @TheVBTutorialsVB Poste mal einen vollständigen Code, der diese Exception bringt, also wie oben, aber mit den Aufrufwerten a und b.
    Erweiterte Antwort => Dateianhänge => Hochladen.
    Editiere bitte Deinen Post entsprechend.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @VaporiZed

    Wie soll ich sonst auf Fehler überprüfen, wenn ich es besser wüsste wäre ich nicht hier :D

    @RodFromGermany

    C#-Quellcode

    1. public partial class Form1 : Form
    2. {
    3. public Form1()
    4. {
    5. InitializeComponent();
    6. }
    7. private void Form1_Load(object sender, EventArgs e)
    8. {
    9. Function test = new Function(pic.Size, pic.Location, 20);
    10. test.DrawCoordinateSystem();
    11. test.ExponentialFunction(1f , 3f);
    12. pic.Image = test.BMP;
    13. }
    14. }
    15. //Dies gehört noch zur Function Klasse
    16. public Bitmap BMP { get; set; }
    17. public Size SurfaceSize { get; set; }
    18. public Point Position { get; set; }
    19. public float Scala { get; set; }
    20. public float StartEndX
    21. {
    22. get
    23. {
    24. return (float)SurfaceSize.Width / Scala;
    25. }
    26. }
    27. public Function(Size surface, Point pos, float scal)
    28. {
    29. SurfaceSize = surface;
    30. Position = pos;
    31. Scala = scal;
    32. }


    Das ist alles, mehr Code steckt nicht dahinter. DrawCoordinateSystem() zeichnet nur ein Koordinatensystem, komplett unabhängig von Funktionen.

    Statt den Vergleich zu Min/Max-Float zu machen (was eh nicht klappt, da DrawCurve viel früher allergisch auf große Zahlen reagiert, wie ich festgestellt habe), wäre eine Begrenzung der Werte auf bestimmte Festwerte eine Möglichkeit. Zweite Variante, da der OverflowEx anscheinend bei unterschiedlichen Werten auftreten kann (hab bisher noch kein Muster gefunden, scheint wohl auch davon abhängig zu sein, welche und wieviele Punkte bereits da sind): mit einem Test-device.DrawCurve in einem Try-Catch-Block prüfen, ob die Exception auftritt. Wenn nicht, wird der Punkt der Kurve hinzugefügt, wenn doch, dann fliegt er raus.
    Allerdings: Wenn die y-Werte eh schon extrem, also nicht darstellbar sind: Warum die Punkte überhaupt akkurat zeichnen wollen? X mag sich ja noch in passablen Bereichen bewegen. Aber würde es einen großen Darstellungsverlust bedeuten, wenn Werte < -1000000 auf eben diesen Wert festgelegt werden, wenn sie in die Kurve sollen? Was ich sagen will: Wenn ein Wert (egal ob X oder Y) z.B. < -100000 oder > 100000 ist, wird der jeweilige Wert zu +/-1000000. Es gibt schon große Monitore. Aber so groß? Die Grenze kann ja auch noch n bisken größer sein.
    Achtung: Ich meine nicht die Werte an sich, sondern die darzustellenden Punkt-Koordinaten in der Kurve.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Wie wäre es wenn du um das Graphics Objekt mal nen using packst, das solltest du sowieso immer machen. Interessant wären die Werte die in den floats drinne steht. Ist da nicht NaN drinnen?
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Hier mein aktueller Code:

    C#-Quellcode

    1. public void ExponentialFunction(float a, float b)
    2. {
    3. List<PointF> points = new List<PointF>();
    4. Pen pen = new Pen(Brushes.Black, 1.5f);
    5. float x = (SurfaceSize.Width) / 2f;
    6. float y = (SurfaceSize.Height) / 2f;
    7. for (float i = -StartEndX; i < StartEndX; i += 0.1f)
    8. {
    9. points.Add(new PointF(i * Scala + x, -a * Scala * (float)Math.Pow(b, i) + y));
    10. using (Graphics device = Graphics.FromImage(BMP))
    11. {
    12. try
    13. {
    14. device.DrawCurve(pen, points.ToArray());
    15. }
    16. catch
    17. {
    18. points.RemoveAt(points.Count - 1);
    19. }
    20. }
    21. }
    22. }


    @jvbsl

    Ich habe oben ja ein Bild von den Werten gepostet, sie haben alle einen Wert zugewiesen. Ich habe den using Block ergänzt, hat zwar nicht geholfen, ist aber an sich schon besser.

    @VaporiZed

    Auf deinen Tipp hin habe ich das ganze in ein Try-Catch-Block gemacht und es wurde dadurch nur seltsamer, es wurde nun nämlich gar kein Graph mehr gezeichnet. Also habe ich den Try-Catch-Block entfernt um die Fehlermeldung zu sehen. Statt eines Overflowexecptions bei hohen Werten zu bekommen erhalte ich bei JEDEM Zeichnen einen Fehler (wie man sieht hat points auch nur ein Element), allerdings keinen Overflowexecption (siehe Bild).

    Edit: Das einzelne Element im Array ist points[0] = (-250, 250), also auch kein aufälliger Wert.
    Edit 2: Wenn ich den using-Block aus der Schleife rausnehme erhalte ich den Fehler bezüglich ungültiger Parameter nicht mehr. Allerdings kann ich dann in der Schleife auch nicht mit Try-Catch zeichnen.
    Edit3: Okay offensichtlich kann man keine Kurve aus nur einem Punkt zeichnen, ups. Also ist es nur noch der Overflowfehler.
    Letztes Edit: Ich habe eine für meine Zwecke zufriedenstellende Lösung gefunden, wen es interessiert:

    C#-Quellcode

    1. public void ExponentialFunction(float a, float b)
    2. {
    3. List<PointF> points = new List<PointF>();
    4. Pen pen = new Pen(Brushes.Black, 1.5f);
    5. float x = (SurfaceSize.Width) / 2f;
    6. float y = (SurfaceSize.Height) / 2f;
    7. for (float i = -StartEndX; i < StartEndX; i += 0.05f)
    8. {
    9. float accu = -a * Scala * (float)Math.Pow(b, i) + y;
    10. if ((accu <= (float)SurfaceSize.Height / 2f) && (accu >= 0))
    11. points.Add(new PointF(i * Scala + x, accu));
    12. }
    13. using (Graphics device = Graphics.FromImage(BMP))
    14. {
    15. device.DrawCurve(pen, points.ToArray());
    16. }
    17. }

    Bilder
    • 3238931bbca35f9f1482fbc587d31def.png

      43,46 kB, 1.155×748, 153 mal angesehen

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „TheVBTutorialsVB“ ()