vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#
vb@rchiv Offline-Reader - exklusiv auf der vb@rchiv CD Vol.4  
 vb@rchiv Quick-Search: Suche startenErweiterte Suche starten   Impressum  | Datenschutz  | vb@rchiv CD Vol.6  | Shop Copyright ©2000-2024
 
zurück
Rubrik: Variablen/Strings · Sonstiges   |   VB-Versionen: VB626.05.05
Der Gebrauch des Datentyps DECIMAL

Besonderheiten und Einschränkungen des Datentyps DECIMAL

Autor:   Manfred BohnBewertung:     [ Jetzt bewerten ]Views:  23.585 
ohne HomepageSystem:  Win9x, WinNT, Win2k, WinXP, Win7, Win8, Win10, Win11 Beispielprojekt auf CD 

Der VB6-Datentyp DECIMAL scheint überwiegend ein Schattendasein zu führen, obwohl er die Möglichkeit bietet, Berechnungen mit ganz besonders hoher Genauigkeit durchzuführen. Das hängt vermutlich auch damit zusammen, dass bei seiner Anwendung eine ganze Reihe von Besonderheiten und Einschränkungen zu beachten sind.Die folgenden Erläuterungen und Demonstrations-Beispiele sollen deshalb einen Beitrag dazu leisten, DECIMAL aus seinem Dornröschenschlaf zu wecken.

Zunächst die Definition des Typs, wie sie die VB6-Dokumentation liefert:
Variablen des Datentyps Decimal werden als 96-Bit-Ganzzahlen (12 Bytes) ohne Vorzeichen mit einer variablen Potenz zur Basis 10 gespeichert.
Die Potenz zur Basis 10 wird als Skalierungsfaktor verwendet und bestimmt die Anzahl der Nachkommastellen, die in einem Bereich von 0 bis 28 liegen kann.
Beim Skalierungsfaktor von 0 (keine Nachkommastellen) liegt der größtmögliche Wert bei +/-79.228.162.514.264.337.593.543.950.335.
Bei 28 Nachkommastellen liegt der größte Wert bei +/- 7,9228162514264337593543950335 und der kleinste Wert, der ungleich Null ist, bei +/-0,0000000000000000000000000001.

Der Datentyp Decimal kann nur mit einem Wert vom Typ Variant benutzt werden, d.h., Sie können keine Variable als Decimal deklarieren. Mit der CDec-Funktion können Sie jedoch einen Wert vom Typ Variant erstellen, dessen Untertyp Decimal ist.

Soweit die Dokumentation.

Ulkigerweise kennt VB6 das Schlüsselwort DECIMAL nicht.
Es ist also weder eine Variablen-Deklaration möglich, noch kann ein DECIMAL in einem Funktionskopf, in einer Ereignisprozedur oder als Bestandteil einer DECLARE-Anweisung auftauchen.
Das ist zwar ungewöhnlich, stellt aber keine echte Einschränkung dar. Überall wo eine Variable des Typs DECIMAL zu deklarieren wäre, ist statt dessen eine Variable des Typs VARIANT einzusetzen. Die Zuweisung eines numerischen Wertes auf die VARIANT-Variable durch die Umwandlungsfunktion 'CDec' deklariert sie als DECIMAL. Das kann durch Anwendung der VB6-Funktion VARTYPE leicht verifiziert werden. In der VB6-Dokumentation steht an einigen Stellen, der Datentyp DECIMAL würde nicht unterstützt. Davon darf man sich nicht irritieren lassen. Nur das Schlüsselwort ist nicht bekannt. Als Untertyp einer VARIANT-Variable wird DECIMAL korrekt an Funktionen übergeben. Das gilt auch für Arrays, deren Feldelemente vom Typ VARIANT, Untertyp DECIMAL sind.

