← All changes
|
libraries/vendor/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
+13
-75
3.3.3
→
3.4
View file →
| @@ -49,9 +49,9 @@ | ||
| 49 | 49 | |
| 50 | 50 | return ExcelError::NAN(); |
| 51 | 51 | } |
| 52 | 52 | |
| 53 | - return 1 - (Gamma::incompleteGamma($degrees / 2, $value / 2) / Gamma::gammaValue($degrees / 2)); | |
| 53 | + return Gamma::regularizedGammaQ($degrees / 2, $value / 2); | |
| 54 | 54 | } |
| 55 | 55 | |
| 56 | 56 | /** |
| 57 | 57 | * CHIDIST. |
| @@ -98,10 +98,18 @@ | ||
| 98 | 98 | |
| 99 | 99 | return 1 - (is_numeric($temp) ? $temp : 0); |
| 100 | 100 | } |
| 101 | 101 | |
| 102 | - return ($value ** (($degrees / 2) - 1) * exp(-$value / 2)) | |
| 103 | - / ((2 ** ($degrees / 2)) * Gamma::gammaValue($degrees / 2)); | |
| 102 | + if ($value == 0.0) { | |
| 103 | + if ($degrees === 2) { | |
| 104 | + return 0.5; | |
| 105 | + } | |
| 106 | + | |
| 107 | + return ($degrees === 1) ? INF : 0.0; | |
| 108 | + } | |
| 109 | + | |
| 110 | + // Log domain, so large degrees of freedom cannot overflow Gamma(d/2). | |
| 111 | + return exp((($degrees / 2) - 1) * log($value) - $value / 2 - ($degrees / 2) * M_LN2 - Gamma::logGamma($degrees / 2)); | |
| 104 | 112 | } |
| 105 | 113 | |
| 106 | 114 | /** |
| 107 | 115 | * CHIINV. |
| @@ -132,10 +140,9 @@ | ||
| 132 | 140 | if ($degrees < 1) { |
| 133 | 141 | return ExcelError::NAN(); |
| 134 | 142 | } |
| 135 | 143 | |
| 136 | - $callback = fn (float $value): float => 1 - (Gamma::incompleteGamma($degrees / 2, $value / 2) | |
| 137 | - / Gamma::gammaValue($degrees / 2)); | |
| 144 | + $callback = fn (float $value): float => Gamma::regularizedGammaQ($degrees / 2, $value / 2); | |
| 138 | 145 | |
| 139 | 146 | $newtonRaphson = new NewtonRaphson($callback); |
| 140 | 147 | |
| 141 | 148 | return $newtonRaphson->execute($probability); |
| @@ -258,76 +265,7 @@ | ||
| 258 | 265 | } |
| 259 | 266 | |
| 260 | 267 | private static function pchisq(float $chi2, int $degrees): float |
| 261 | 268 | { |
| 262 | - return self::gammp($degrees, 0.5 * $chi2); | |
| 263 | - } | |
| 264 | - | |
| 265 | - private static function gammp(int $n, float $x): float | |
| 266 | - { | |
| 267 | - if ($x < 0.5 * $n + 1) { | |
| 268 | - return self::gser($n, $x); | |
| 269 | - } | |
| 270 | - | |
| 271 | - return 1 - self::gcf($n, $x); | |
| 272 | - } | |
| 273 | - | |
| 274 | - // Return the incomplete gamma function P(n/2,x) evaluated by | |
| 275 | - // series representation. Algorithm from numerical recipe. | |
| 276 | - // Assume that n is a positive integer and x>0, won't check arguments. | |
| 277 | - // Relative error controlled by the eps parameter | |
| 278 | - private static function gser(int $n, float $x): float | |
| 279 | - { | |
| 280 | - /** @var float $gln */ | |
| 281 | - $gln = Gamma::ln($n / 2); | |
| 282 | - $a = 0.5 * $n; | |
| 283 | - $ap = $a; | |
| 284 | - $sum = 1.0 / $a; | |
| 285 | - $del = $sum; | |
| 286 | - for ($i = 1; $i < 101; ++$i) { | |
| 287 | - ++$ap; | |
| 288 | - $del = $del * $x / $ap; | |
| 289 | - $sum += $del; | |
| 290 | - if ($del < $sum * self::EPS) { | |
| 291 | - break; | |
| 292 | - } | |
| 293 | - } | |
| 294 | - | |
| 295 | - return $sum * exp(-$x + $a * log($x) - $gln); | |
| 296 | - } | |
| 297 | - | |
| 298 | - // Return the incomplete gamma function Q(n/2,x) evaluated by | |
| 299 | - // its continued fraction representation. Algorithm from numerical recipe. | |
| 300 | - // Assume that n is a postive integer and x>0, won't check arguments. | |
| 301 | - // Relative error controlled by the eps parameter | |
| 302 | - private static function gcf(int $n, float $x): float | |
| 303 | - { | |
| 304 | - /** @var float $gln */ | |
| 305 | - $gln = Gamma::ln($n / 2); | |
| 306 | - $a = 0.5 * $n; | |
| 307 | - $b = $x + 1 - $a; | |
| 308 | - $fpmin = 1.e-300; | |
| 309 | - $c = 1 / $fpmin; | |
| 310 | - $d = 1 / $b; | |
| 311 | - $h = $d; | |
| 312 | - for ($i = 1; $i < 101; ++$i) { | |
| 313 | - $an = -$i * ($i - $a); | |
| 314 | - $b += 2; | |
| 315 | - $d = $an * $d + $b; | |
| 316 | - if (abs($d) < $fpmin) { | |
| 317 | - $d = $fpmin; | |
| 318 | - } | |
| 319 | - $c = $b + $an / $c; | |
| 320 | - if (abs($c) < $fpmin) { | |
| 321 | - $c = $fpmin; | |
| 322 | - } | |
| 323 | - $d = 1 / $d; | |
| 324 | - $del = $d * $c; | |
| 325 | - $h = $h * $del; | |
| 326 | - if (abs($del - 1) < self::EPS) { | |
| 327 | - break; | |
| 328 | - } | |
| 329 | - } | |
| 330 | - | |
| 331 | - return $h * exp(-$x + $a * log($x) - $gln); | |
| 269 | + return Gamma::regularizedGammaP($degrees / 2, 0.5 * $chi2); | |
| 332 | 270 | } |
| 333 | 271 | } |