none
Mit mehr als 28 Dezimalstellen rechnen RRS feed

  • Frage

  • Ich möchte eine Anwendung erstellen, die Wurzeln einer eingegebenen Zahl mit Hilfe des Heron-Verfahrens möglichst genau berechnet. Leider unterstützt der von mir gewählte Datentyp Decimal nur 28 Nachkommastellen. Hat irgendjemand eine Idee, wie ich die Wurzeln genauer berechnen kann?

    Hier mein Code:

     Dim fehlertext As String = "Lange Wurzel Rechner - Fehler!"
        Dim tv As Single
    
        Dim l As Decimal
        Dim b As Decimal
        Dim sq As Decimal
    
       Private Sub ButtonLos_Click(sender As Object, e As EventArgs) Handles ButtonLos.Click
            Try
                tv = TextBox1.Text / 2
            Catch ex As Exception
                MsgBox("Bitte geben Sie eine Zahl ein!", MsgBoxStyle.Critical, fehlertext)
                Exit Sub
            End Try
    
            Try
                sq = TextBox1.Text
            Catch ex As Exception
                If TextBox1.Text.StartsWith("-") Then
                    MsgBox("Die eingegebene Zahl ist zu klein. Bitte geben Sie eine Zahl zwischen " & Decimal.MinValue.ToString & " und " & Decimal.MaxValue.ToString & " ein.", MsgBoxStyle.Critical, fehlertext)
                Else
                    MsgBox("Die eingegebene Zahl ist zu groß. Bitte geben Sie eine Zahl zwischen " & Decimal.MinValue.ToString & " und " & Decimal.MaxValue.ToString & " ein.", MsgBoxStyle.Critical, fehlertext)
                End If
                RichTextBox1.Text = ""
                Exit Sub
            End Try
    
            l = 1
            b = sq
            For i = 0 To 100000
                l = (l + b) / 2
                b = sq / l
            Next
    
            If NumericUpDown1.Value < 29 Then
                RichTextBox1.Text = Math.Round(l, CInt(NumericUpDown1.Value), MidpointRounding.AwayFromZero)
            Else
                RichTextBox1.Text = l.ToString
            End If
    
    
        End Sub

    Sonntag, 29. Dezember 2013 14:16

Antworten

  • Ähm ja...
    die Wurzel aus 2 wirst du nie 100% exakt ermitteln können. Egal mit welchem Datentyp. Du könntest dir höchstens etwas ausdenken, was sich √2 statt 1,41... merkt um exakter rechnen zu können. Die Dezimalausgabe wird allerdings auch nicht anders. Die ganzen .NET Zahlentypen können alle höchstens Rationale Zahlen fassen.

    Wenn du auf Ganze Zahlen zum speichern setzt dann musst du dir merken an welcher Stelle du dir das Komma vorstellst. Wirklich anders macht .NET das auch nicht. Es wird eine ganze Zahl gespeichert und ein Exponent der angibt um wie viele Stellen das Komma verschoben werden soll (Binär).


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    • Bearbeitet Tom Lambert (Koopakiller)Moderator Montag, 30. Dezember 2013 12:32
    • Als Antwort markiert QR-3 Montag, 30. Dezember 2013 15:27
    • Tag als Antwort aufgehoben QR-3 Montag, 30. Dezember 2013 15:39
    • Als Antwort markiert QR-3 Montag, 30. Dezember 2013 15:39
    Montag, 30. Dezember 2013 08:57
    Moderator
  • Hallo,

    zu 1.
    Das ist nicht immer ganz einfach. Angenommen du hast 2 Zahlen: 1.23 und 2.34. Diese möchtest du nun addieren. Als Ganze Zahl könnten diese so aussehen:
    1230 und 2340, die letzten 3 Stellen sind die Dezimalstellen. Wenn du nun beide Zahlen addierst und das Komma zurück verschiebst erhälst du das Ergebnis: 3570 > 3.57.
    Für die Subtraktion, Multiplikation und Division läuft es genauso ab. Für den Rest wird es schwieriger. Aber mehr brauchst du nicht wirklich. Wenn du doch mal mehr brauchen solltest, vieles lässt sich auf die Exponentialfunktion zurück führen. Diese lässt sich mittels einer Reihenentsicklung berechnen. Für einige andere Dinge gibt es noch andere Entwicklungsmöglichkeiten. Wikipedia ist für diese Formeln ein ziehmlich allumfassender Anlaufpunkt mit vielen Ansätzen.

    zu 2.
    Du musst noch einen Verweis auf System.Numerics setzen. Mache dazu im Projektmappen Explorer einen Rechtsklick auf Verweise und wähle Verweis hinzufügen... Dort wählst du anschließend System.Numerics aus.


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    • Als Antwort markiert QR-3 Freitag, 10. Januar 2014 15:44
    Mittwoch, 1. Januar 2014 13:45
    Moderator

