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. Zunächst die Definition des Typs, wie sie die VB6-Dokumentation liefert: 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. 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
Anzeige
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. |
sevISDN 1.0 Überwachung aller eingehender Anrufe! Die DLL erkennt alle über die CAPI-Schnittstelle eingehenden Anrufe und teilt Ihnen sogar mit, aus welchem Ortsbereich der Anruf stammt. Weitere Highlights: Online-Rufident, Erkennung der Anrufbehandlung u.v.m. Tipp des Monats April 2024 Skyfloy Chart von Microsoft und dazu noch gratis Tutorial für Microsoft Chart Controls für Microsoft .NET Framework 3.5 Neu! sevCommand 4.0 Professionelle Schaltflächen im modernen Design! Mit nur wenigen Mausklicks statten auch Sie Ihre Anwendungen ab sofort mit grafischen Schaltflächen im modernen Look & Feel aus (WinXP, Office, Vista oder auch Windows 8), inkl. große Symbolbibliothek. |
||||||||||||||||
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. |