vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#

https://www.vbarchiv.net
Rubrik: Variablen/Strings · Algorithmen/Mathematik   |   VB-Versionen: VB.NET31.05.07
Eine Klasse für die Bruch-Rechnung

Klasse für das Rechnen mit Brüchen auf der Basis von Ganzzahl-Arithmetik

Autor:   Manfred BohnBewertung:  Views:  13.467 
ohne HomepageSystem:  WinNT, Win2k, WinXP, Vista, Win7, Win8, Win10 Beispielprojekt auf CD 

Problemstellung:

Visual Basic stellt Datentypen für Ganzzahl- und für Gleitkomma-Variable zur Verfügung. Bei Verwendung der integrierten Datentypen müssen Berechnungen mit Bruchzahlen deshalb in der Form von Dezimalbrüchen durch Gleitkomma-Variable erledigt werden.

Das bringt Tücken mit sich.

Bruchzahlen können in Dezimaldarstellung bekanntlich zu unendlichen (z.B. periodischen) Brüchen werden - und die lassen sich nur fehlerbehaftet durch Gleitkomma-Variable abbilden. Berechnungen mit Bruchzahlen, die sich nur gering von 0 unterscheiden, führen bei Gleitkomma-Variablen zu einem erheblichen Verlust gültiger Stellen im Ergebnis. Insbesondere iterativ arbeitende Algorithmen können auf die daraus resultierende Fehlerakkumulation im Einzelfall sehr negativ reagieren und liefern dann extrem ungenaue Resultate.

Es liegt deshalb nahe, mit einer Klasse zu arbeiten, die tatsächlich Brüche verarbeitet - wo also Bruchzahlen aus (ganzzahligem) Zähler und Nenner bestehen und auch entsprechend den Regeln des Bruchrechnens manipuliert werden.

Die hier vorgestellte Klasse 'cBruchzahl' ist so aufgebaut.

Sie kann auch als einführendes Beispiel für die Programmierung von Operator-Überladungen herangezogen werden, die seit VB 2005 auch in MS-BASIC möglich ist.

Überblick über die Klasse 'cBruchzahl':
Eine Instanz der Klasse 'cBruchzahl' steht für eine einzelne Bruchzahl, bestehend aus Zähler und Nenner, die in zwei klassenglobalen LONG-Variablen abgelegt werden.

Die von 'cBruchzahl' durchgeführten Rechen-Operationen beruhen auf der Ganzzahl-Arithmetik und sind deshalb exakt - müssen sich aber im Rahmen des Datentyps LONG bewegen. Da es sich dabei um eine 8-Byte-Ganzzahl handelt, stellt das keine besonders gravierende Einschränkung dar. (Im Prinzip könnte man auch auf ganzzahlig gerundete DECIMAL-Werte zurückgreifen (12 Byte), aber das hätte erhebliche Leistungseinbußen zur Folge. Zudem kann dann nicht mehr auf die VB-Modulo-Division zurückgegriffen werden - das VBARCHIV enthält einen entsprechenden Tipp, der zeigt, wie es statt dessen geht.)

Die Wert-Zuweisung (Zähler und Nenner) kann über den Konstruktor oder durch die (überladene) Methode 'Zuweisung' erfolgen, wobei jeweils ein LONG-Wert für den Zähler und für den Nenner des Bruchs gefordert ist.

Bei Bedarf kann auch ein DECIMAL-Wert direkt zugewiesen werden - wobei allerdings nur vier Nachkommastellen bei der Bildung des Bruchs beachtet werden (schreibgeschützte Methode: 'FromDecimal').

Bei der Wert-Zuweisung, aber auch bei allen Berechnungen mit Brüchen, wird das Ergebnis stets automatisch soweit möglich gekürzt und danach klassenglobal gespeichert. Der Nenner kann dabei nicht den Wert 0 annehmen (d.h. eine Ausnahme wird ausgelöst). Dieses Verhalten entspricht der mathematischen Definition rationaler Zahlen.

Folgende Readonly-Abfrage-Methoden sind in der Klasse enthalten:

  • Zähler (der Wert des Zählers wird als LONG zurückgegeben)
  • Nenner (der Wert des Nenners wird als LONG zurückgegeben)
  • ToDecimal (der Bruch wird als Dezimalbruch vom Typ DECIMAL zurückgegeben)
  • ToDouble (der Bruch wird als Dezimalbruch zurückgegeben, gewandelt in einen DOUBLE-Wert)
  • ToString (der Bruch wird als nichtnumerischer Ausdruck-String zurückgeben, z.B. "2 / 3")

