From 8ca594e5fa48bd0a4e1596e709ee399c2f29df97 Mon Sep 17 00:00:00 2001 From: codisart Date: Thu, 15 Oct 2015 17:13:48 +0200 Subject: [PATCH] [TECH] Add the arcsine method Add the arcsine method for the Decimal class and the tests --- src/Decimal.php | 81 +++++++++++++++++++++++++++++ tests/Decimal/DecimalArcsinTest.php | 50 ++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 tests/Decimal/DecimalArcsinTest.php diff --git a/src/Decimal.php b/src/Decimal.php index 3e440a0..2acd99b 100644 --- a/src/Decimal.php +++ b/src/Decimal.php @@ -778,7 +778,38 @@ public function sec($scale = null) return DecimalConstants::one()->div($cos)->round($scale); } + /** + * Calculates the arcsine of this with the highest possible accuracy + * + * @param integer $scale [description] + * @return Decimal + */ + public function arcsin($scale = null) + { + if($this->comp(DecimalConstants::one(), $scale + 2) === 1 || $this->comp(DecimalConstants::negativeOne(), $scale + 2) === -1) { + throw new \DomainException( + "The arcsin of this number is undefined." + ); + } + + if ($this->round($scale)->isZero()) { + return DecimalConstants::zero; + } + if ($this->round($scale)->equals(DecimalConstants::one())) { + return DecimalConstants::pi()->div(Decimal::fromInteger(2))->round($scale); + } + if ($this->round($scale)->equals(DecimalConstants::negativeOne())) { + return DecimalConstants::pi()->div(Decimal::fromInteger(-2))->round($scale); + } + $scale = ($scale === null) ? 32 : $scale; + + return self::powerSerie( + $this, + DecimalConstants::zero(), + $scale + ); + } /** * Returns exp($this), said in other words: e^$this . * @@ -834,6 +865,56 @@ private static function factorialSerie (Decimal $x, Decimal $firstTerm, callable return $approx->round($scale); } + + /** + * Internal method used to compute arcsine * + * + * @param Decimal $x + * @param Decimal $firstTerm + * @param $scale + * @return Decimal + */ + private static function powerSerie (Decimal $x, Decimal $firstTerm, $scale) + { + $approx = $firstTerm; + $change = InfiniteDecimal::getPositiveInfinite(); + + $xPowerN = DecimalConstants::One(); // Calculates x^n + $factorN = DecimalConstants::One(); // Calculates a_n + + $numerator = DecimalConstants::one(); + $denominator = DecimalConstants::one(); + + for ($i = 1; !$change->floor($scale + 2)->isZero(); $i++) { + $xPowerN = $xPowerN->mul($x); + + if ($i % 2 == 0) { + $factorN = DecimalConstants::zero(); + } elseif ($i == 1) { + $factorN = DecimalConstants::one(); + } else { + $incrementNum = Decimal::fromInteger($i - 2); + $numerator = $numerator->mul($incrementNum, $scale +2); + + $incrementDen = Decimal::fromInteger($i - 1); + $increment = Decimal::fromInteger($i); + $denominator = $denominator + ->div($incrementNum, $scale +2) + ->mul($incrementDen, $scale +2) + ->mul($increment, $scale +2); + + $factorN = $numerator->div($denominator, $scale + 2); + } + + if (!$factorN->isZero()) { + $change = $factorN->mul($xPowerN, $scale + 2); + $approx = $approx->add($change, $scale + 2); + } + } + + return $approx->round($scale); + } + /** * Calculates the tangent of this method with the highest possible accuracy * Note that accuracy is limited by the accuracy of predefined PI; diff --git a/tests/Decimal/DecimalArcsinTest.php b/tests/Decimal/DecimalArcsinTest.php new file mode 100644 index 0000000..85dd457 --- /dev/null +++ b/tests/Decimal/DecimalArcsinTest.php @@ -0,0 +1,50 @@ +arcsin($digits); + + $this->assertTrue( + Decimal::fromString($answer)->equals($arcsinX), + "The answer must be " . $answer . ", but was " . $arcsinX + ); + } + + /** + * @expectedException \DomainException + * @expectedExceptionMessage The arcsin of this number is undefined. + */ + public function testArcsinGreaterThanOne() + { + Decimal::fromString('25.546')->arcsin(); + } + + /** + * @expectedException \DomainException + * @expectedExceptionMessage The arcsin of this number is undefined. + */ + public function testArcsinFewerThanNegativeOne() + { + Decimal::fromString('-304.75')->arcsin(); + } +}