Besonderes Augenmerk ist darauf zu richten, dass DECIMAL-Variable nur Werte im Bereich von ca. -7,9E+28 bis +7,9E+28 annehmen können. Das ist ein erheblich geringeres Intervall, als es z.B. der Datentyp SINGLE bietet. DECIMAL-Variablen kennen zwar Gleitkomma-, aber eben keine Exponential-Darstellung. Es ist darauf zu achten, dass bei Durchführung der mathematischen Operationen kein Überlauf entsteht bzw. eine Fehlerbehandlung implementiert ist.

Die vier Grundrechenarten können direkt auf VARIANT-Variable des Typs DECIMAL angewendet werden. Auch die Kombination mit anderen numerischen Datentypen in einem Ausdrucks ist möglich. Bei gemischten Datentypen innerhalb eines Ausdrucks sollte man durch Verwendung der Umwandlungsfunktionen CDec, CDbl, CLng usw. sicherstellen, dass alle Variablen bei der Berechnung des Ausdrucks den gleichen Datentyp besitzen. Nur dadurch ist die erzielte Genauigkeit des Ergebnisses abschätzbar.

Die in VB6 eingebauten mathematischen Funktionen akzeptieren zwar den Datentyp DECIMAL als Eingabeparameter, aber sie liefern stets als Ergebnis eine Variable des Typs DOUBLE.

Eine VARIANT-Variable des Untertyps DECIMAL geben lediglich folgende numerische Funktionen zurück: ABS, FIX, ROUND

Die Winkelfunktionen SIN, TAN, ATN, die Funktionen LOG, EXP, SQR und das Potenzieren (^) können deshalb in Berechnungen nicht verwendet werden, wenn die 28-stellige Genauigkeit des Datentyps DECIMAL auch im Ergebnis erhalten bleiben soll.

In den Demonstrationsbeispielen sind deshalb einige gebräuchliche einfache Algorithmen als Funktionen beigefügt, die die Genauigkeit des Datentyps DECIMAL verwenden (Quadratwurzel, ganzzahliges Potenzieren, natürlicher Logarithmus, Exponentialfunktion, Sinus). Sie sollen als Anregung zur Entwicklung schnellerer und noch genauerer Funktionen für diesen Datentyp dienen.

' Beginn des Quellcodes der Demonstrationsbeispiele für den Datentyp DECIMAL
 
Option Explicit
 
