New Bessel Algorithm, providing a higher degree of accuracy and precision (#1946)

* New Bessel Algorithm, providing a higher degree of precision (12 decimal places) and still matching/exceeding MS Excel's precision across the range of values
This commit is contained in:
Mark Baker 2021-03-24 13:29:54 +01:00 committed by GitHub
parent 1a7b9a446a
commit 07ad800755
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1078 additions and 641 deletions

View File

@ -3,7 +3,6 @@
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering; namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
class BesselI class BesselI
{ {
@ -16,9 +15,12 @@ class BesselI
* Excel Function: * Excel Function:
* BESSELI(x,ord) * BESSELI(x,ord)
* *
* @param float $x The value at which to evaluate the function. * NOTE: The MS Excel implementation of the BESSELI function is still not accurate.
* This code provides a more accurate calculation
*
* @param mixed (float) $x The value at which to evaluate the function.
* If x is nonnumeric, BESSELI returns the #VALUE! error value. * If x is nonnumeric, BESSELI returns the #VALUE! error value.
* @param int $ord The order of the Bessel function. * @param mixed (int) $ord The order of the Bessel function.
* If ord is not an integer, it is truncated. * If ord is not an integer, it is truncated.
* If $ord is nonnumeric, BESSELI returns the #VALUE! error value. * If $ord is nonnumeric, BESSELI returns the #VALUE! error value.
* If $ord < 0, BESSELI returns the #NUM! error value. * If $ord < 0, BESSELI returns the #NUM! error value.
@ -28,7 +30,7 @@ class BesselI
public static function BESSELI($x, $ord) public static function BESSELI($x, $ord)
{ {
$x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x); $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
$ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord); $ord = ($ord === null) ? 0 : Functions::flattenSingleValue($ord);
if ((is_numeric($x)) && (is_numeric($ord))) { if ((is_numeric($x)) && (is_numeric($ord))) {
$ord = (int) floor($ord); $ord = (int) floor($ord);
@ -36,7 +38,7 @@ class BesselI
return Functions::NAN(); return Functions::NAN();
} }
$fResult = self::calculate($x, $ord); $fResult = self::calculate((float) $x, $ord);
return (is_nan($fResult)) ? Functions::NAN() : $fResult; return (is_nan($fResult)) ? Functions::NAN() : $fResult;
} }
@ -46,27 +48,87 @@ class BesselI
private static function calculate(float $x, int $ord): float private static function calculate(float $x, int $ord): float
{ {
if (abs($x) <= 30) { // special cases
$fResult = $fTerm = ($x / 2) ** $ord / MathTrig::FACT($ord); switch ($ord) {
$ordK = 1; case 0:
$fSqrX = ($x * $x) / 4; return self::besselI0($x);
do { case 1:
$fTerm *= $fSqrX; return self::besselI1($x);
$fTerm /= ($ordK * ($ordK + $ord));
$fResult += $fTerm;
} while ((abs($fTerm) > 1e-12) && (++$ordK < 100));
return $fResult;
} }
$f_2_PI = 2 * M_PI; return self::besselI2($x, $ord);
}
$fXAbs = abs($x); private static function besselI0(float $x): float
$fResult = exp($fXAbs) / sqrt($f_2_PI * $fXAbs); {
if (($ord & 1) && ($x < 0)) { $ax = abs($x);
$fResult = -$fResult;
if ($ax < 3.75) {
$y = $x / 3.75;
$y = $y * $y;
return 1.0 + $y * (3.5156229 + $y * (3.0899424 + $y * (1.2067492
+ $y * (0.2659732 + $y * (0.360768e-1 + $y * 0.45813e-2)))));
} }
return $fResult; $y = 3.75 / $ax;
return (exp($ax) / sqrt($ax)) * (0.39894228 + $y * (0.1328592e-1 + $y * (0.225319e-2 + $y * (-0.157565e-2
+ $y * (0.916281e-2 + $y * (-0.2057706e-1 + $y * (0.2635537e-1 +
$y * (-0.1647633e-1 + $y * 0.392377e-2))))))));
}
private static function besselI1(float $x): float
{
$ax = abs($x);
if ($ax < 3.75) {
$y = $x / 3.75;
$y = $y * $y;
$ans = $ax * (0.5 + $y * (0.87890594 + $y * (0.51498869 + $y * (0.15084934 + $y * (0.2658733e-1 +
$y * (0.301532e-2 + $y * 0.32411e-3))))));
return ($x < 0.0) ? -$ans : $ans;
}
$y = 3.75 / $ax;
$ans = 0.2282967e-1 + $y * (-0.2895312e-1 + $y * (0.1787654e-1 - $y * 0.420059e-2));
$ans = 0.39894228 + $y * (-0.3988024e-1 + $y * (-0.362018e-2 + $y * (0.163801e-2 +
$y * (-0.1031555e-1 + $y * $ans))));
$ans *= exp($ax) / sqrt($ax);
return ($x < 0.0) ? -$ans : $ans;
}
private static function besselI2(float $x, int $ord): float
{
if ($x === 0.0) {
return 0.0;
}
$tox = 2.0 / abs($x);
$bip = 0;
$ans = 0.0;
$bi = 1.0;
for ($j = 2 * ($ord + (int) sqrt(40.0 * $ord)); $j > 0; --$j) {
$bim = $bip + $j * $tox * $bi;
$bip = $bi;
$bi = $bim;
if (abs($bi) > 1.0e+12) {
$ans *= 1.0e-12;
$bi *= 1.0e-12;
$bip *= 1.0e-12;
}
if ($j === $ord) {
$ans = $bip;
}
}
$ans *= self::besselI0($x) / $bi;
return ($x < 0.0 && (($ord % 2) === 1)) ? -$ans : $ans;
} }
} }

