1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.numbers.gamma; 18 19 /** 20 * Special math functions. 21 * 22 * @since 1.1 23 */ 24 final class SpecialMath { 25 /** Minimum x for log1pmx(x). */ 26 private static final double X_MIN = -1; 27 /** Low threshold to use log1p(x) - x. */ 28 private static final double X_LOW = -0.79149064; 29 /** High threshold to use log1p(x) - x. */ 30 private static final double X_HIGH = 1; 31 /** 2^-6. */ 32 private static final double TWO_POW_M6 = 0x1.0p-6; 33 /** 2^-12. */ 34 private static final double TWO_POW_M12 = 0x1.0p-12; 35 /** 2^-20. */ 36 private static final double TWO_POW_M20 = 0x1.0p-20; 37 /** 2^-53. */ 38 private static final double TWO_POW_M53 = 0x1.0p-53; 39 40 /** Private constructor. */ 41 private SpecialMath() { 42 // intentionally empty. 43 } 44 45 /** 46 * Returns {@code log(1 + x) - x}. This function is accurate when {@code x -> 0}. 47 * 48 * <p>This function uses a Taylor series expansion when x is small ({@code |x| < 0.01}): 49 * 50 * <pre> 51 * ln(1 + x) - x = -x^2/2 + x^3/3 - x^4/4 + ... 52 * </pre> 53 * 54 * <p>or around 0 ({@code -0.791 <= x <= 1}): 55 * 56 * <pre> 57 * ln(x + a) = ln(a) + 2 [z + z^3/3 + z^5/5 + z^7/7 + ... ] 58 * 59 * z = x / (2a + x) 60 * </pre> 61 * 62 * <p>For a = 1: 63 * 64 * <pre> 65 * ln(x + 1) - x = -x + 2 [z + z^3/3 + z^5/5 + z^7/7 + ... ] 66 * = z * (-x + 2z^2 [ 1/3 + z^2/5 + z^4/7 + ... ]) 67 * </pre> 68 * 69 * <p>The code is based on the {@code log1pmx} documentation for the <a 70 * href="https://rdrr.io/rforge/DPQ/man/log1pmx.html">R DPQ package</a> with addition of the 71 * direct Taylor series for tiny x. 72 * 73 * <p>See Abramowitz, M. and Stegun, I. A. (1972) Handbook of Mathematical Functions. New York: 74 * Dover. Formulas 4.1.24 and 4.2.29, p.68. <a 75 * href="https://en.wikipedia.org/wiki/Abramowitz_and_Stegun">Wikipedia: Abramowitz_and_Stegun</a> 76 * provides links to the full text which is in public domain. 77 * 78 * @param x Value x 79 * @return {@code log(1 + x) - x} 80 */ 81 static double log1pmx(double x) { 82 // -1 is the minimum supported value 83 if (x <= X_MIN) { 84 return x == X_MIN ? Double.NEGATIVE_INFINITY : Double.NaN; 85 } 86 // Use the thresholds documented in the R implementation 87 if (x < X_LOW || x > X_HIGH) { 88 return Math.log1p(x) - x; 89 } 90 final double a = Math.abs(x); 91 92 // Addition to the R version for small x. 93 // Use a direct Taylor series: 94 // ln(1 + x) = x - x^2/2 + x^3/3 - x^4/4 + ... 95 if (a < TWO_POW_M6) { 96 return log1pmxSmall(x, a); 97 } 98 99 // The use of the following series is fast converging: 100 // ln(x + 1) - x = -x + 2 [z + z^3/3 + z^5/5 + z^7/7 + ... ] 101 // = z * (-x + 2z^2 [ 1/3 + z^2/5 + z^4/7 + ... ]) 102 // z = x / (2 + x) 103 // 104 // Tests show this is more accurate when |x| > 1e-4 than the direct Taylor series. 105 // The direct series can be modified to sum multiple terms together for a small 106 // increase in precision to a closer match to this variation but the direct series 107 // takes approximately 3x longer to converge. 108 109 final double z = x / (2 + x); 110 final double zz = z * z; 111 112 // Series sum 113 // sum(k=0,...,Inf; zz^k/(3+k*2)) = 1/3 + zz/5 + zz^2/7 + zz^3/9 + ... ) 114 115 double sum = 1.0 / 3; 116 double numerator = 1; 117 int denominator = 3; 118 for (;;) { 119 numerator *= zz; 120 denominator += 2; 121 final double sum2 = sum + numerator / denominator; 122 // Since |x| <= 1 the additional terms will reduce in magnitude. 123 // Iterate until convergence. Expected iterations: 124 // x iterations 125 // -0.79 38 126 // -0.5 15 127 // -0.1 5 128 // 0.1 5 129 // 0.5 10 130 // 1.0 15 131 if (sum2 == sum) { 132 break; 133 } 134 sum = sum2; 135 } 136 return z * (2 * zz * sum - x); 137 } 138 139 /** 140 * Returns {@code log(1 + x) - x}. This function is accurate when 141 * {@code x -> 0}. 142 * 143 * <p>This function uses a Taylor series expansion when x is small 144 * ({@code |x| < 0.01}): 145 * 146 * <pre> 147 * ln(1 + x) - x = -x^2/2 + x^3/3 - x^4/4 + ... 148 * </pre> 149 * 150 * <p>No loop iterations are used as the series is directly expanded 151 * for a set number of terms based on the absolute value of x. 152 * 153 * @param x Value x (assumed to be small) 154 * @param a Absolute value of x 155 * @return {@code log(1 + x) - x} 156 */ 157 private static double log1pmxSmall(double x, double a) { 158 // Use a direct Taylor series: 159 // ln(1 + x) = x - x^2/2 + x^3/3 - x^4/4 + ... 160 // Reverse the summation (small to large) for a marginal increase in precision. 161 // To stop the Taylor series the next term must be less than 1 ulp from the 162 // answer. 163 // x^n/n < |log(1+x)-x| * eps 164 // eps = machine epsilon = 2^-53 165 // x^n < |log(1+x)-x| * eps 166 // n < (log(|log(1+x)-x|) + log(eps)) / log(x) 167 // In practice this is a conservative limit. 168 169 final double x2 = x * x; 170 171 if (a < TWO_POW_M53) { 172 // Below machine epsilon. Addition of x^3/3 is not possible. 173 // Subtract from zero to prevent creating -0.0 for x=0. 174 return 0 - x2 / 2; 175 } 176 177 final double x4 = x2 * x2; 178 179 // +/-9.5367431640625e-07: log1pmx = -4.547470617660916e-13 : 180 // -4.5474764000725028e-13 181 // n = 4.69 182 if (a < TWO_POW_M20) { 183 // n=5 184 return x * x4 / 5 - 185 x4 / 4 + 186 x * x2 / 3 - 187 x2 / 2; 188 } 189 190 // +/-2.44140625E-4: log1pmx = -2.9797472637290841e-08 : -2.9807173914456693e-08 191 // n = 6.49 192 if (a < TWO_POW_M12) { 193 // n=7 194 return x * x2 * x4 / 7 - 195 x2 * x4 / 6 + 196 x * x4 / 5 - 197 x4 / 4 + 198 x * x2 / 3 - 199 x2 / 2; 200 } 201 202 // Assume |x| < 2^-6 203 // +/-0.015625: log1pmx = -0.00012081346403474586 : -0.00012335696813916864 204 // n = 10.9974 205 206 // n=11 207 final double x8 = x4 * x4; 208 return x * x2 * x8 / 11 - 209 x2 * x8 / 10 + 210 x * x8 / 9 - 211 x8 / 8 + 212 x * x2 * x4 / 7 - 213 x2 * x4 / 6 + 214 x * x4 / 5 - 215 x4 / 4 + 216 x * x2 / 3 - 217 x2 / 2; 218 } 219 }