' Konstante als Indikator für Fehlerbedingungen
Const dcError As Variant = "ERROR"
Sub decimal_demo()
  ' Demonstration der Verwendung des Datentyps DECIMAL
 
  ' Der Variant-Datentyp muss bei der Variablen-Deklaration
  ' zwar nicht explizit angegeben werden, aber das wäre unübersichtlich
  Dim a As Variant, b As Variant, c As Variant
 
  ' 28-stellige Ganzzahl als String-Konstante definieren
  Const strGanzzahl As String = "1234567890123456789012345678"
 
  ' Wichtiger Hinweis: (Win2000 etc.)
  ' Als Komma in Strings, die mit der Funktion CDEC gewandelt werden,
  ' muss unbedingt das Zeichen verwendet werden, das in
  ' Systemsteuerung -> Ländereinstellungen -> Zahlen -> Dezimaltrennzeichen
  ' eingestellt ist (meist: ,)
  Const strGleitzahl As String = "123456789,1234567890123456789"
 
 
  ' Die Zuweisung der String-Konstanten auf eine
  ' Variable des Typs VARIANT per <CDec> erzeugt
  ' eine Variable des Untertyps <Decimal>
  ' --------------------------------------------
  a = CDec(strGanzzahl)
 
  If Not VarType(a) = vbDecimal Then
    MsgBox "Nanu !", vbExclamation
    Exit Sub
  End If
 
  ' Die Variable vom Typ DECIMAL wird korrekt addiert
  ' Die Ergebnisvariable ist vom Typ DECIMAL
  c = a + a
  Debug.Print "a + a = "; c
  ' a + a = 2469135780246913578024691356
 
  ' Die Multiplikation liefert das gleiche Ergebnis
  Debug.Print "a * 2 = "; a * 2
  ' a * 2 = 2469135780246913578024691356
 
 
  ' Durchführung von Multiplikation und Division
  ' --------------------------------------------
 
  ' Bei der Multiplikation mit einer Zahl des Typs Double
  ' wird das Ergebnis als Gleitkommazahl gespeichert
  ' (max. so viele Nachkommastellen, wie hinter dem Ganzzahl-
  ' Anteil noch möglich sind)
  c = a * CDbl(5.2)
  Debug.Print "a * 5,2 = "; c
  ' a * 5,2 = 6419753028641975302864197525,6
 
  ' Das Divisions-Ergebnis wird mit
  ' 28 Nachkommastellen gespeichert
  c = a / c
  Debug.Print "a / c = "; c
  ' a / c = 0,1923076923076923076923076923
 
  ' Die Genauigkeit der Rechenoperationen ist für die volle
  ' Stellenzahl gewährleistet
  ' Rückrechnung
  c = 1 / c
  Debug.Print "1 / c = "; c
  ' 1 / c =  5,2000000000000000000000000002
 
  ' Man erhält das gleiche Ergebnis, wenn der
  ' Multiplikatonsfaktor als Decimal verwendet wird
  c = a * CDec(5.2)
  c = a / c
  ' Ergebnis der Rückrechnung
  Debug.Print "1 / c = "; 1 / c
  ' 1 / c =  5,2000000000000000000000000002
 
 
 
  ' Die Funktionen ROUND, INT und die Typumwandlungsfunktionen
  ' sind für den Datentyp DECIMAL verwendbar
  ' ----------------------------------------------------------
  a = CDec(strGleitzahl)
  Debug.Print "a = "; Round(a, 17)
  ' a =  123456789,12345678901234568
  Debug.Print "Int = "; Int(a)
  ' Int =  123456789
  Debug.Print "Dbl = "; CDbl(strGleitzahl)
  ' Dbl =  123456789,123457
  Debug.Print "Sng = "; CSng(strGleitzahl)
  ' Sng =  1,234568E+08
 
 
 
  ' Beispiel für die Verwendung des Datentyps DECIMAL
  ' als Parameter einer Funktion
  ' --------------------------------------------------
  b = CDec(strGanzzahl)
  c = Addition(a, b)
  ' Die nicht mehr darstellbaren Nachkommastellen
  ' sind abgeschnitten !
  Debug.Print "a + b = "; c
  ' a + b =  1234567890123456789135802467,1
 
 
  ' Verwendung der Funktionen ABS und FIX
  ' -------------------------------------
  ' ABS-Funktion: Ergebnis bleibt DECIMAL (=14)
  c = Abs(-a)
  Debug.Print "Rückgabe-Typ ABS: " + CStr(VarType(c))
 
  ' FIX-Funktion: Ergebnis bleibt DECIMAL (=14)
  c = Fix(CDec(strGleitzahl))
  Debug.Print "Rückgabe-Typ FIX: " + CStr(VarType(c))
 
  ' Ermittlung der Nachkommastellen eines DECIMAL
  c = CDec(strGleitzahl) - Fix(CDec(strGleitzahl))
  Debug.Print "Nachkommastellen von " + strGleitzahl + " = "; c
  ' 0,1234567890123456789
 
 
  ' Das Verhalten der Funktionen SQR und ^ beim Datentyp
  ' DECIMAL ist nicht unproblematisch:
  ' Das Ergebnis der Berechnung der Quadrat-Wurzel
  ' wird von VB6 nur als DOUBLE geliefert, obwohl ein
  ' DECIMAL übergeben und akzeptiert worden ist
  Debug.Print "sqr(a) = "; Sqr(a)
  ' sqr(a) =  11111,1110661111
 
  ' Bei der Berechnung der Quadratwurzel mit der Genauigkeit
  ' DECIMAL muss man sich deshalb einer Hilfsfunktion bedienen
  Debug.Print "sqr(a) = "; Quadratwurzel(a)
  ' sqr(a) =  11111,111066111110969986110534
 
  ' Auch das Potenzieren liefert im Ergebnis
  ' den Datentyp DOUBLE
  Debug.Print "-a^3 = "; (-a) ^ CDec(3)
  ' -a^3 =  -1,88167637743418E+24
 
  ' Das Potenzieren mit einem ganzzahligen Exponenten
  ' benötigt deshalb ebenfalls eine Hilfsfunktion, um
  ' mit DECIMAL-Genauigkeit durchgeführt werden zu können
   Debug.Print "-a^3 = "; Potenzieren(-a, 3)
  ' -a^3 =  -1881676377434183982474065,6126
 
  ' Das gleiche beim 'natürlichen' Logarithmus
  Debug.Print "log(a) = "; Log(a)
  ' Hilfsfunktion für Genauigkeit des Datentyps DECIMAL
  Debug.Print "log(a) = "; Logarithmus(a)
 
  ' bei der Exponentialfunktion
  Debug.Print "exp(30,3) = "; Exp(30.3)
  ' Hilfsfunktion für Genauigkeit des Datentyps DECIMAL
  Debug.Print "exp(30,3) = "; Exponential(CDec("30,3"))
 
  ' und beim SINUS ...
  a = CDec("-4,736789012345678901234567")
  Debug.Print "sin(a) = "; Sin(a)
  ' Hilfsfunktion für Genauigkeit des Datentyps DECIMAL
  Debug.Print "sin(a) = "; Sinus(a)
 
 
 
  ' Die Verwendung des Datentyps DECIMAL zur
  ' Steuerung von FOR_NEXT-Schleifen bereitet keine Probleme
  ' --------------------------------------------------------
  Dim ug As Variant, og As Variant, stp As Variant
  Dim i As Variant
 
  ' Alle Schleifenvariablen mit DECIMALs belegen
  ug = CDec("-123456789,123456789123456789")
  og = Abs(ug)
  stp = CDec((og - ug) / CDec(10))
 
  Debug.Print vbCrLf + "Decimal in FOR_NEXT-Schleife"
  For i = ug To og Step stp
    Debug.Print CStr(i); " --> "; CStr(Potenzieren(i, 3))
  Next i
 
 
  ' Die Verwendung von Arrays, deren Felder vom Datentyp
  ' DECIMAL sind, ist ebenfalls möglich
  ' ---------------------------------------------------
  ' Es kann keine Variable des Typs VARIANT verwendet werden,
  ' die ein Array des Typs DECIMAL aufnimmt, sondern es
  ' ist ein Array mit Feldern des Datentyps VARIANT zu deklarieren
  Dim arr() As Variant, k&
  ReDim arr(1 To 20)
  a = CDec("100,12345678901234567890123")
 
  ' Felder des Array mit DECIMAL-Werten füllen
  For k = LBound(arr) To UBound(arr)
    arr(k) = a * CDec(k)
  Next k
 
  ' Übergabe des Array an eine Funktion
  ' zur Berechnung der Feld-Summe mit DECIMAL-Genauigkeit
  Debug.Print vbCrLf + "Array-Summe = "; CStr(Array_Summe(arr()))
  ' Array-Summe: 21025,9259256925925925692583