View File

@ -3,7 +3,6 @@
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering; namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
class BesselJ class BesselJ
{ {
@ -15,9 +14,12 @@ class BesselJ
* Excel Function: * Excel Function:
* BESSELJ(x,ord) * BESSELJ(x,ord)
* *
* @param float $x The value at which to evaluate the function. * NOTE: The MS Excel implementation of the BESSELJ function is still not accurate, particularly for higher order
* values with x < -8 and x > 8. This code provides a more accurate calculation
*
* @param mixed (float) $x The value at which to evaluate the function.
* If x is nonnumeric, BESSELJ returns the #VALUE! error value. * If x is nonnumeric, BESSELJ returns the #VALUE! error value.
* @param int $ord The order of the Bessel function. If n is not an integer, it is truncated. * @param mixed (int) $ord The order of the Bessel function. If n is not an integer, it is truncated.
* If $ord is nonnumeric, BESSELJ returns the #VALUE! error value. * If $ord is nonnumeric, BESSELJ returns the #VALUE! error value.
* If $ord < 0, BESSELJ returns the #NUM! error value. * If $ord < 0, BESSELJ returns the #NUM! error value.
* *
@ -34,7 +36,7 @@ class BesselJ
return Functions::NAN(); return Functions::NAN();
} }
$fResult = self::calculate($x, $ord); $fResult = self::calculate((float) $x, $ord);
return (is_nan($fResult)) ? Functions::NAN() : $fResult; return (is_nan($fResult)) ? Functions::NAN() : $fResult;
} }
@ -44,28 +46,123 @@ class BesselJ
private static function calculate(float $x, int $ord): float private static function calculate(float $x, int $ord): float
{ {
if (abs($x) <= 30) { // special cases
$fResult = $fTerm = ($x / 2) ** $ord / MathTrig::FACT($ord); switch ($ord) {
$ordK = 1; case 0:
$fSqrX = ($x * $x) / -4; return self::besselJ0($x);
do { case 1:
$fTerm *= $fSqrX; return self::besselJ1($x);
$fTerm /= ($ordK * ($ordK + $ord));
$fResult += $fTerm;
} while ((abs($fTerm) > 1e-12) && (++$ordK < 100));
return $fResult;
} }
$f_PI_DIV_2 = M_PI / 2; return self::besselJ2($x, $ord);
$f_PI_DIV_4 = M_PI / 4; }
$fXAbs = abs($x); private static function besselJ0(float $x): float
$fResult = sqrt(Functions::M_2DIVPI / $fXAbs) * cos($fXAbs - $ord * $f_PI_DIV_2 - $f_PI_DIV_4); {
if (($ord & 1) && ($x < 0)) { $ax = abs($x);
$fResult = -$fResult;
if ($ax < 8.0) {
$y = $x * $x;
$ans1 = 57568490574.0 + $y * (-13362590354.0 + $y * (651619640.7 + $y * (-11214424.18 + $y *
(77392.33017 + $y * (-184.9052456)))));
$ans2 = 57568490411.0 + $y * (1029532985.0 + $y * (9494680.718 + $y * (59272.64853 + $y *
(267.8532712 + $y * 1.0))));
return $ans1 / $ans2;
} }
return $fResult; $z = 8.0 / $ax;
$y = $z * $z;
$xx = $ax - 0.785398164;
$ans1 = 1.0 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
$ans2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y *
(0.7621095161e-6 - $y * 0.934935152e-7)));
return sqrt(0.636619772 / $ax) * (cos($xx) * $ans1 - $z * sin($xx) * $ans2);
}
private static function besselJ1(float $x): float
{
$ax = abs($x);
if ($ax < 8.0) {
$y = $x * $x;
$ans1 = $x * (72362614232.0 + $y * (-7895059235.0 + $y * (242396853.1 + $y *
(-2972611.439 + $y * (15704.48260 + $y * (-30.16036606))))));
$ans2 = 144725228442.0 + $y * (2300535178.0 + $y * (18583304.74 + $y * (99447.43394 + $y *
(376.9991397 + $y * 1.0))));
return $ans1 / $ans2;
}
$z = 8.0 / $ax;
$y = $z * $z;
$xx = $ax - 2.356194491;
$ans1 = 1.0 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e-6))));
$ans2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y *
(-0.88228987e-6 + $y * 0.105787412e-6)));
$ans = sqrt(0.636619772 / $ax) * (cos($xx) * $ans1 - $z * sin($xx) * $ans2);
return ($x < 0.0) ? -$ans : $ans;
}
private static function besselJ2(float $x, int $ord): float
{
$ax = abs($x);
if ($ax === 0.0) {
return 0.0;
}
if ($ax > $ord) {
return self::besselj2a($ax, $ord, $x);
}
return self::besselj2b($ax, $ord, $x);
}
private static function besselj2a(float $ax, int $ord, float $x)
{
$tox = 2.0 / $ax;
$bjm = self::besselJ0($ax);
$bj = self::besselJ1($ax);
for ($j = 1; $j < $ord; ++$j) {
$bjp = $j * $tox * $bj - $bjm;
$bjm = $bj;
$bj = $bjp;
}
$ans = $bj;
return ($x < 0.0 && ($ord % 2) == 1) ? -$ans : $ans;
}
private static function besselj2b(float $ax, int $ord, float $x)
{
$tox = 2.0 / $ax;
$jsum = false;
$bjp = $ans = $sum = 0.0;
$bj = 1.0;
for ($j = 2 * ($ord + (int) sqrt(40.0 * $ord)); $j > 0; --$j) {
$bjm = $j * $tox * $bj - $bjp;
$bjp = $bj;
$bj = $bjm;
if (abs($bj) > 1.0e+10) {
$bj *= 1.0e-10;
$bjp *= 1.0e-10;
$ans *= 1.0e-10;
$sum *= 1.0e-10;
}
if ($jsum === true) {
$sum += $bj;
}
$jsum = !$jsum;
if ($j === $ord) {
$ans = $bjp;
}
}
$sum = 2.0 * $sum - $bj;
$ans /= $sum;
return ($x < 0.0 && ($ord % 2) === 1) ? -$ans : $ans;
} }
} }

