Rubrik: Variablen/Strings · Algorithmen/Mathematik | VB-Versionen: VB2005, VB2008 | 29.12.09 |
Logarithmus mit hoher Genauigkeit Auf der Basis des Datentyps Decimal kann das Napier-Verfahren Logarithmen mit einer Genauigkeit von 25 Stellen berechnen | ||
Autor: Manfred Bohn | Bewertung: | Views: 11.097 |
ohne Homepage | System: Win2k, WinXP, Win7, Win8, Win10, Win11 | kein Beispielprojekt |
Zur Berechnung des natürlichen Logarithmus einer Zahl steht in Net-VB die Funktion "Log" (Namespace: System.Math) zur Verfügung. Sie akzeptiert als Argument Variable des Datentyps "Double" und berechnet den nat. Logarithmus mit einer Genauigkeit von etwa 14-15 Stellen.
Eine höhere Genauigkeit (mind. 25 Stellen) lässt sich erzielen, wenn man auf den Datentyp "Decimal" zurückgreift und Logarithmen-Werte durch eine geeignete Reihenentwicklung approximiert (z.B. nach Napier).
Für mich überraschend, arbeitet beim Datentyp "Decimal" der BKM-Algorithmus, der auf eine Logarithmentabelle zurückgreift, weniger effizient als das "klassische" Napier-Verfahren, das in vielen Fällen bereits nach der Berechnung von wenigen Termen den genauen Logarithmus liefert.
Eine hohe Genauigkeit des Logarithmus lässt sich allerdings nur erzielen, wenn der Wert des Arguments im Datentyp "Decimal" exakt dargestellt werden kann. Bei Dezimalbrüchen kann es zu kleinen Ungenauigkeiten kommen, die die Präzision des Logarithmus um 1-3 Stellen herabsetzen. Kann der Dezimalbruch als gewöhnlicher Bruch dargestellt werden, ist die Berechnung der Differenz zwischen dem Zähler-Log und dem Nenner-Log genauer.
Einschränkungen des Datentyps "Decimal" bei der Verarbeitung von kleinen Werten sind zu beachten:
Decimal-Variable kennen keine Exponentialdarstellung. Der kleinste darstellbare Decimal-Wert größer 0 beträgt etwa 1E-28. Logarithmen für Werte, die nur wenig über 0 liegen (z.B. Epsilon-Werte bei Vergleichsoperationen), können deshalb durch die Funktion "LN_Napier" evt. nicht präzise bestimmt werden. Erst bei Argumenten ab 0.00001 ist eine auf etwa 25 Stellen präzise Abbildung gewährleistet. Der optionale Parameter "AcceptEpsilonArgs" gibt an, ob "LN_Napier" Argumente nahe 0 akzeptieren soll. Die VB-Funktion "Math.Log" liefert bei vielstelligen Double-Argumenten kleiner +1E-13 genauere (Double-) Ergebnisse als "LN_Napier" (wg. der Zahl der erforderlichen Nachkomma-Nullen).
Anwendungsbeispiel für den natürlichen Logarithmus des Dezimalbruchs 2 / 3:
("Exakter" Wert: -0,4054651081081643819780131154643491365719904 ....)
Math.Log(2# / 3#) ' --> -0.40546510810816444# (15 Stellen) Math.Log(2#) - Math.Log(3#) --> -' 0.4054651081081645# (15 Stellen) LN_Napier(2D) - LN_Napier(3D) ' --> -0.4054651081081643819780131154D (28 Stellen) LN_Napier(2D / 3D) ' --> -0.4054651081081643819780131142D (26 Stellen)
Anwendungsbeispiel für den natürlichen Logarithmus des Epsilonwertes 1.23456789E-25:
("Exakter" Wert: -57,3539063026354895393997846566208146411226672837 ....)
Dieser Wert ist als Decimal zwar noch darstellbar, aber nur als 0.0000000000000000000000001235D
LN_Napier(CDec(1.23456789E-25#), True) ' --> -57.353556354771201581840486473D (5 Stellen !!)
Als Double-Wert ist eine exakte Darstellung möglich: 1.23456789E-25
Math.Log(1.23456789E-25#) ' --> -57.353906302635487 (15 +1 Stellen)
Der größte berechenbare natürliche Logarithmus:
LN_Napier(Decimal.MaxValue) ' --> 66.542129333754749704054283602D (27 Stellen; über 100 Terme)
''' <summary>Logarithmus eines positiven Decimal-Wertes (n. Napier)</summary> ''' <param name="arg">Argument (größer 0)</param> ''' <param name="AcceptEpsilonArgs">Sollen Argument kleiner 0.00001 ''' akzeptiert werden</param> ''' <param name="steps">Zahl der erforderlichen Terme</param> ''' <returns>natürlicher Logarithmus des Arguments</returns> Public Function LN_Napier(ByVal arg As Decimal, _ Optional ByVal AcceptEpsilonArgs As Boolean = False, _ Optional ByRef steps As Integer = -1) As Decimal Const epsilon As Decimal = 0.0000000000000000000000000001D Const ln10 As Decimal = 2.3025850929940456840179914526D If arg <= 0D Then Throw New ArgumentOutOfRangeException("Logarithmus für negativen Wert undefiniert") End If If Not AcceptEpsilonArgs Then If arg < 0.00001 Then Throw New ArgumentException("Logarithmus: Wert nahe 0 ist nicht zugelassen") End If End If ' ggf. Transformation des Arguments (Bereich 1-10) Dim argkorr As Decimal = 0D While arg > 10 arg /= 10 : argkorr += 1 End While While arg < 1 arg *= 10 : argkorr -= 1 End While Dim helpfactor As Decimal = _ ((arg - 1D) * (arg - 1D)) / ((arg + 1D) * (arg + 1D)) ' Startwerte Dim log As Decimal = (arg - 1) / (arg + 1) Dim bruch As Decimal = log Dim add As Decimal, index As Decimal = 1D steps = 1 Do steps += 1 index += 2D bruch *= helpfactor add = bruch / index log += add Loop While Math.Abs(add) > epsilon Return (log * 2D) + argkorr * ln10 End Function
''' <summary>Logarithmus eines positiven Decimal-Wertes</summary> ''' <param name="arg">Argument (größer 0)</param> ''' <param name="base">Basis des Logarithmus (größer 0)</param> ''' <returns>Logarithmus des Arguments zur Basis</returns> Public Function Log_Napier(ByVal arg As Decimal, _ ByVal base As Decimal, _ Optional ByVal AcceptEpsilonValues As Boolean = False) As Decimal Return LN_Napier(arg, AcceptEpsilonValues) / _ LN_Napier(base, AcceptEpsilonValues) End Function