End Sub
Function Addition(ByVal a As Variant, ByVal b As Variant) As Variant
 
  ' Die Funktion addiert beliebige numerische Datentypen und
  ' gibt sie als VARIANT -> DECIMAL zurück
 
  On Error GoTo fehler
 
  Addition = dcError
 
  ' ungeeignete Eingabe abfangen
  If Not IsNumeric(a) Or Not IsNumeric(b) Then Exit Function
 
  ' Sicherstellung des Ergebnistyps durch die
  ' explizite Typumwandlungsfunktion CDec
  Addition = CDec(a) + CDec(b)
 
  Exit Function
 
fehler:
   ' Überlauf des DECIMAL-Datentyps abfangen
   Addition = dcError
End Function
Function Quadratwurzel(ByVal arg As Variant) As Variant
 
  ' Quadratwurzel aus arg
  ' Rückgabe: Variant -> Decimal
 
  ' Hilfsvariable
  Dim iarg As Variant
  Dim a As Variant, b As Variant
 
  ' Für Iterations-Abbruch
  Dim ok As Boolean
  Dim z&
 
  ' Hilfskonstanten
  Dim zwei As Variant
  Dim epsilon As Variant
  zwei = CDec(2)
  epsilon = CDec("0,0000000000000000000000001")
 
  Quadratwurzel = dcError
 
  ' unbrauchbare Eingabe abfangen
  If Not IsNumeric(arg) Then Exit Function
  If arg < 0 Then Exit Function
 
  ' Sonderfall
  If arg = CDec(0) Then
    Quadratwurzel = 0
    Exit Function
  End If
 
  iarg = CDec(arg)
  a = CDec(1)
  b = CDec(iarg)
 
  Do Until ok
    z = z + 1
    a = (a + b) / zwei
    b = iarg / a
    ' Abbruchbedingungen prüfen
    ok = (a - b < epsilon) Or z > 100
  Loop
 
  a = (a + b) / zwei
 
  ' Rückgabe
  Quadratwurzel = a