Die Abfrage per 'ToDecimal' oder 'ToDouble' birgt das Risiko des Informationsverlustes.

Folgende Vergleichsoperatoren sind in der Klasse enthalten:

  • gleich, ungleich, größer, kleiner, größergleich, kleinergleich

Man beachte, dass die Vergleiche auf (ganzzahligen) Decimal-Werten beruhen und keine Epsilontik enthalten ist. Nur numerisch exakt identische Brüche werden als 'gleich' ausgewiesen. (Im Einzelfall können andere Vergleichs-Varianten zweckmäßiger sein.)

Die Operatoren für die vier Grundrechenarten sind in der Klasse implementiert.

Die Kommentarzeilen im Code enthalten weitere Details zu den Methoden.

Die Berechnung des größten gemeinsamen Teilers (GGT) habe ich einem Tipp aus dem VBARCHIV entnommen und für VB 2005 geringfügig überarbeitet.

Hinweise zum Demonstrations-Beispiel:
Das beigefügte Demonstrationsbeispiel (SubRoutine: Bruchzahl_Demo) ermittelt in einer Schleife jeweils vier ganzzahlige, vorzeichenbehaftete Zufallszahlen und verwendet diese als Zähler bzw. Nenner für zwei Bruchzahlen. Mit diesen Bruchzahlen werden arithmetische und Vergleichsoperationen ausgeführt - simultan mit der impliziten Ganzzahl-Arithmetik des Datentyps DECIMAL und mit den überladenen Operatoren der Klasse 'cBruchzahl'. Die Ergebnisse werden jeweils (als Decimal-Werte) verglichen.

Es ist zu beachten, dass eventuell bei Brüchen nahe 0 die Dezimalbruchberechnung weniger genau ist als die direkte Bruchrechnung. Es kann deshalb zu geringfügigen Abweichungen kommen (d.h. eine der STOP-Bedingungen tritt auf). In diesem Fall kann das Vergleichsformat 'fmt' geändert werden, damit ein weniger sensibler Vergleich der beiden Berechnungsweisen durchgeführt wird.

Aus der Klasse 'cBruchzahl' können ein- und mehrdimensionale statische und dynamische Arrays gebildet werden. Dabei ist lediglich zu beachten, dass jedes einzelne Array-Element vor seiner Verwendung 'konstruiert' werden muss. Die Routine 'BruchzahlArray_Demo' zeigt, wie es geht.

Die Klasse 'cBruchzahl'

Option Strict On
Option Explicit On
 