View File

@ -15,9 +15,9 @@ class BesselK
* Excel Function: * Excel Function:
* BESSELK(x,ord) * BESSELK(x,ord)
* *
* @param float $x The value at which to evaluate the function. * @param mixed (float) $x The value at which to evaluate the function.
* If x is nonnumeric, BESSELK returns the #VALUE! error value. * If x is nonnumeric, BESSELK returns the #VALUE! error value.
* @param int $ord The order of the Bessel function. If n is not an integer, it is truncated. * @param mixed (int) $ord The order of the Bessel function. If n is not an integer, it is truncated.
* If $ord is nonnumeric, BESSELK returns the #VALUE! error value. * If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
* If $ord < 0, BESSELK returns the #NUM! error value. * If $ord < 0, BESSELK returns the #NUM! error value.
* *
@ -29,22 +29,13 @@ class BesselK
$ord = ($ord === null) ? 0 : Functions::flattenSingleValue($ord); $ord = ($ord === null) ? 0 : Functions::flattenSingleValue($ord);
if ((is_numeric($x)) && (is_numeric($ord))) { if ((is_numeric($x)) && (is_numeric($ord))) {
if (($ord < 0) || ($x == 0.0)) { $ord = (int) floor($ord);
$x = (float) $x;
if (($ord < 0) || ($x <= 0.0)) {
return Functions::NAN(); return Functions::NAN();
} }
switch (floor($ord)) { $fBk = self::calculate($x, $ord);
case 0:
$fBk = self::besselK0($x);
break;
case 1:
$fBk = self::besselK1($x);
break;
default:
$fBk = self::besselK2($x, $ord);
}
return (is_nan($fBk)) ? Functions::NAN() : $fBk; return (is_nan($fBk)) ? Functions::NAN() : $fBk;
} }
@ -52,38 +43,51 @@ class BesselK
return Functions::VALUE(); return Functions::VALUE();
} }
private static function besselK0(float $fNum): float private static function calculate($x, $ord): float
{ {
if ($fNum <= 2) { // special cases
$fNum2 = $fNum * 0.5; switch (floor($ord)) {
case 0:
return self::besselK0($x);
case 1:
return self::besselK1($x);
}
return self::besselK2($x, $ord);
}
private static function besselK0(float $x): float
{
if ($x <= 2) {
$fNum2 = $x * 0.5;
$y = ($fNum2 * $fNum2); $y = ($fNum2 * $fNum2);
return -log($fNum2) * BesselI::BESSELI($fNum, 0) + return -log($fNum2) * BesselI::BESSELI($x, 0) +
(-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y * (-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y *
(0.10750e-3 + $y * 0.74e-5)))))); (0.10750e-3 + $y * 0.74e-5))))));
} }
$y = 2 / $fNum; $y = 2 / $x;
return exp(-$fNum) / sqrt($fNum) * return exp(-$x) / sqrt($x) *
(1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y * (1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y *
(0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3)))))); (0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3))))));
} }
private static function besselK1(float $fNum): float private static function besselK1(float $x): float
{ {
if ($fNum <= 2) { if ($x <= 2) {
$fNum2 = $fNum * 0.5; $fNum2 = $x * 0.5;
$y = ($fNum2 * $fNum2); $y = ($fNum2 * $fNum2);
return log($fNum2) * BesselI::BESSELI($fNum, 1) + return log($fNum2) * BesselI::BESSELI($x, 1) +
(1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y * (1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y *
(-0.110404e-2 + $y * (-0.4686e-4))))))) / $fNum; (-0.110404e-2 + $y * (-0.4686e-4))))))) / $x;
} }
$y = 2 / $fNum; $y = 2 / $x;
return exp(-$fNum) / sqrt($fNum) * return exp(-$x) / sqrt($x) *
(1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y * (1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y *
(0.325614e-2 + $y * (-0.68245e-3))))))); (0.325614e-2 + $y * (-0.68245e-3)))))));
} }

View File