End Function
Function Potenzieren(ByVal arg As Variant, _
  ByVal lngExponent As Long) As Variant
 
  ' Potenzieren = arg ^ lngExponent, wobei lngExponent >=0
 
  Dim iarg As Variant
  Dim i As Long
 
  On Error GoTo fehler
 
  Potenzieren = dcError
 
  ' ungeeignete Eingabe abfangen
  If lngExponent < 0 Then Exit Function
  If Not IsNumeric(arg) Then Exit Function
 
  iarg = CDec(arg)
  Potenzieren = CDec(1)
 
  ' Sonderfall
  If lngExponent = 0 Then Exit Function
 
  For i = 1 To lngExponent
    Potenzieren = Potenzieren * iarg
  ext i
 
  Exit Function
 
fehler:
  ' Überlauf des Datentyps verhindern
  Potenzieren = dcError
End Function
Function Logarithmus(ByVal arg As Variant) As Variant
 
  ' natürlicher Logarithmus bei positivem Argument
 
  Dim p As Variant, q As Variant ' p = Produkt q = iterierte Quadratwurzel
  Dim i As Long
  Dim epsilon As Variant         ' Abbruchkriterium
 
  On Error GoTo fehler
 
  Const n As Long = 2000
  epsilon = CDec("0,0000000000000000000000000001")
 
  Logarithmus = dcError
 
  ' unbrauchbare Eingabe abfangen
  If Not IsNumeric(arg) Then Exit Function
  If arg < epsilon Then Exit Function
 
  p = CDec(1): q = CDec(arg) ' Initialisierungen
 
  For i = 1 To n
    q = Quadratwurzel(q)
    ' Abbruchkriterien
    If q = 0 Then Exit For
    If q = dcError Then Exit For
    If Abs(q - CDec(1)) < epsilon Then Exit For
 
    p = p * CDec(2) / (q + CDec(1) / q)
  Next i
 
  Logarithmus = p * (arg - CDec(1) / arg) / CDec(2)
  Exit Function
 
fehler:
  Logarithmus = dcError
End Function
Function Exponential(ByVal arg As Variant) As Variant
 
  ' Wert der Exponentialfunktion für <arg>
 
  Dim glied As Variant, e As Variant, i As Long
 
  Exponential = dcError
 
  If Not IsNumeric(arg) Then Exit Function
 
  ' Geltungsbereich der Funktion
  ' ausserhalb dieses Bereichs ist die Genauigkeit nicht gegeben
  ' statt dessen die VB6-Funktion EXP verwenden
  If arg > 40 Or arg < -10 Then Exit Function
 
  glied = CDec(1)
  e = glied
  For i = 1 To 300
    glied = glied * (arg / CDec(i))
    If Abs(glied) <= CDec(0) Then Exit For
    e = e + glied
  Next i
 
  Exponential = e
