Ln calculates natural logarithm of d. Precision argument specifies how precise the result must be (number of digits after decimal point). Negative precision is allowed. Example: d1, err := NewFromFloat(13.3).Ln(2) d1.String() // output: "2.59" d2, err := NewFromFloat(579.161).Ln(10) d2.Strin
(precision int32)
| 1119 | // d2, err := NewFromFloat(579.161).Ln(10) |
| 1120 | // d2.String() // output: "6.3615805046" |
| 1121 | func (d Decimal) Ln(precision int32) (Decimal, error) { |
| 1122 | // Algorithm based on The Use of Iteration Methods for Approximating the Natural Logarithm, |
| 1123 | // James F. Epperson, The American Mathematical Monthly, Vol. 96, No. 9, November 1989, pp. 831-835. |
| 1124 | if d.IsNegative() { |
| 1125 | return Decimal{}, fmt.Errorf("cannot calculate natural logarithm for negative decimals") |
| 1126 | } |
| 1127 | |
| 1128 | if d.IsZero() { |
| 1129 | return Decimal{}, fmt.Errorf("cannot represent natural logarithm of 0, result: -infinity") |
| 1130 | } |
| 1131 | |
| 1132 | calcPrecision := precision + 2 |
| 1133 | z := d.Copy() |
| 1134 | |
| 1135 | var comp1, comp3, comp2, comp4, reduceAdjust Decimal |
| 1136 | comp1 = z.Sub(Decimal{oneInt, 0}) |
| 1137 | comp3 = Decimal{oneInt, -1} |
| 1138 | |
| 1139 | // for decimal in range [0.9, 1.1] where ln(d) is close to 0 |
| 1140 | usePowerSeries := false |
| 1141 | |
| 1142 | if comp1.Abs().Cmp(comp3) <= 0 { |
| 1143 | usePowerSeries = true |
| 1144 | } else { |
| 1145 | // reduce input decimal to range [0.1, 1) |
| 1146 | expDelta := int32(z.NumDigits()) + z.exp |
| 1147 | z.exp -= expDelta |
| 1148 | |
| 1149 | // Input decimal was reduced by factor of 10^expDelta, thus we will need to add |
| 1150 | // ln(10^expDelta) = expDelta * ln(10) |
| 1151 | // to the result to compensate that |
| 1152 | ln10 := ln10.withPrecision(calcPrecision) |
| 1153 | reduceAdjust = NewFromInt32(expDelta) |
| 1154 | reduceAdjust = reduceAdjust.Mul(ln10) |
| 1155 | |
| 1156 | comp1 = z.Sub(Decimal{oneInt, 0}) |
| 1157 | |
| 1158 | if comp1.Abs().Cmp(comp3) <= 0 { |
| 1159 | usePowerSeries = true |
| 1160 | } else { |
| 1161 | // initial estimate using floats |
| 1162 | zFloat := z.InexactFloat64() |
| 1163 | comp1 = NewFromFloat(math.Log(zFloat)) |
| 1164 | } |
| 1165 | } |
| 1166 | |
| 1167 | epsilon := Decimal{oneInt, -calcPrecision} |
| 1168 | |
| 1169 | if usePowerSeries { |
| 1170 | // Power Series - https://en.wikipedia.org/wiki/Logarithm#Power_series |
| 1171 | // Calculating n-th term of formula: ln(z+1) = 2 sum [ 1 / (2n+1) * (z / (z+2))^(2n+1) ] |
| 1172 | // until the difference between current and next term is smaller than epsilon. |
| 1173 | // Coverage quite fast for decimals close to 1.0 |
| 1174 | |
| 1175 | // z + 2 |
| 1176 | comp2 = comp1.Add(Decimal{twoInt, 0}) |
| 1177 | // z / (z + 2) |
| 1178 | comp3 = comp1.DivRound(comp2, calcPrecision) |