Public Class cBruchzahl
 
  ' Die Klasse kapselt eine Bruchzahl und enthält 
  ' Bruchzahl-Operatoren  (+ - * /  < <= = <> => >)
 
  ' Manfred Bohn für VBARCHIV
  ' Mai 2007
 
  ' ===============================================
  ' globale Hilfs-Variable für gekapselte Bruchzahl
  ' ===============================================
  Dim gZähler As Long
  Dim gNenner As Long
  ' ==========================================================
  ' Konstruktoren
  ' ==========================================================
  Public Sub New()
    ' Standard-Konstruktor erforderlich: 
    ' Nenner-Null muss verhindert werden
    gZähler = 0 : gNenner = 1
  End Sub
 
  Public Sub New(ByVal Zähler As Long, ByVal Nenner As Long)
    ' zusätzlicher Konstruktor mit Zuweisungs-Parametern
    ' und automatischer Kürzung des Bruchs
    Zuweisung(Zähler, Nenner, True)
  End Sub
  '==================================================
  'Zuweisungs-Methoden
  '==================================================
  Public Overloads Sub Zuweisung(ByVal Zähler As Long, _
    ByVal Nenner As Long, _
    Optional ByVal Bruch_Kürzen As Boolean = True)
 
    ' Explizite Zuweisung von Zähler und Nenner der Bruchzahl
    ' Der dritte Parameter bestimmt, ob der Bruch vor der Zuweisung
    ' gekürzt wird (verwendet für interne Zwecke)
 
    ' Beide Parameter können vorzeichenbehaftet sein
    ' Man beachte jedoch, dass negative Werte sowohl im
    ' Zähler als auch im Nenner zu einem positiven Wert 
    ' des Bruchs führen
 
    ' Hilfsvariable sinnvoll initialisieren
    gZähler = 0 : gNenner = 1
 
    ' ggf. sofort Überlauf auslösen
    If Nenner = 0 Then
      ' so nicht !!!
      Throw New System.DivideByZeroException
    ElseIf Zähler = 0 Then
      ' Nenner sofort 'kürzen'
      Nenner = 1
    ElseIf Bruch_Kürzen Then
      ' falls gewünscht, Bruch kürzen
      Kürzen(Zähler, Nenner)
    End If
 
    ' Zuweisung der Parameter auf interne Variable durchführen
    gZähler = Zähler
    gNenner = Nenner
  End Sub
 
  Public Overloads Sub Zuweisung(ByVal Ganzzahl As Long, _
    ByVal Zähler As Long, _
    ByVal Nenner As Long)
 
    ' Zuweisung einer Ganzzahl mit Bruch (unechter Bruch)
    ' z.b. Zahl -1 1/2 --> Parameter: -1,1,2 
 
    ' In diesem Fall müssen Zähler und Nenner positiv sein
    ' weil bereits die Ganzzahl das Vorzeichen des 
    ' unechten Bruchs definiert
 
    If Zähler < 0 Or Nenner < 1 Then
      ' beide Vorzeichen müssen positiv sein
      Throw New System.ArgumentOutOfRangeException
    Else
      Zuweisung(Zähler + Ganzzahl * Nenner, Nenner)
    End If
  End Sub
 
  Public WriteOnly Property FromDecimal() As System.Decimal
    ' Ein Dezimalbruch wird in eine
    ' Bruchzahl umgewandelt und zugewiesen
    ' Dabei werden VIER Nachkommastellen beachtet!!
 
    ' Beispiel: -0,125 ---> - 1 / 8  Zähler = -1 : Nenner = 8
 
    Set(ByVal dec As Decimal)
      Dim sgn As Integer = Math.Sign(dec)
      dec = Math.Abs(dec)
      Dim Nenner As Long = 10000 ' Komma um 4 Stellen verschieben
      Dim Zähler As Long = _
      CLng(Math.Round(((dec - Fix(dec)) * Nenner), 0))
 
      Zuweisung(CLng(Fix(dec)), zähler, Nenner)
      ' Vorzeichen der Bruchzahl explizit eintragen
      gZähler *= sgn
    End Set
  End Property
  ' ============================================================
  ' Abfragemethoden
  ' ============================================================
  Public ReadOnly Property Zähler() As Long
    ' Abfrage des Zählers
    Get
      Zähler = gZähler
    End Get
  End Property
 
  Public ReadOnly Property Nenner() As Long
    ' Abfrage des Nenners
    Get
      Nenner = gNenner
    End Get
  End Property
 
  Public Shadows ReadOnly Property ToString() As String
    ' Die schattenwerfende Funktion gibt den Bruch 
    ' bzw. die Ganzzahl (Nenner = 1) als String zurück
 
    ' SHADOWS ist erforderlich weil 'ToString' von der 
    ' allgemeinen Basisklasse geerbt worden ist
    Get
      If gNenner <> 1 Then
        Return CStr(gZähler) + " / " + CStr(gNenner)
      Else
        Return CStr(gZähler)
      End If
    End Get
  End Property
 
  Public ReadOnly Property ToDecimal() As System.Decimal
    ' Die Funktion gibt den Wert des Bruchs 
    ' bzw. die Ganzzahl als Decimal zurück
    ' (--> hohe Genauigkeit der Dezimaldarstellung)
    Get
      If gNenner <> 1 Then
        ' Wichtig: Erst in Decimal wandeln, dann dividieren,
        ' sonst kommt es zu einem Genauigkeitsverlust, weil
        ' lediglich eine Double-Division durchgeführt wird !!! 
        Return CDec(gZähler) / CDec(gNenner)
      Else
        Return CDec(gZähler)
      End If
    End Get
  End Property
 
  Public ReadOnly Property ToDouble() As System.Double
    ' Die Funktion gibt den Wert des Bruchs 
    ' bzw. die Ganzzahl als Double-Gleikomma-Zahl zurück
    ' (--> geringe Genauigkeit der Dezimaldarstellung)
    Get
      If gNenner <> 1 Then
        Return CDbl(gZähler) / CDbl(gNenner)
      Else
        Return CDbl(gZähler)
      End If
    End Get
  End Property
  ' ==============================================================
  ' Überladung der Vergleichs-Operatoren  = <> <= < >= >      
  ' ==============================================================
  Public Shared Operator >(ByVal Bruchzahl1 As cBruchzahl, _
    ByVal Bruchzahl2 As cBruchzahl) As Boolean
 
    ' Operator: Größenvergleich von 2 Bruchzahlen
    ' Es gilt: a/b > c/d falls a*d > b*c
 
    Dim Term1, Term2 As Decimal
    Compare_Help(Bruchzahl1, Bruchzahl2, Term1, Term2)
 
    Return Term1 > Term2
  End Operator
 
  Public Shared Operator <(ByVal Bruchzahl1 As cBruchzahl, _
    ByVal Bruchzahl2 As cBruchzahl) As Boolean
 
    ' Operator: Größenvergleich von 2 Bruchzahlen
 
    Dim Term1, Term2 As Decimal
    Compare_Help(Bruchzahl1, Bruchzahl2, Term1, Term2)
 
    Return Term1 < Term2
  End Operator
 
  Public Shared Operator >=(ByVal Bruchzahl1 As cBruchzahl, _
    ByVal Bruchzahl2 As cBruchzahl) As Boolean
 
    ' Operator: Größenvergleich von 2 Bruchzahlen
    Dim Term1, Term2 As Decimal
    Compare_Help(Bruchzahl1, Bruchzahl2, Term1, Term2)
 
    Return Term1 >= Term2
  End Operator
 
  Public Shared Operator <=(ByVal Bruchzahl1 As cBruchzahl, _
    ByVal Bruchzahl2 As cBruchzahl) As Boolean
 
    ' Operator: Größenvergleich von 2 Bruchzahlen
    Dim Term1, Term2 As Decimal
    Compare_Help(Bruchzahl1, Bruchzahl2, Term1, Term2)
 
    Return Term1 <= Term2
  End Operator
 
  Public Shared Operator =(ByVal Bruchzahl1 As cBruchzahl, _
    ByVal Bruchzahl2 As cBruchzahl) As Boolean
 
    ' Operator: numerische Gleichheit von 2 Bruchzahlen
    Dim Term1, Term2 As Decimal
    Compare_Help(Bruchzahl1, Bruchzahl2, Term1, Term2)
 
    Return Term1 = Term2
  End Operator
 
  Public Shared Operator <>(ByVal Bruchzahl1 As cBruchzahl, _
    ByVal Bruchzahl2 As cBruchzahl) As Boolean
 
    ' Operator: numerische Ungleichheit von 2 Bruchzahlen
    Dim Term1, Term2 As Decimal
    Compare_Help(Bruchzahl1, Bruchzahl2, Term1, Term2)
 
    Return Term1 <> Term2
  End Operator
  ' ===========================================================
  ' Überladung der mathematischen Operatoren + - * /
  ' ===========================================================
  Public Shared Operator +(ByVal Bruchzahl1 As cBruchzahl, _
    ByVal Bruchzahl2 As cBruchzahl) As cBruchzahl
 
    ' überladener Operator: Addition zweier Bruchzahlen
 
    ' Operationsergebnis an Operator-Zielvariable übergeben 
    Return OperationTyp1(True, Bruchzahl1, Bruchzahl2)
  End Operator
 
  Public Shared Operator -(ByVal Bruchzahl1 As cBruchzahl, _
    ByVal Bruchzahl2 As cBruchzahl) As cBruchzahl
 
    ' überladener Operator: Subtraktion zweier Bruchzahlen
 
    ' Operationsergebnis an Operator-Zielvariable übergeben 
    Return OperationTyp1(False, Bruchzahl1, Bruchzahl2)
  End Operator
 
  Public Shared Operator *(ByVal Bruchzahl1 As cBruchzahl, _
    ByVal Bruchzahl2 As cBruchzahl) As cBruchzahl
 
    ' überladener Operator: Multiplikation zweier Bruchzahlen
 
    ' Operationsergebnis an Operator-Zielvariable übergeben 
    Return OperationTyp2(True, Bruchzahl1, Bruchzahl2)
  End Operator
 
  Public Shared Operator /(ByVal Bruchzahl1 As cBruchzahl, _
    ByVal Bruchzahl2 As cBruchzahl) As cBruchzahl
 
    ' überladener Operator: Division zweier Bruchzahlen
 
    ' Operationsergebnis an Operator-Zielvariable übergeben 
    Return OperationTyp2(False, Bruchzahl1, Bruchzahl2)
  End Operator
  ' ===========================================
  ' Hilfsfunktion für Subtraktion und Addition
  ' ===========================================
  Private Shared Function OperationTyp1( _
    ByVal Addition As Boolean, _
    ByVal Bruchzahl1 As cBruchzahl, _
    ByVal Bruchzahl2 As cBruchzahl) As cBruchzahl
 
    ' Die Funktion addiert oder subtrahiert zwei Brüche
    ' dabei wird der ggf. erforderliche gemeinsame Nenner
    ' hergestellt - und nach der Operation - falls möglich - 
    ' wieder gekürzt
 
    ' beide Brüche auf den kleinsten gemeinsamen Nenner 
    ' erweitern
    If Not Erweitern(Bruchzahl1, Bruchzahl2) Then
      Throw New System.OverflowException
    End If
 
    Dim erg As New cBruchzahl
    Dim Zähler, Nenner As Long
 
    ' Zähler der erweiterten Brüche addieren oder subtrahieren
    If Addition Then
      Zähler = Bruchzahl1.Zähler + Bruchzahl2.Zähler
    Else
      ' Subtraktion
      Zähler = Bruchzahl1.Zähler - Bruchzahl2.Zähler
    End If
 
    Nenner = Bruchzahl1.Nenner ' Readonly-Property auslesen
 
    ' Ergebnis-Bruch - falls möglich - kürzen
    Kürzen(Zähler, Nenner)
 
    ' Ergebnis-Bruchzahl erstellen
    erg.Zuweisung(Zähler, Nenner)
 
    ' Operations-Ergebnis zurückgeben
    Return erg
  End Function
  ' =============================================
  ' Hilfsfunktion für Multiplikation und Division
  ' =============================================
  Private Shared Function OperationTyp2( _
    ByVal Multiplikation As Boolean, _
    ByVal Bruchzahl1 As cBruchzahl, _
    ByVal Bruchzahl2 As cBruchzahl) As cBruchzahl
 
    ' Die Funktion multipliziert oder dividiert zwei Brüche
    ' und nach der Operation - falls möglich - kürzt sie das
    ' Ergebnis
    ' Der Nenner darf bei Division nicht 0 werden
 
    Dim erg As New cBruchzahl
    Dim Zähler, Nenner As Long
    If Multiplikation Then
      Zähler = Bruchzahl1.Zähler * Bruchzahl2.Zähler
      Nenner = Bruchzahl1.Nenner * Bruchzahl2.Nenner
    Else
      ' Division = Multiplikation mit Kehrwert
      Zähler = Bruchzahl1.Zähler * Bruchzahl2.Nenner
      Nenner = Bruchzahl1.Nenner * Bruchzahl2.Zähler
    End If
 
    If Nenner = 0 Then
      Throw New System.DivideByZeroException
    Else
      ' Ergebnis-Bruch - falls möglich - kürzen
      Kürzen(Zähler, Nenner)
 
      ' Ergebnis-Bruchzahl erstellen
      erg.Zuweisung(Zähler, Nenner)
    End If
 
    ' Operations-Ergebnis zurückgeben
    Return erg
  End Function
  ' ==========================================
  ' Hilfsfunktion: Kürzen eines Bruchs (GGT)
  ' ==========================================
  Private Shared Function Kürzen _
    (ByRef t1 As Long, ByRef t2 As Long) As Boolean
 
    ' erwartet: Ein Zählerwert und ein Nennerwert
    ' Funktion kürzt beide Werte soweit möglich
 
    If t1 = 0 Then t2 = 1 : Exit Function
 
    ' Absolutwerte sicherstellen
    Dim abs_t1 As Long = Math.Abs(t1)
    Dim abs_t2 As Long = Math.Abs(t2)
 
    ' Ermittlung: größter gemeinsamer Teiler  
    Dim mn As Long = GGT(abs_t1, abs_t2)
 
    ' Zähler und Nenner kürzen
    ' Vorzeichen bleibt jeweils erhalten (mn stets positiv)
    t1 \= mn : t2 \= mn
  End Function
  ' ===========================================
  ' Hilfsfunktion: Erweitern eines Bruchs (KGV)
  ' ===========================================
  Private Shared Function Erweitern(ByRef t1 As cBruchzahl, _
    ByRef t2 As cBruchzahl) As Boolean
 
    ' Die beiden Bruchzahlen werden auf einen gemeinsamen
    ' Nenner erweitert
    ' Rückgabe true: falls es geklappt hat
 
    Dim abs_t1n As Long = Math.Abs(t1.Nenner)
    Dim abs_t2n As Long = Math.Abs(t2.Nenner)
    Dim t1z, t2z As Long
 
    Try
      ' Ermittlung: Kleinstes gemeinsames Vielfaches der 
      ' beiden Nenner ---> gemeinsamer Nenner
      Dim mx As Long = KGV(abs_t1n, abs_t2n)
 
      ' Transformation der beiden Zähler entsprechend dem 
      ' gemeinsamen Nenner
      t1z = t1.Zähler * (mx \ t1.Nenner)
      t2z = t2.Zähler * (mx \ t2.Nenner)
 
      ' Erweiterung auf Bruchzahl-Instanzen zuweisen
      ' Kürzen bei Zuweisung durch Parameter 3 = false verhindern!
      t1.Zuweisung(t1z, mx, False)
      t2.Zuweisung(t2z, mx, False)
      Return True
    Catch ex As Exception
      ' vermutlich Überlauf (bei Modulo-Division)
      Return False
    End Try
  End Function</pre></code>
 