End Function
Function Sinus(ByVal arg As Variant) As Variant
 
  ' Berechnung des Sinus für einen Winkel im Bogenmaß
 
  Dim sign  As Boolean                ' für Vorzeichen
  Dim px As Variant, x As Variant
  Dim erg As Variant                  ' für Summation
  Dim pglied  As Variant              ' für Summationsglied
  Dim fakultaet As Variant
  Dim i As Long
 
  Dim pi As Variant
  pi = CDec("3,141592653589793238462643383")
 
  If Not IsNumeric(arg) Then Exit Function
 
  x = arg
 
  ' x auf spitzen Winkel zurueckführen
  If x < CDec(0) Then
     x = -x
     sign = True
  End If
 
  While x > CDec(2) * pi
     x = x - CDec(2) * pi
  Wend
 
  If (CDec(1.5) * pi <= x And x <= CDec(2) * pi) Then
    x = CDec(2) * pi - x
    sign = Not sign
  ElseIf pi <= x And x < CDec(1.5) * pi Then
    x = x - pi
    sign = Not sign
  ElseIf CDec(0.5) * pi <= x And x < pi Then
    x = pi - x
  End If
 
  If sign Then x = -x
 
  ' Taylorformel (12 Glieder summieren)
  sign = False
  px = x * x
  fakultaet = 1
  erg = x     ' 1. Glied
  i = 3
  While i <= 27                                   ' mehr geht bei Decimal nicht
    x = x * px                                    ' erhoehe die Potenz von x um 2
    fakultaet = fakultaet * CDec(i) * CDec(i - 1) ' errechne neue, aktuelle Fakultaet}
    pglied = x / fakultaet
    If Not sign Then pglied = -pglied             ' wechselndes Vorzeichen
    erg = erg + pglied                            ' neues Glied aufaddieren
    sign = Not sign
    i = i + 2
 Wend
 
  Sinus = erg  
End Function
Function Array_Summe(arr() As Variant) As Variant
 
  ' Berechnung der Summe der Felder eines Arrays, dessen
  ' Feld-Elemente aus numerischen Datentypen bestehen
 
  Dim i As Long
 
  On Error GoTo fehler
 
  Array_Summe = CDec(0) ' Rückgabe als DECIMAL einstellen
 
  For i = LBound(arr) To UBound(arr)
    If Not IsNumeric(arr(i)) Then
      ' Jedes Feld könnte einen nicht-numerischen
      ' Inhalt besitzen --> Abbruch der Funktion
      Array_Summe = dcError
      Exit Function
    End If
    Array_Summe = Array_Summe + CDec(arr(i))
  Next i
 
  Exit Function
 
fehler:  
  ' Überlauf des Datentyps bzw.
  ' nicht-dimensioniertes Array abfangen
  Array_Summe = dcError
End Function
' Ende des Quellcodes der Demonstrationsbeispiele für den Datentyp DECIMAL

Dieser Tipp wurde bereits 23.585 mal aufgerufen.

Voriger Tipp   |   Zufälliger Tipp   |   Nächster Tipp

Über diesen Tipp im Forum diskutieren
Haben Sie Fragen oder Anregungen zu diesem Tipp, können Sie gerne mit anderen darüber in unserem Forum diskutieren.

Neue Diskussion eröffnen

nach obenzurück


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.
 
   

Druckansicht Druckansicht Copyright ©2000-2024 vb@rchiv Dieter Otter
Alle 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.

Diese Seiten wurden optimiert für eine Bildschirmauflösung von mind. 1280x1024 Pixel