Skip to content
This repository was archived by the owner on Dec 27, 2023. It is now read-only.

Commit adbd243

Browse files
committed
Merge pull request #44 from codisart/add-arcsin-method
[TECH] Add the arcsine method
2 parents 91cf80a + 8ca594e commit adbd243

File tree

2 files changed

+131
-0
lines changed

2 files changed

+131
-0
lines changed

src/Decimal.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,7 +778,38 @@ public function sec($scale = null)
778778
return DecimalConstants::one()->div($cos)->round($scale);
779779
}
780780

781+
/**
782+
* Calculates the arcsine of this with the highest possible accuracy
783+
*
784+
* @param integer $scale [description]
785+
* @return Decimal
786+
*/
787+
public function arcsin($scale = null)
788+
{
789+
if($this->comp(DecimalConstants::one(), $scale + 2) === 1 || $this->comp(DecimalConstants::negativeOne(), $scale + 2) === -1) {
790+
throw new \DomainException(
791+
"The arcsin of this number is undefined."
792+
);
793+
}
794+
795+
if ($this->round($scale)->isZero()) {
796+
return DecimalConstants::zero;
797+
}
798+
if ($this->round($scale)->equals(DecimalConstants::one())) {
799+
return DecimalConstants::pi()->div(Decimal::fromInteger(2))->round($scale);
800+
}
801+
if ($this->round($scale)->equals(DecimalConstants::negativeOne())) {
802+
return DecimalConstants::pi()->div(Decimal::fromInteger(-2))->round($scale);
803+
}
781804

805+
$scale = ($scale === null) ? 32 : $scale;
806+
807+
return self::powerSerie(
808+
$this,
809+
DecimalConstants::zero(),
810+
$scale
811+
);
812+
}
782813
/**
783814
* Returns exp($this), said in other words: e^$this .
784815
*
@@ -834,6 +865,56 @@ private static function factorialSerie (Decimal $x, Decimal $firstTerm, callable
834865
return $approx->round($scale);
835866
}
836867

868+
869+
/**
870+
* Internal method used to compute arcsine *
871+
*
872+
* @param Decimal $x
873+
* @param Decimal $firstTerm
874+
* @param $scale
875+
* @return Decimal
876+
*/
877+
private static function powerSerie (Decimal $x, Decimal $firstTerm, $scale)
878+
{
879+
$approx = $firstTerm;
880+
$change = InfiniteDecimal::getPositiveInfinite();
881+
882+
$xPowerN = DecimalConstants::One(); // Calculates x^n
883+
$factorN = DecimalConstants::One(); // Calculates a_n
884+
885+
$numerator = DecimalConstants::one();
886+
$denominator = DecimalConstants::one();
887+
888+
for ($i = 1; !$change->floor($scale + 2)->isZero(); $i++) {
889+
$xPowerN = $xPowerN->mul($x);
890+
891+
if ($i % 2 == 0) {
892+
$factorN = DecimalConstants::zero();
893+
} elseif ($i == 1) {
894+
$factorN = DecimalConstants::one();
895+
} else {
896+
$incrementNum = Decimal::fromInteger($i - 2);
897+
$numerator = $numerator->mul($incrementNum, $scale +2);
898+
899+
$incrementDen = Decimal::fromInteger($i - 1);
900+
$increment = Decimal::fromInteger($i);
901+
$denominator = $denominator
902+
->div($incrementNum, $scale +2)
903+
->mul($incrementDen, $scale +2)
904+
->mul($increment, $scale +2);
905+
906+
$factorN = $numerator->div($denominator, $scale + 2);
907+
}
908+
909+
if (!$factorN->isZero()) {
910+
$change = $factorN->mul($xPowerN, $scale + 2);
911+
$approx = $approx->add($change, $scale + 2);
912+
}
913+
}
914+
915+
return $approx->round($scale);
916+
}
917+
837918
/**
838919
* Calculates the tangent of this method with the highest possible accuracy
839920
* Note that accuracy is limited by the accuracy of predefined PI;

tests/Decimal/DecimalArcsinTest.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
use Litipk\BigNumbers\Decimal as Decimal;
4+
5+
/**
6+
* @group arcsin
7+
*/
8+
class DecimalArcsinTest extends PHPUnit_Framework_TestCase
9+
{
10+
public function arcsinProvider() {
11+
// Some values providede by wolframalpha
12+
return [
13+
['0.154', '0.15461530016096', 14],
14+
['1', '1.57079632679489662', 17],
15+
['-1', '-1.57079632679489662', 17],
16+
];
17+
}
18+
19+
/**
20+
* @dataProvider arcsinProvider
21+
*/
22+
public function testSimple($nr, $answer, $digits)
23+
{
24+
$x = Decimal::fromString($nr);
25+
$arcsinX = $x->arcsin($digits);
26+
27+
$this->assertTrue(
28+
Decimal::fromString($answer)->equals($arcsinX),
29+
"The answer must be " . $answer . ", but was " . $arcsinX
30+
);
31+
}
32+
33+
/**
34+
* @expectedException \DomainException
35+
* @expectedExceptionMessage The arcsin of this number is undefined.
36+
*/
37+
public function testArcsinGreaterThanOne()
38+
{
39+
Decimal::fromString('25.546')->arcsin();
40+
}
41+
42+
/**
43+
* @expectedException \DomainException
44+
* @expectedExceptionMessage The arcsin of this number is undefined.
45+
*/
46+
public function testArcsinFewerThanNegativeOne()
47+
{
48+
Decimal::fromString('-304.75')->arcsin();
49+
}
50+
}

0 commit comments

Comments
 (0)