@ -14,11 +14,11 @@ class BesselY
* Excel Function: * Excel Function:
* BESSELY(x,ord) * BESSELY(x,ord)
* *
* @param float $x The value at which to evaluate the function. * @param mixed (float) $x The value at which to evaluate the function.
* If x is nonnumeric, BESSELY returns the #VALUE! error value. * If x is nonnumeric, BESSELY returns the #VALUE! error value.
* @param int $ord The order of the Bessel function. If n is not an integer, it is truncated. * @param mixed (int) $ord The order of the Bessel function. If n is not an integer, it is truncated.
* If $ord is nonnumeric, BESSELY returns the #VALUE! error value. * If $ord is nonnumeric, BESSELY returns the #VALUE! error value.
* If $ord < 0, BESSELY returns the #NUM! error value. * If $ord < 0, BESSELY returns the #NUM! error value.
* *
* @return float|string Result, or a string containing an error * @return float|string Result, or a string containing an error
*/ */
@ -28,22 +28,13 @@ class BesselY
$ord = ($ord === null) ? 0 : Functions::flattenSingleValue($ord); $ord = ($ord === null) ? 0 : Functions::flattenSingleValue($ord);
if ((is_numeric($x)) && (is_numeric($ord))) { if ((is_numeric($x)) && (is_numeric($ord))) {
if (($ord < 0) || ($x == 0.0)) { $ord = (int) floor($ord);
$x = (float) $x;
if (($ord < 0) || ($x <= 0.0)) {
return Functions::NAN(); return Functions::NAN();
} }
switch (floor($ord)) { $fBy = self::calculate($x, $ord);
case 0:
$fBy = self::besselY0($x);
break;
case 1:
$fBy = self::besselY1($x);
break;
default:
$fBy = self::besselY2($x, $ord);
}
return (is_nan($fBy)) ? Functions::NAN() : $fBy; return (is_nan($fBy)) ? Functions::NAN() : $fBy;
} }
@ -51,46 +42,66 @@ class BesselY
return Functions::VALUE(); return Functions::VALUE();
} }
private static function besselY0(float $fNum): float private static function calculate($x, $ord): float
{ {
if ($fNum < 8.0) { // special cases
$y = ($fNum * $fNum); switch (floor($ord)) {
$f1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y * case 0:
return self::besselY0($x);
case 1:
return self::besselY1($x);
}
return self::besselY2($x, $ord);
}
private static function besselY0(float $x): float
{
if ($x < 8.0) {
$y = ($x * $x);
$ans1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y *
(-86327.92757 + $y * 228.4622733)))); (-86327.92757 + $y * 228.4622733))));
$f2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y * $ans2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y *
(47447.26470 + $y * (226.1030244 + $y)))); (47447.26470 + $y * (226.1030244 + $y))));
return $f1 / $f2 + 0.636619772 * BesselJ::BESSELJ($fNum, 0) * log($fNum); return $ans1 / $ans2 + 0.636619772 * BesselJ::BESSELJ($x, 0) * log($x);
} }
$z = 8.0 / $fNum; $z = 8.0 / $x;
$y = ($z * $z); $y = ($z * $z);
$xx = $fNum - 0.785398164; $xx = $x - 0.785398164;
$f1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6))); $ans1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
$f2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y * $ans2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y *
(-0.934945152e-7)))); (-0.934945152e-7))));
return sqrt(0.636619772 / $fNum) * (sin($xx) * $f1 + $z * cos($xx) * $f2); return sqrt(0.636619772 / $x) * (sin($xx) * $ans1 + $z * cos($xx) * $ans2);
} }
private static function besselY1(float $fNum): float private static function besselY1(float $x): float
{ {
if ($fNum < 8.0) { if ($x < 8.0) {
$y = ($fNum * $fNum); $y = ($x * $x);
$f1 = $fNum * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y * $ans1 = $x * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y *
(0.7349264551e9 + $y * (-0.4237922726e7 + $y * 0.8511937935e4))))); (0.7349264551e9 + $y * (-0.4237922726e7 + $y * 0.8511937935e4)))));
$f2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y * $ans2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y *
(0.1020426050e6 + $y * (0.3549632885e3 + $y))))); (0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
return $f1 / $f2 + 0.636619772 * (BesselJ::BESSELJ($fNum, 1) * log($fNum) - 1 / $fNum); return ($ans1 / $ans2) + 0.636619772 * (BesselJ::BESSELJ($x, 1) * log($x) - 1 / $x);
} }
return sqrt(0.636619772 / $fNum) * sin($fNum - 2.356194491); $z = 8.0 / $x;
$y = $z * $z;
$xx = $x - 2.356194491;
$ans1 = 1.0 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e-6))));
$ans2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y *
(-0.88228987e-6 + $y * 0.105787412e-6)));
return sqrt(0.636619772 / $x) * (sin($xx) * $ans1 + $z * cos($xx) * $ans2);
} }
private static function besselY2(float $x, int $ord) private static function besselY2(float $x, int $ord): float
{ {
$fTox = 2 / $x; $fTox = 2.0 / $x;
$fBym = self::besselY0($x); $fBym = self::besselY0($x);
$fBy = self::besselY1($x); $fBy = self::besselY1($x);
for ($n = 1; $n < $ord; ++$n) { for ($n = 1; $n < $ord; ++$n) {

View File

@ -8,7 +8,7 @@ use PHPUnit\Framework\TestCase;
class BesselITest extends TestCase class BesselITest extends TestCase
{ {
const BESSEL_PRECISION = 1E-8; const BESSEL_PRECISION = 1E-9;
protected function setUp(): void protected function setUp(): void
{ {

View File

@ -8,7 +8,7 @@ use PHPUnit\Framework\TestCase;
class BesselKTest extends TestCase class BesselKTest extends TestCase
{ {
const BESSEL_PRECISION = 1E-8; const BESSEL_PRECISION = 1E-12;
protected function setUp(): void protected function setUp(): void
{ {

View File

@ -8,7 +8,7 @@ use PHPUnit\Framework\TestCase;
class BesselYTest extends TestCase class BesselYTest extends TestCase
{ {
const BESSEL_PRECISION = 1E-8; const BESSEL_PRECISION = 1E-12;
protected function setUp(): void protected function setUp(): void
{ {

View File

@ -1,10 +1,10 @@
<?php <?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\MathTrig; namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcExp; use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcExp;
use PhpOffice\PhpSpreadsheet\Calculation\TextData\Concatenate; use PhpOffice\PhpSpreadsheet\Calculation\TextData;
use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@ -19,7 +19,7 @@ class ReptTest extends TestCase
*/ */
public function testReptDirect($expectedResult, $val = null, $rpt = null): void public function testReptDirect($expectedResult, $val = null, $rpt = null): void
{ {
$result = Concatenate::builtinREPT(is_string($val) ? trim($val, '"') : $val, $rpt); $result = TextData::builtinREPT(is_string($val) ? trim($val, '"') : $val, $rpt);
self::assertEquals($expectedResult, $result); self::assertEquals($expectedResult, $result);
} }

View File

@ -6,208 +6,12 @@ return [
1.5, -1, 1.5, -1,
], ],
[ [
2.249E-5, '#VALUE!',
-1, 6, 'NaN', 1,
],
[
0.0,
0, 3,
],
[
4.8807925900000004,
3, 0,
],
[
0.00027146000000000001,
1, 5,
],
[
0.98166642999999998,
1.5, 1,
],
[
0.33783461999999997,
-1.5, 2.5,
],
[
0.0,
-1.5, 14.99,
],
[
0.0,
1, 30,
],
[
2.51671625,
2.5, 1,
],
[
2.51671625,
2.5, 1.5,
],
[
-2.51671625,
-2.5, 1.5,
],
[
6.20583492,
3.5, 1,
],
[
0.0073673699999999998,
0.69999999999999996, 3,
],
[
3.8320120499999999,
3.5, 2,
], ],
[ [
'#VALUE!', '#VALUE!',
1.5, 'XYZ', 1.5, 'NaN',
],
[
'#VALUE!',
'ABC', 3,
],
[
-1030.9147225199999,
-9, 1,
],
[
-6.20583492,
-3.5, 1,
],
[
-0.39288151999999998,
-0.73499999999999999, 1,
],
[
0.0,
0, 1,
],
[
0.01750268,
0.035000000000000003, 1,
],
[
0.56515910000000003,
1, 1,
],
[
0.98166642999999998,
1.5, 1,
],
[
2.51671625,
2.5, 1,
],
[
6.20583492,
3.5, 1,
],
[
864.49619395000002,
-9, 2,
],
[
3.8320120499999999,
-3.5, 2,
],
[
0.070619940000000006,
-0.73499999999999999, 2,
],
[
0.0,
0, 2,
],
[
0.00015313999999999999,
0.035000000000000003, 2,
],
[
0.10825973,
0.90000000000000002, 2,
],
[
0.13574766999999999,
1, 2,
],
[
0.60327242999999997,
1.8999999999999999, 2,
],
[
1.2764661500000001,
2.5, 2,
],
[
3.8320120499999999,
3.5, 2,
],
[
6.4221893799999998,
4, 2,
],
[
8.8999999999999995E-7,
0.035000000000000003, 3,
],
[
0.0073673699999999998,
0.69999999999999996, 3,
],
[
0.0154285,
0.89000000000000001, 3,
],
[
3.3372757800000001,
4, 3,
],
[
0.50472435999999998,
4, 5,
],
[
2.8410000000000001E-5,
1.5, 7,
],
[
0.00013237000000000001,
3, 9,
],
[
7.3782034300000001,
-3.5, 0,
],
[
1.6467231899999999,
-1.5, 0,
],
[
1.0,
0, 0,
],
[
1.26606588,
1, 0,
],
[
1.6467231899999999,
1.5, 0,
],
[
3.2898391400000002,
2.5, 0,
],
[
7.3782034300000001,
3.5, 0,
],
[
'#NUM!',
-3.5, -1,
], ],
[ [
'#VALUE!', '#VALUE!',
@ -215,10 +19,290 @@ return [
], ],
[ [
'#VALUE!', '#VALUE!',
1, true, 1.5, true,
], ],
[ [
104777847.71856035, 30596.33413506702,
21, 2, -12.5, 0,
],
[
2815.7166648041534,
-10, 0,
],
[
268.161313800963,
-7.5, 0,
],
[
7.37820347757186,
-3.5, 0,
],
[
3.28983917239129,
-2.5, 0,
],
[
1.26606584803426,
-1, 0,
],
[
1.00391006406612,
-0.125, 0,
],
[
1.0,
0, 0,
],
[
1.00391006406612,
0.125, 0,
],
[
1.26606584803426,
1, 0,
],
[
3.28983917239129,
2.5, 0,
],
[
7.37820347757186,
3.5, 0,
],
[
427.564125313702,
8, 0,
],
[
30596.33413506702,
12.5, 0,
],
[
306693019.30610687,
22, 0,
],
[
5590908218797.506,
32, 0,
],
[
-29345.751017210714,
-12.5, 1,
],
[
-2670.988320559247,
-10, 1,
],
[
-249.584367947395,
-7.5, 1,
],
[
-6.205834932063,
-3.5, 1,
],
[
-2.51671624202536,
-2.5, 1,
],
[
-0.565159097581943,
-1, 1,
],
[
-0.062622149768879,
-0.125, 1,
],
[
0.0,
0, 1,
],
[
0.062622149768879,
0.125, 1,
],
[
0.565159097581943,
1, 1,
],
[
2.51671624202536,
2.5, 1,
],
[
6.205834932063,
3.5, 1,
],
[
399.873134789592,
8, 1,
],
[
29345.751017210714,
12.5, 1,
],
[
299639573.1780888,
22, 1,
],
[
5502845662755.76,
32, 1,
],
[
25901.0143489759,
-12.5, 2,
],
[
2281.518997169878,
-10, 2,
],
[
201.605482454266,
-7.5, 2,
],
[
3.83201207162936,
-3.5, 2,
],
[
1.27646615881561,
-2.5, 2,
],
[
0.135747666580699,
-1, 2,
],
[
0.001955669369142,
-0.125, 2,
],
[
0.0,
0, 2,
],
[
0.001955669369142,
0.125, 2,
],
[
0.135747666580699,
1, 2,
],
[
1.27646615881561,
2.5, 2,
],
[
3.83201207162936,
3.5, 2,
],
[
327.595838875391,
8, 2,
],
[
25901.0143489759,
12.5, 2,
],
[
279453053.1335468,
22, 2,
],
[
5246981502816.42,
32, 2,
],
[
-10949.6155627918,
-12.5, 5,
],
[
-777.188296433171,
-10, 5,
],
[
-48.2413608068669,
-7.5, 5,
],
[
-0.223984956078518,
-3.5, 5,
],
[
-0.032843475454962,
-2.5, 5,
],
[
-0.000271463149585,
-1, 5,
],
[
-0.000000007952461,
-0.125, 5,
],
[
0.0,
0, 5,
],
[
0.000000007952461,
0.125, 5,
],
[
0.000271463149585,
1, 5,
],
[
0.032843475454962,
2.5, 5,
],
[
0.223984956078518,
3.5, 5,
],
[
85.5358071768162,
8, 5,
],
[
10949.6155627918,
12.5, 5,
],
[
171888048.06410483,
22, 5,
],
[
3762429557237.2134,
32, 5,
],
[
-0.002645111902465,
-0.5, 3.5,
],
[
-0.257894303289036,
-0.5, 1.5,
],
[
1.06348334399461,
-0.5, 0.5,
],
[
1.06348334399461,
0.5, 0.5,
],
[
0.25789430328903,
0.5, 1.5,
],
[
0.002645111902465,
0.5, 3.5,
],
[
188340493120020360000000000.0,
64, 8,
], ],
]; ];

View File

@ -3,167 +3,307 @@
return [ return [
[ [
'#NUM!', '#NUM!',
1.5, 1.5, -1,
-1,
],
[
0.0,
0,
1,
],
[
0.44005059000000002,
1,
1,
],
[
0.00024976000000000002,
1,
5,
],
[
0.32992572999999997,
1.8999999999999999,
2,
],
[
-0.49709409999999998,
-2.5,
1.5,
],
[
0.13737753,
3.5,
1,
],
[
0.013974,
0.89000000000000001,
3,
],
[
0.45862918000000003,
3.5,
2,
], ],
[ [
'#VALUE!', '#VALUE!',
1.5, 'NaN', 1,
'XYZ',
], ],
[ [
'#VALUE!', '#VALUE!',
'ABC', 1.5, 'NaN',
3,
], ],
[ [
-0.13737753, '#VALUE!',
-3.5, true, 1,
1,
], ],
[ [
-0.34323577999999999, '#VALUE!',
-0.73499999999999999, 1.5, true,
1, ],
[
0.146884054700421,
-12.5, 0,
],
[
-0.245935764451348,
-10, 0,
],
[
0.266339657880379,
-7.5, 0,
],
[
-0.380127739987264,
-3.5, 0,
],
[
-0.048383776468198,
-2.5, 0,
],
[
0.765197686557967,
-1, 0,
],
[
0.996097563041985,
-0.125, 0,
],
[
1.0,
0, 0,
],
[
0.996097563041985,
0.125, 0,
],
[
0.765197686557967,
1, 0,
],
[
-0.048383776468198,
2.5, 0,
],
[
-0.380127739987264,
3.5, 0,
],
[
0.171650807137554,
8, 0,
],
[
0.146884054700421,
12.5, 0,
],
[
-0.120651475704867,
22, 0,
],
[
0.138079009746556,
32, 0,
],
[
0.165483804614760,
-12.5, 1,
],
[
-0.043472746168862,
-10, 1,
],
[
-0.135248427579706,
-7.5, 1,
],
[
-0.137377527362327,
-3.5, 1,
],
[
-0.497094102464274,
-2.5, 1,
],
[
-0.440050585744933,
-1, 1,
],
[
-0.062378009134495,
-0.125, 1,
], ],
[ [
0.0, 0.0,
0, 0, 1,
1,
], ],
[ [
0.01749732, 0.062378009134495,
0.035000000000000003, 0.125, 1,
1,
], ],
[ [
0.55793651, 0.440050585744933,
1.5, 1, 1,
1,
], ],
[ [
0.49709409999999998, 0.497094102464274,
2.5, 2.5, 1,
1,
], ],
[ [
0.13737753, 0.137377527362327,
3.5, 3.5, 1,
1,
], ],
[ [
0.14484733999999999, 0.234636346853915,
-9, 8, 1,
2,
], ],
[ [
0.064538960000000006, -0.165483804614760,
-0.73499999999999999, 12.5, 1,
2, ],
[
0.117177789643852,
22, 1,
],
[
-0.026589028475905,
32, 1,
],
[
-0.173361463438783,
-12.5, 2,
],
[
0.254630313685121,
-10, 2,
],
[
-0.230273410525790,
-7.5, 2,
],
[
0.458629184194308,
-3.5, 2,
],
[
0.446059058439617,
-2.5, 2,
],
[
0.114903484931900,
-1, 2,
],
[
0.001950583109930,
-0.125, 2,
], ],
[ [
0.0, 0.0,
0, 0, 2,
2,
], ],
[ [
0.094586299999999998, 0.001950583109930,
0.90000000000000002, 0.125, 2,
2,
], ],
[ [
0.32992572999999997, 0.114903484931900,
1.8999999999999999, 1, 2,
2,
], ],
[ [
0.00015311, 0.446059058439617,
0.035000000000000003, 2.5, 2,
2,
], ],
[ [
0.45862918000000003, 0.458629184194308,
3.5, 3.5, 2,
2,
], ],
[ [
0.36412814999999998, -0.112991720424075,
4, 8, 2,
2,
], ],
[ [
8.8999999999999995E-7, -0.173361463438783,
0.035000000000000003, 12.5, 2,
3,
], ],
[ [
0.0069296499999999999, 0.131304002036127,
0.69999999999999996, 22, 2,
3,
], ],
[ [
0.013974, -0.139740824026300,
0.89000000000000001, 32, 2,
3,
], ],
[ [
0.43017147, -0.034737699762240,
4, -12.5, 5,
3,
], ],
[ [
0.13208665999999999, 0.234061528186794,
4, -10, 5,
5,
], ],
[ [
2.4680000000000001E-5, -0.283473905162551,
1.5, -7.5, 5,
7,
], ],
[ [
8.4400000000000005E-5, -0.080441986647992,
3, -3.5, 5,
9,
], ],
[
-0.019501625134503,
-2.5, 5,
],
[
-0.000249757730211,
-1, 5,
],
[
-0.000000007942113,
-0.125, 5,
],
[
0.0,
0, 5,
],
[
0.000000007942113,
0.125, 5,
],
[
0.000249757730211,
1, 5,
],
[
0.019501625134503,
2.5, 5,
],
[
0.080441986647992,
3.5, 5,
],
[
0.185774772190564,
8, 5,
],
[
0.034737699762240,
12.5, 5,
],
[
0.036304102444491,
22, 5,
],
[
0.026241347352986,
32, 5,
],
[
-0.002563729994587,
-0.5, 3.5,
],
[
-0.242268457674874,
-0.5, 1.5,
],
[
0.938469807240813,
-0.5, 0.5,
],
[
0.938469807240813,
0.5, 0.5,
],
[
0.242268457674874,
0.5, 1.5,
],
[
0.002563729994587,
0.5, 3.5,
],
[
0.063689476134356,
64, 8,
],
]; ];

View File

@ -3,192 +3,174 @@
return [ return [
[ [
'#NUM!', '#NUM!',
1.5, 1.5, -1,
-1,
],
[
'#NUM!',
0,
2,
],
[
7990.0124327800004,
0.10000000000000001,
3,
],
[
0.42102444,
1,
0,
],
[
0.21380557,
1.5,
0,
],
[
'#NUM!',
-1.5,
2,
],
[
0.27738780000000002,
1.5,
1,
],
[
0.58365597000000002,
1.5,
2,
],
[
0.094982449999999996,
2.2999999999999998,
1.5,
],
[
0.073890819999999996,
2.5,
1,
],
[
0.022239390000000001,
3.5,
1,
],
[
0.059161819999999997,
3.5,
3,
],
[
397.95880105999998,
3,
9,
],
[
0.032307120000000002,
3.5,
2,
], ],
[ [
'#VALUE!', '#VALUE!',
1.5, 'NaN', 1,
'XYZ',
], ],
[ [
'#VALUE!', '#VALUE!',
'ABC', 1.5, 'NaN',
3,
], ],
[ [
'#NUM!', '#NUM!',
-3.5, -1.5, 1,
1,
], ],
[ [
'#NUM!', '#NUM!',
-0.73499999999999999, 0.0, 1,
1,
], ],
[ [
'#NUM!', '#VALUE!',
0, true, 1,
1,
], ],
[ [
28.50197, '#VALUE!',
0.035000000000000003, 1.5, true,
1,
], ],
[ [
0.27738780000000002, 2.20786908479938,
1.5, 0.125, 0,
1,
], ],
[ [
0.073890819999999996, 0.421024421083418,
2.5, 1, 0,
1,
], ],
[ [
0.022239390000000001, 0.062347554191019,
3.5, 2.5, 0,
1,
], ],
[ [
'#NUM!', 0.019598896971074,
-9, 3.5, 0,
2,
], ],
[ [
'#NUM!', 0.000146470701181,
-0.73499999999999999, 8, 0,
2,
], ],
[ [
'#NUM!', 0.000001308403709,
0, 12.5, 0,
2,
], ],
[ [
2.0790271499999999, 0.000000000074124,
0.90000000000000002, 22, 0,
2,
], ],
[ [
0.29690929999999999, 0.000000000000003,
1.8999999999999999, 32, 0,
2,
], ],
[ [
1632.1537072900001, 7.83111830250218,
0.035000000000000003, 0.125, 1,
2,
], ],
[ [
0.032307120000000002, 0.601907231666906,
3.5, 1, 1,
2,
], ],
[ [
0.017401429999999999, 0.073890815650267,
4, 2.5, 1,
2,
], ],
[ [
186560.35423214, 0.022239393224641,
0.035000000000000003, 3.5, 1,
3,
], ],
[ [
21.972169050000002, 0.000155369216605,
0.69999999999999996, 8, 1,
3,
], ],
[ [
10.31747315, 0.000001359767834,
0.89000000000000001, 12.5, 1,
3,
], ],
[ [
0.029884919999999999, 0.00000000007579,
4, 22, 1,
3,
], ],
[ [
0.15434255, 0.000000000000003,
4, 32, 1,
5,
], ],
[ [
2457.7004395499998, 127.505761924834,
1.5, 0.125, 2,
7,
], ],
[ [
397.95880105999998, 1.62483888441723,
3, 1, 2,
9, ],
[
0.121460206711233,
2.5, 2,
],
[
0.032307121670869,
3.5, 2,
],
[
0.000185313005332,
8, 2,
],
[
0.000001525966562,
12.5, 2,
],
[
0.000000000081014,
22, 2,
],
[
0.000000000000003,
32, 2,
],
[
12570631.999947274,
0.125, 5,
],
[
360.960586772793,
1, 5,
],
[
2.71688429212586,
2.5, 5,
],
[
0.364824403327597,
3.5, 5,
],
[
0.000619358014056,
8, 5,
],
[
0.00000339242503,
12.5, 5,
],
[
0.000000000128956,
22, 5,
],
[
0.000000000000004,
32, 5,
],
[
0.924419035021323,
0.5, 0.5,
],
[
1.65644112801108,
0.5, 1.5,
],
[
62.0579095045362,
0.5, 3.5,
],
[
0.0,
64, 8,
], ],
]; ];

View File

@ -3,117 +3,174 @@
return [ return [
[ [
'#NUM!', '#NUM!',
1.5, 1.5, -1,
-1,
],
[
0.49807035999999999,
2.5,
0,
],
[
0.14591814,
2.5,
1,
],
[
-0.38133584999999998,
2.5,
2,
],
[
0.41018842,
3.5,
1,
],
[
-0.35833535,
3.5,
3,
],
[
0.045371439999999999,
3.5,
2,
],
[
-0.17121431000000001,
12.5,
0,
], ],
[ [
'#VALUE!', '#VALUE!',
1.5, 'NaN', 1,
'XYZ',
], ],
[ [
'#VALUE!', '#VALUE!',
'ABC', 1.5, 'NaN',
3,
], ],
[ [
'#NUM!', '#NUM!',
-3.5, -1.5, 1,
1,
], ],
[ [
'#NUM!', '#NUM!',
-0.73499999999999999, 0.0, 1,
1,
], ],
[ [
'#NUM!', '#VALUE!',
0, true, 1,
1,
], ],
[ [
-0.41230863000000001, '#VALUE!',
1.5, 1.5, true,
1,
], ],
[ [
0.14591814, -1.38968063456627,
2.5, 0.125, 0,
1,
], ],
[ [
0.41018842, 0.088256971397708,
3.5, 1, 0,
1,
], ],
[ [
'#NUM!', 0.498070358446689,
-9, 2.5, 0,
2,
], ],
[ [
'#NUM!', 0.189021944512578,
-0.73499999999999999, 3.5, 0,
2,
], ],
[ [
'#NUM!', 0.223521489249681,
0, 8, 0,
2,
], ],
[ [
-1.9459096, -0.171214306884316,
0.90000000000000002, 12.5, 0,
2,
], ],
[ [
-0.66987867999999995, 0.119887597856641,
1.8999999999999999, 22, 0,
2,
], ],
[ [
0.045371439999999999, -0.028742484734472,
3.5, 32, 0,
2,
], ],
[ [
-0.79585141999999998, -5.19993610955387,
4, 0.125, 1,
5, ],
[
-0.78121282095312,
1, 1,
],
[
0.145918137508313,
2.5, 1,
],
[
0.410188416627698,
3.5, 1,
],
[
-0.158060461835146,
8, 1,
],
[
-0.153838256351639,
12.5, 1,
],
[
0.123405856078544,
22, 1,
],
[
-0.138544831448816,
32, 1,
],
[
-81.8092971182956,
0.125, 2,
],
[
-1.65068261330395,
1, 2,
],
[
-0.381335848440038,
2.5, 2,
],
[
0.045371436417535,
3.5, 2,
],
[
-0.263036604708467,
8, 2,
],
[
0.146600185868054,
12.5, 2,
],
[
-0.108668883667682,
22, 2,
],
[
0.020083432768921,
32, 2,
],
[
-8018358.447601137,
0.125, 5,
],
[
-260.405867809914,
1, 5,
],
[
-3.83017599167454,
2.5, 5,
],
[
-1.14946031467642,
3.5, 5,
],
[
0.256401064838239,
8, 5,
],
[
-0.232903937762173,
12.5, 5,
],
[
0.16848172635078,
22, 5,
],
[
-0.139464153399118,
32, 5,
],
[
-0.444518733762708,
0.5, 0.5,
],
[
-1.47147239186729,
0.5, 1.5,
],
[
-42.0594942777845,
0.5, 3.5,
],
[
0.077260138596538,
64, 8,
], ],
]; ];