<pre><code>  ' ===========================================================
  ' Hilfsfunktion: KGV 
  ' kleinstes gemeinsames Vielfaches zweier Ganzzahlen
  '==========================================================
  Private Shared Function KGV _
    (ByVal z1 As Long, ByVal z2 As Long) As Long
 
    ' Berechnung des kleinsten gemeinsamen Vielfachen (KGV)
    ' durch Verwendung der Funktion GGT
    ' Es werden positiv ausgeprägte Parameter erwartet
 
    ' Die Klammern um die beiden ersten Argumente sind
    ' von entscheidender Bedeutung für ein korrektes Ergebnis
    Return (z1 \ GGT(z1, z2)) * z2
  End Function
  ' ============================================================
  ' Hilfsfunktion: GGT 
  ' größter gemeinsamer Teiler zweier Ganzzahlen
  '============================================================
  Private Shared Function GGT _
    (ByVal z1 As Long, ByVal z2 As Long) As Long
 
    ' Berechnung des größten gemeinsamen Teilers (GGT)
    ' durch den Euklidischen Algorithmus
    ' Es werden positive Werte in den Parametern erwartet
 
    Dim dummy, rest As Long
 
    ' Wenn die erste Zahl größer ist, dann
    ' Zahlen tauschen
    If z1 < z2 Then
      dummy = z2 : z2 = z1 : z1 = dummy
    End If
 
    ' Rest bei Ganzzahl-Division
    rest = z1 Mod z2
    If rest = 0 Then
      ' Wenn Rest = 0, ist GGT die kleinere Zahl 
      Return z2
    Else
      ' Ansonsten setzen wir den Wert von z1 auf den
      ' Wert von z2 und den Wert von z2 auf Rest
      z1 = z2 : z2 = rest
 
      ' Abbruchbedingung: Modulo der Zahlen = 0
      While z1 Mod z2 <> 0
        ' Rest berechnen und die Werte neu setzen
        rest = z1 Mod z2
        z1 = z2 : z2 = rest
      End While
 
      ' Rückgabe: Rest = GGT
      Return rest
    End If
  End Function
  ' =================================================================
  ' allgemeine Hilfsfunktion für Vergleichsoperatoren
  ' =================================================================
  Private Shared Sub Compare_Help(ByVal Bruchzahl1 As cBruchzahl, _
    ByVal Bruchzahl2 As cBruchzahl, _
    ByRef Term1 As Decimal, _
    ByRef Term2 As Decimal)
 
    ' Hilfsfunktion für Vergleichsoperatoren
    ' CrossOver-Multiplikationen
    ' um einen Überlauf möglichst zu vermeiden wird vor der 
    ' Multiplikation explizit in DECIMAL gewandelt
 
    Term1 = CDec(Bruchzahl1.Zähler) * CDec(Bruchzahl2.Nenner)
    Term2 = CDec(Bruchzahl2.Zähler) * CDec(Bruchzahl1.Nenner)
  End Sub