Alle Antworten

  • Hallo,
    das Standard .NET-Framework unterstützt keine genaueren Typen für Dezimalzahlen. Es gibt aber 3 andere Möglichkeiten:

    1. Unter den BCL-Klassen auf CodePlex befindet sich auch eine BigRational-Klasse:
      https://bcl.codeplex.com/wikipage?title=BigRational&referringTitle=Home
      Mit dieser kannst du Zahlen, in einem Bruch darstellen.
    2. Du benutzt die BigInteger-Struktur und legst eine feste Anzahl an Stellen fest, welche nach dem Komma stehen.
    3. Du suchst dir einen thirdparty Hersteller einer solchen Klasse.

    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    Sonntag, 29. Dezember 2013 14:25
    Moderator
  • Hallo,

    ich habe mir deine Links mal angesehen. Das Problem ist, dass

    1. Wurzeln häufig irrationale Zahlen sind.

    2. Ich auch die Nachkommastellen einer Wurzel berechnen möchte.

    Montag, 30. Dezember 2013 08:48
  • Ähm ja...
    die Wurzel aus 2 wirst du nie 100% exakt ermitteln können. Egal mit welchem Datentyp. Du könntest dir höchstens etwas ausdenken, was sich √2 statt 1,41... merkt um exakter rechnen zu können. Die Dezimalausgabe wird allerdings auch nicht anders. Die ganzen .NET Zahlentypen können alle höchstens Rationale Zahlen fassen.

    Wenn du auf Ganze Zahlen zum speichern setzt dann musst du dir merken an welcher Stelle du dir das Komma vorstellst. Wirklich anders macht .NET das auch nicht. Es wird eine ganze Zahl gespeichert und ein Exponent der angibt um wie viele Stellen das Komma verschoben werden soll (Binär).


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    • Bearbeitet Tom Lambert (Koopakiller)Moderator Montag, 30. Dezember 2013 12:32
    • Als Antwort markiert QR-3 Montag, 30. Dezember 2013 15:27
    • Tag als Antwort aufgehoben QR-3 Montag, 30. Dezember 2013 15:39
    • Als Antwort markiert QR-3 Montag, 30. Dezember 2013 15:39
    Montag, 30. Dezember 2013 08:57
    Moderator
  • Ich glaube ich habe das Prinzip verstanden. Mir ist klar, wie ich die Zahlen sehr genau speichern kann.

    1. Ich verstehe aber nicht, wie ich mit diesen Zahle rechnen kann.
    2. Ich kann nicht mit der BigInteger-Struktur arbeiten. Wenn ich System.Numerics.BigInteger aufrufe, wird die Fehlermeldung "Der Typ 'System.Numerics.BigInteger' ist nicht definiert." ausgegeben.

    QR-3

    P.S. Ich bin die nächste Woche abwesend und werde deshalb solange wahrscheinlich nicht antworten.

    Montag, 30. Dezember 2013 17:45
  • Hallo,

    zu 1.
    Das ist nicht immer ganz einfach. Angenommen du hast 2 Zahlen: 1.23 und 2.34. Diese möchtest du nun addieren. Als Ganze Zahl könnten diese so aussehen:
    1230 und 2340, die letzten 3 Stellen sind die Dezimalstellen. Wenn du nun beide Zahlen addierst und das Komma zurück verschiebst erhälst du das Ergebnis: 3570 > 3.57.
    Für die Subtraktion, Multiplikation und Division läuft es genauso ab. Für den Rest wird es schwieriger. Aber mehr brauchst du nicht wirklich. Wenn du doch mal mehr brauchen solltest, vieles lässt sich auf die Exponentialfunktion zurück führen. Diese lässt sich mittels einer Reihenentsicklung berechnen. Für einige andere Dinge gibt es noch andere Entwicklungsmöglichkeiten. Wikipedia ist für diese Formeln ein ziehmlich allumfassender Anlaufpunkt mit vielen Ansätzen.

    zu 2.
    Du musst noch einen Verweis auf System.Numerics setzen. Mache dazu im Projektmappen Explorer einen Rechtsklick auf Verweise und wähle Verweis hinzufügen... Dort wählst du anschließend System.Numerics aus.


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    • Als Antwort markiert QR-3 Freitag, 10. Januar 2014 15:44
    Mittwoch, 1. Januar 2014 13:45
    Moderator
  • Gut, dass mit den Biginteger-Variablen funktioniert.

    Allerdings habe ich immer noch Probleme mit der Rechnerei. Die Ergebnissestimmen von den Zahlen her nicht und das Komma wird immer an der falschen Stelle eingefügt.

    Hier mein Code:

    Imports System.Math
    Imports System.Numerics
    
    Public Class Form1
    
        Dim summeb As BigInteger
        Function Summe(ByVal summ1 As BigInteger, ByVal summ2 As BigInteger, ByVal summ1e As BigInteger, ByVal summ2e As BigInteger)
            If summ1.ToString.Length = summ2.ToString.Length Then
                summeb = summ1 + summ2
            Else
                If summ1.ToString.Length > summ2.ToString.Length Then
                    Dim dif = summ1.ToString.Length - summ2.ToString.Length
                    For i = 0 To dif
                        summ2 = summ2.ToString & 0
                        summ2e = summ2e + 1
                    Next
                    summeb = summ1 + summ2
                Else
                    Dim dif = summ2.ToString.Length - summ1.ToString.Length
                    For i = 0 To dif
                        summ1 = summ1.ToString & 0
                        summ1e = summ1e + 1
                    Next
                    summeb = summ2 + summ1
                End If
            End If
            le = summ1e
            be = summ2e
            Return summeb
        End Function
    
    
        Dim quotientb As BigInteger
        Function Quotient2(ByVal quotient1 As BigInteger, ByVal quotient1e As BigInteger)
            quotient1 = quotient1.ToString & 0
            quotient1e = quotient1e + 1
    
            quotientb = quotient1 / 2
    
            le = quotient1e
            Return quotientb
        End Function
    
    
    
        Dim fehlertext As String = "Lange Wurzel Rechner - Fehler!"
        Dim tv As Single
    
    
        Public l, b, le, be As BigInteger
    
    
        Private Sub ButtonLos_Click(sender As Object, e As EventArgs) Handles ButtonLos.Click
            Try
                tv = TextBox1.Text / 2
            Catch ex As Exception
                MsgBox("Bitte geben Sie eine Zahl ein!", MsgBoxStyle.Critical, fehlertext)
                Exit Sub
            End Try
    
            Try
                tv = TextBox1.Text
            Catch ex As Exception
                If TextBox1.Text.StartsWith("-") Then
                    MsgBox("Die eingegebene Zahl ist zu klein. Bitte geben Sie eine Zahl zwischen " & Decimal.MinValue.ToString & " und " & Decimal.MaxValue.ToString & " ein.", MsgBoxStyle.Critical, fehlertext)
                Else
                    MsgBox("Die eingegebene Zahl ist zu groß. Bitte geben Sie eine Zahl zwischen " & Decimal.MinValue.ToString & " und " & Decimal.MaxValue.ToString & " ein.", MsgBoxStyle.Critical, fehlertext)
                End If
                RichTextBox1.Text = ""
                Exit Sub
            End Try
    
    
            l = 1
            le = 0
            b = tv.ToString.Split(",").First & tv.ToString.Split(",").Last
            be = tv.ToString.Split(",").Last.Length
    
            For i = 0 To NumericUpDown1.Value * 3 + 100
                l = Quotient2(Summe(l, b, le, be), le)
            Next
    
            
            RichTextBox1.Text = l.ToString.Remove(l.ToString.Length - le) & "," & l.ToString.Remove(0, le)
    
    
        End Sub
    End Class

    Wenn Wurzel 2 berechnet werden soll, bekomme ich als Ausgabe "1157894736842101207999218959208483536355633026972108967456303303817435268629075481987058544503511564039,5802373903001989611350202932333449605749106881761428307144589983401672102923987452781843775821229064192".
    Diese Zahl müsste aber mit 1(,)4142... anfangen.

    Findet jemand den Fehler?


    • Bearbeitet QR-3 Freitag, 10. Januar 2014 15:45
    Freitag, 10. Januar 2014 15:43
  • Ich helfe dir gerne, wenn du einen ansatzweise ordentlichen Programmierstil verwendest. Schreibe Option Strict On in die erste Zeile und korrigiere alle Fehler. Dann kannst du dir wenigstens sicher sein das du die Werte auch richtig umgewandelt hast.

    Um beispielsweise mit 10 zu multiplizieren ist folgendes ungeeignet:

    quotient1 = quotient1.ToString & 0

    wie folgt wäre es besser:

    quotient1 *= 10

    Dann kann ich dir nur empfehlen, dich ans Debuggen zu gewöhnen. Einfach Zeile für Zeile (F11) den Quellcode ausführen und die Werte mitrechnen (Auf die Variablen zeigen um deren Werte zu erhalten).
    Ich weiß von was ich rede, man braucht Teilweise ewig um einen Fehler zu finden, aber so kam ich bisher am schnellsten vorran.


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    Freitag, 10. Januar 2014 17:26
    Moderator
  • Ich habe Option Strict On hinzugefügt und die Fehler wie von VS vorgeschlagen verbessert. Das Debuggen bekomme ich aber nicht hin. Wenn ich F11 drücke bekomme ich folgende Meldung:

    und das Programm wird ganz normal ausgeführt.

    Die Ausgabe bei der Berechnung einer Wurzel ist die selbe geblieben - mit den gleichen Fehlern.
    • Bearbeitet QR-3 Samstag, 11. Januar 2014 08:43
    Samstag, 11. Januar 2014 08:42
  • Gut das du es korrigiert hast ;)

    Die Meldung sagt aus, das Eigenschaften nicht debuggt werden.
    Gehe mal an den Anfang der Methode, wo die Berechnung beginnt. Dort drückst du F9. Dadurch wird ein Haltepunkt erzeugt, welcher den Quellcode wärend der Ausführung anhält. Ab dort kannst du dann mit F11 weiter machen.

    Selbst nach der Meldung sollte der Debugger eigentlich bei F11 stehen bleiben. Wenn du eine Eigenschaft haben solltest, welche du debuggen willst, gehe in diese und setze dort deinen Heltepunkt.

    Wenn du noch Hilfe brauchst, poste bitte deinen überarbeiteten Code.


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    Samstag, 11. Januar 2014 11:07
    Moderator
  • Das Debuggen funzt! :) Ich finde es aber sehr verwirrend und finde auch keinen Fehler...  :(

    Hier mein Code:

    Option Strict On
    Imports System.Numerics
    
    Public Class Form1
    
        Dim summeb As BigInteger
        Function Summe(ByVal summ1 As BigInteger, ByVal summ2 As BigInteger, ByVal summ1e As BigInteger, ByVal summ2e As BigInteger) As BigInteger
            If summ1.ToString.Length = summ2.ToString.Length Then
                summeb = summ1 + summ2
            Else
                If summ1.ToString.Length > summ2.ToString.Length Then
                    Dim dif = summ1.ToString.Length - summ2.ToString.Length
                    For i = 0 To dif
                        summ2 *= 10
                        summ2e += 1
                    Next
                    summeb = summ1 + summ2
                Else
                    Dim dif = summ2.ToString.Length - summ1.ToString.Length
                    For i = 0 To dif
                        summ1 *= 10
                        summ1e += 1
                    Next
                    summeb = summ2 + summ1
                End If
            End If
            le = summ1e
            be = summ2e
            Return summeb
        End Function
    
    
        Dim quotientb As BigInteger
        Function Quotient2(ByVal quotient1 As BigInteger, ByVal quotient1e As BigInteger) As BigInteger
            quotient1 *= 10
            quotient1e += 1
    
            quotientb = quotient1 / 2
    
            le = quotient1e
            Return quotientb
        End Function
    
    
    
        Dim fehlertext As String = "Lange Wurzel Rechner - Fehler!"
        Dim tv As Single
    
    
        Public l, b, le, be As BigInteger
    
    
        Private Sub ButtonLos_Click(sender As Object, e As EventArgs) Handles ButtonLos.Click
            Try
                tv = CSng(CDbl(TextBox1.Text) / 2)
            Catch ex As Exception
                MsgBox("Bitte geben Sie eine Zahl ein!", MsgBoxStyle.Critical, fehlertext)
                Exit Sub
            End Try
    
            Try
                tv = CSng(TextBox1.Text)
            Catch ex As Exception
                If TextBox1.Text.StartsWith("-") Then
                    MsgBox("Die eingegebene Zahl ist zu klein. Bitte geben Sie eine Zahl zwischen " & Decimal.MinValue.ToString & " und " & Decimal.MaxValue.ToString & " ein.", MsgBoxStyle.Critical, fehlertext)
                Else
                    MsgBox("Die eingegebene Zahl ist zu groß. Bitte geben Sie eine Zahl zwischen " & Decimal.MinValue.ToString & " und " & Decimal.MaxValue.ToString & " ein.", MsgBoxStyle.Critical, fehlertext)
                End If
                RichTextBox1.Text = ""
                Exit Sub
            End Try
    
    
            l = 1
            le = 0
            b = CType(tv.ToString.Split(CChar(",")).First & tv.ToString.Split(CChar(",")).Last, BigInteger)
            be = tv.ToString.Split(CChar(",")).Last.Length
    
            For i = 0 To NumericUpDown1.Value * 3 + 100
                l = Quotient2(Summe(l, b, le, be), le)
            Next
    
            
            RichTextBox1.Text = l.ToString.Remove(CInt(l.ToString.Length - le)) & "," & l.ToString.Remove(0, CInt(le))
    
    
        End Sub
    End Class
    

    Samstag, 11. Januar 2014 13:28
  • Hi

      If summ1.ToString.Length = summ2.ToString.Length Then

    vergleicht die Länger 2er Sting wobei 100 genau so lang(3) ist wie 999 ohne jetzt die BigInterger Struktur da einzubeziehen.

    Da deine Methode ja Summe heist. Geh ich jetzt davon aus das zu die Zahlen addieren willst. Hierfür gibt es die Add Methode, der Struktur.

    MFG
    Björn

    Samstag, 11. Januar 2014 15:51
  • Jaa...

    Ich verstehe das Problem nicht.

    Montag, 13. Januar 2014 16:13
  • Dein Code enthält noch Fehler. Von einfachen wie:
    Du wandelst mit CSng einen String in einen Single um. Im Fehlerfall gibst du aber die Minimal- und Maximalwerte von Decimal.
    Bis hin zu Syntakteischen Dingen, die ich nicht nachvollziehen kann. Stelle einen Plan auf, was du wie abändern musst. Versuche es zu begreifen, wie der Computer es bei Gleitkommazahlen macht. So musst du es auch tun.

    Der Code aus deiner Frage hat ja anscheinend funktioniert. Schreibe dir einfach eine Klasse, welche einen rationalen Typen nach macht und die Rechenoperationen übernimmt. Du kannst natürlich auch die BCL-Klasse BigRational verwenden.

    Sicher ist es nicht es nicht leicht und man verzweifelt schnell an solchen Dingen. Aber mir fehlt die Zeit deinen Code umzuschreiben, zumal es dir nichts bringen würde.


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    Montag, 13. Januar 2014 19:23
    Moderator