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

Commit

Permalink
Merge pull request #46 from codisart/add-arctan-method
Browse files Browse the repository at this point in the history
[TECH] Add the arctan method. Thanks to @codisart .
  • Loading branch information
castarco committed Dec 15, 2015
2 parents ec108cd + cc56a44 commit 4495d4d
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 1 deletion.
69 changes: 68 additions & 1 deletion src/Decimal.php
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,35 @@ public function arccos($scale = null)
)->round($scale);
}

/**
* Calculates the arctangente of this with the highest possible accuracy
*
* @param integer $scale
* @return Decimal
*/
public function arctan($scale = null)
{
$piOverFour = DecimalConstants::pi()->div(Decimal::fromInteger(4), $scale + 2)->round($scale);

if ($this->round($scale)->isZero()) {
return DecimalConstants::zero();
}
if ($this->round($scale)->equals(DecimalConstants::one())) {
return $piOverFour;
}
if ($this->round($scale)->equals(DecimalConstants::negativeOne())) {
return DecimalConstants::negativeOne()->mul($piOverFour);
}

$scale = ($scale === null) ? 32 : $scale;

return self::simplePowerSerie(
$this,
DecimalConstants::zero(),
$scale
)->round($scale);
}

/**
* Returns exp($this), said in other words: e^$this .
*
Expand Down Expand Up @@ -905,7 +934,7 @@ private static function factorialSerie (Decimal $x, Decimal $firstTerm, callable


/**
* Internal method used to compute arcsine *
* Internal method used to compute arcsine and arcosine
*
* @param Decimal $x
* @param Decimal $firstTerm
Expand Down Expand Up @@ -953,6 +982,44 @@ private static function powerSerie (Decimal $x, Decimal $firstTerm, $scale)
return $approx->round($scale);
}

/**
* Internal method used to compute arctan and arccotan
*
* @param Decimal $x
* @param Decimal $firstTerm
* @param $scale
* @return Decimal
*/
private static function simplePowerSerie (Decimal $x, Decimal $firstTerm, $scale)
{
$approx = $firstTerm;
$change = InfiniteDecimal::getPositiveInfinite();

$xPowerN = DecimalConstants::One(); // Calculates x^n
$sign = DecimalConstants::One(); // Calculates a_n

for ($i = 1; !$change->floor($scale + 2)->isZero(); $i++) {
$xPowerN = $xPowerN->mul($x);

if ($i % 2 === 0) {
$factorN = DecimalConstants::zero();
} else {
if ($i % 4 === 1) {
$factorN = DecimalConstants::one()->div(Decimal::fromInteger($i), $scale + 2);
} else {
$factorN = DecimalConstants::negativeOne()->div(Decimal::fromInteger($i), $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;
Expand Down
33 changes: 33 additions & 0 deletions tests/Decimal/DecimalArctanTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

use Litipk\BigNumbers\Decimal as Decimal;

/**
* @group arctan
*/
class DecimalArctanTest extends PHPUnit_Framework_TestCase
{
public function arctanProvider() {
// Some values provided by wolframalpha
return [
['0.154', '0.15279961393666', 14],
['0', '0', 17],
['-1', '-0.78539816339744831', 17],
];
}

/**
* @dataProvider arctanProvider
*/
public function testSimple($nr, $answer, $digits)
{
$x = Decimal::fromString($nr);
$arctanX = $x->arctan($digits);

$this->assertTrue(
Decimal::fromString($answer)->equals($arctanX),
"The answer must be " . $answer . ", but was " . $arctanX
);
}

}

0 comments on commit 4495d4d

Please sign in to comment.