End Class

Demonstration der Klasse 'cBruchzahl'

  Public Sub Bruchzahl_Demo()
    ' Demonstration der Anwendung der Klasse 'cBruchzahl'
 
    ' Es werden Brüche aus Zufallszahlen gebildet und
    ' die Ergebnisse der Operatoren der Klasse 'cBruchzahl'
    ' mit den entsprechenden Gleitkomma-Operationen verglichen
 
    ' Hinweis: Operationen mit Brüchen sehr nahe bei 0
    ' können unterschiedliche Resultate liefern
    ' weil die Gleitkomma-Berechnung gültige Stellen einbüßt
 
    Dim i  ' Loop
    Dim zähler1, zähler2 As Long ' Zufallszahlen
    Dim nenner1, nenner2 As Long ' Zufallszahlen (-10000 bis +10000)
 
    Dim bz1, bz2, bz3 As New cBruchzahl  ' Bruchzahlen 
    Dim x1, x2, x3, xb3 As Decimal       ' Dezimalzahlen 
 
    Dim fmt As String = "g20"        ' Format (Ergebnisvergleich)
 
    ' Zufallszahlengenerator initialisieren
    Randomize(Microsoft.VisualBasic.Timer)
 
    ' Schleife über Testdurchläufe
    For i = 1 To 100000
 
      ' 4 ganzzahlige vorzeichenbehaftete Zufallszahlen
      ' bei jedem Umlauf ermitteln
      zähler1 = RandomLong()
      nenner1 = RandomLong()
      If i Mod 100 = 0 Then
        ' manchmal sind beide Bruchzahlen gleich
        zähler2 = zähler1 : nenner2 = nenner1
      Else
        ' meist aber nicht
        zähler2 = RandomLong()
        nenner2 = RandomLong()
      End If
 
      ' aus den Zufallszahlen 2 Bruchzahlen erzeugen
      ' Verwendung des Konstruktors
      bz1 = New cBruchzahl(zähler1, nenner1)
      ' Verwendung der Zuweisungsfunktion
      bz2.Zuweisung(zähler2, nenner2)
 
      ' aus den Zufallszahlen 2 Dezimalbrüche erzeugen
      x1 = CDec(zähler1) / CDec(nenner1)
      x2 = CDec(zähler2) / CDec(nenner2)
 
      ' ab hier beginnen die Vergleichstests:
 
      ' Dezimalbruch in Bruch umwandeln (4 Nachkommastellen)
      bz3.FromDecimal = x1
      xb3 = bz3.ToDecimal  ' Bruch als Dezimalbruch abfragen
      ' Dezimalbruch auf 4 Stellen identisch 
      ' mit Dezimaldarstellung der Bruchzahl ?
      If Math.Round(x1, 4).ToString(fmt) _
          <> xb3.ToString(fmt) Then Stop
 
      ' Subtraktion durchführen
      x3 = x1 - x2      ' Dezimal
      bz3 = bz1 - bz2   ' Bruch 
      ' Bruch in Dezimalbruchdarstellung abfragen
      xb3 = bz3.ToDecimal
      ' Ergebnis gerundet vergleichen
      If x3.ToString(fmt) <> xb3.ToString(fmt) Then Stop
 
      ' Addition durchführen
      x3 = x1 + x2
      bz3 = bz1 + bz2
      ' Bruch in Dezimalbruchdarstellung abfragen
      xb3 = bz3.ToDecimal
      ' Ergebnis gerundet vergleichen
      If x3.ToString(fmt) <> xb3.ToString(fmt) Then Stop
 
      ' Multiplikation durchführen
      x3 = x1 * x2
      bz3 = bz1 * bz2
      ' Bruch in Dezimalbruchdarstellung abfragen
      xb3 = bz3.ToDecimal
      ' Ergebnis gerundet vergleichen
      If x3.ToString(fmt) <> xb3.ToString(fmt) Then Stop
 
      ' Division durchführen
      x3 = x1 / x2
      bz3 = bz1 / bz2
      ' Bruch in Dezimalbruchdarstellung abfragen
      xb3 = bz3.ToDecimal
      ' Ergebnis gerundet vergleichen
      If x3.ToString(fmt) <> xb3.ToString(fmt) Then Stop
 
      ' Vergleichsoperatoren überprüfen
      ' Der Dezimal-Vergleich muss genauso 
      ' ausfallen wie der Bruch-Vergleich
      If (x1 > x2) <> (bz1 > bz2) Then Stop
      If (x1 >= x2) <> (bz1 >= bz2) Then Stop
      If (x1 = x2) <> (bz1 = bz2) Then Stop
      If (x1 <> x2) <> (bz1 <> bz2) Then Stop
      If (x1 < x2) <> (bz1 < bz2) Then Stop
      If (x1 <= x2) <> (bz1 <= bz2) Then Stop
 
      ' nächster Testdurchlauf
    Next i
 
    MsgBox("Demonstration der Klasse 'cBruchzahl' beendet")
  End Sub
  Public Function RandomLong( _
    Optional ByVal Null_OK As Boolean = False) As Long
 
    ' Hilfsfunktion: berechnet vorzeichenbehaftete
    ' Ganzzahlen im Bereich -10000 bis 10000
 
    Dim r As Long
 
    ' zufällige Ganzzahl
    r = CLng(Rnd() * 10000)
 
    ' ggf. Null vermeiden
    If Not Null_OK Then
      If r = 0 Then r = 1
    End If
 
    ' zufälliges Vorzeichen
    If Rnd() > 0.5 Then
      Return -r
    Else
      Return r
    End If
  End Function
  Public Sub BruchZahlArray_Demo()
    ' Beispiel für ein dynamisches, zweidimensionales
    ' BruchZahl-Array
 
    Dim i, k As Integer ' Loop
 
    Dim bz_arr(,) As cBruchzahl
    Dim bz_sum As New cBruchzahl
    Dim dec_sum As System.Decimal
    ReDim bz_arr(10, 20)
 
    ' Wert-Zuweisung unter gleichzeitiger
    ' Konstruktion der Arrayfeld-Objektinstanzen
    For i = 0 To UBound(bz_arr, 1)
      For k = 0 To UBound(bz_arr, 2)
        bz_arr(i, k) = New cBruchzahl(i, k + 1)
      Next k
    Next i
 
    ' Überprüfung des Array-Inhaltes
    bz_sum = New cBruchzahl
    For i = 0 To UBound(bz_arr, 1)
      For k = 0 To UBound(bz_arr, 2)
        ' Kontrolle des Feldinhaltes
        If bz_arr(i, k).ToDecimal <> _
           CDec(i) / CDec(k + 1) Then Stop
 
        ' Summation aller Brüche in einer Bruchzahl
        bz_sum += bz_arr(i, k)
        ' Summation aller Brüche in einem Decimal-Wert
        dec_sum += bz_arr(i, k).ToDecimal
      Next k
    Next i
 
    ' Vergleich der beiden Summationsergebnisse
    ' Man beachte die Stringabfrage bei bz_sum
    ' hier ist wegen der Funktionsüberladung zunächst
    ' die DECIMAL-Transformation notwendig
    If dec_sum.ToString("g15") <> _
       bz_sum.ToDecimal.ToString("g15") Then
      MsgBox("OOOPS!°")
    End If
 
    MsgBox("Summe aller Array-Elemente: " + bz_sum.ToString)
  End Sub



Anzeige

Kauftipp Unser Dauerbrenner!Diesen und auch alle anderen Tipps & Tricks finden Sie auch auf unserer aktuellen vb@rchiv  Vol.6
(einschl. Beispielprojekt!)

Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren vb@rchiv!
- nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten
- Symbol-Galerie mit mehr als 3.200 Icons im modernen Look
Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m.
 
 
Copyright ©2000-2019 vb@rchiv Dieter OtterAlle Rechte vorbehalten.


Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein.