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

Commit debaec1

Browse files
committed
Rebase of the branch and resolution of conflict
2 parents dce3858 + 944a883 commit debaec1

File tree

6 files changed

+94
-68
lines changed

6 files changed

+94
-68
lines changed

src/Decimal.php

Lines changed: 50 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -571,21 +571,32 @@ public function ceil($scale = 0)
571571
return $this;
572572
}
573573

574+
if ($this->isNegative()) {
575+
return self::fromString(bcadd($this->value, '0', $scale));
576+
}
577+
578+
return $this->innerTruncate($scale);
579+
}
580+
581+
private function innerTruncate($scale = 0, $ceil = true)
582+
{
574583
$rounded = bcadd($this->value, '0', $scale);
575584

576585
$rlen = strlen($rounded);
577586
$tlen = strlen($this->value);
578587

579-
$mustCeil = false;
588+
$mustTruncate = false;
580589
for ($i=$tlen-1; $i >= $rlen; $i--) {
581590
if ((int)$this->value[$i] > 0) {
582-
$mustCeil = true;
591+
$mustTruncate = true;
583592
break;
584593
}
585594
}
586595

587-
if ($mustCeil) {
588-
$rounded = bcadd($rounded, bcpow('10', -$scale, $scale), $scale);
596+
if ($mustTruncate) {
597+
$rounded = $ceil ?
598+
bcadd($rounded, bcpow('10', -$scale, $scale), $scale) :
599+
bcsub($rounded, bcpow('10', -$scale, $scale), $scale);
589600
}
590601

591602
return self::fromString($rounded, $scale);
@@ -602,6 +613,10 @@ public function floor($scale = 0)
602613
return $this;
603614
}
604615

616+
if ($this->isNegative()) {
617+
return $this->innerTruncate($scale, false);
618+
}
619+
605620
return self::fromString(bcadd($this->value, '0', $scale));
606621
}
607622

@@ -620,14 +635,14 @@ public function abs()
620635

621636
/**
622637
* Calculate modulo with a decimal
623-
* @param $d
638+
* @param Decimal $d
639+
* @param integer $scale
624640
* @return $this % $d
625641
*/
626642
public function mod(Decimal $d, $scale = null)
627643
{
628-
// integer division
629644
$div = $this->div($d, 1)->floor();
630-
return $this->sub($div->mul($d, $scale));
645+
return $this->sub($div->mul($d), $scale);
631646
}
632647

633648
/**
@@ -639,47 +654,40 @@ public function mod(Decimal $d, $scale = null)
639654
*/
640655
public function sin($scale = null) {
641656
// First normalise the number in the [0, 2PI] domain
642-
$twoPi = DecimalConstants::PI()->mul(Decimal::fromString("2"));
643-
$x = $this->mod($twoPi);
644-
$zero = Decimal::fromString("0");
657+
$x = $this->mod(DecimalConstants::PI()->mul(Decimal::fromString("2")));
645658

646-
// PI has only 32 siginficant numbers
659+
// PI has only 32 significant numbers
647660
$significantNumbers = is_null($scale) ? 32 : $scale;
648661

649662
// Next use Maclaurin's theorem to approximate sin with high enough accuracy
650663
// note that the accuracy is depended on the accuracy of the given PI constant
651664
$faculty = Decimal::fromString("1"); // Calculates the faculty under the sign
652-
$loopCounter = 1; // Calculates the iteration we are in
653665
$xPowerN = Decimal::fromString("1"); // Calculates x^n
654666
$approx = Decimal::fromString("0"); // keeps track of our approximation for sin(x)
655667

656-
while (true) {
668+
$change = InfiniteDecimal::getPositiveInfinite();
669+
670+
for ($i = 1; !$change->round($significantNumbers)->isZero(); $i++) {
657671
// update x^n and n! for this walkthrough
658672
$xPowerN = $xPowerN->mul($x);
659-
$faculty = $faculty->mul(Decimal::fromString((string) $loopCounter));
673+
$faculty = $faculty->mul(Decimal::fromString((string) $i));
660674

661675
// only do calculations if n is uneven
662676
// otherwise result is zero anyways
663-
if ($loopCounter % 2 === 1) {
677+
if ($i % 2 === 1) {
664678
// calculate the absolute change in this iteration.
665679
$change = $xPowerN->div($faculty);
666680

667681
// change should be added if x mod 4 == 1 and subtracted if x mod 4 == 3
668-
if ($loopCounter % 4 === 1) {
682+
if ($i % 4 === 1) {
669683
$approx = $approx->add($change);
670684
} else {
671685
$approx = $approx->sub($change);
672686
}
673-
674-
// Terminate the method if our change is sufficiently small
675-
if ($change->floor($significantNumbers)->equals($zero)) {
676-
return $approx->round($significantNumbers);
677-
}
678687
}
679-
680-
681-
$loopCounter++;
682688
}
689+
690+
return $approx->round($significantNumbers);
683691
}
684692

685693
/**
@@ -691,47 +699,40 @@ public function sin($scale = null) {
691699
*/
692700
public function cos($scale = null) {
693701
// First normalise the number in the [0, 2PI] domain
694-
$twoPi = DecimalConstants::PI()->mul(Decimal::fromString("2"));
695-
$x = $this->mod($twoPi);
696-
$zero = Decimal::fromString("0");
702+
$x = $this->mod(DecimalConstants::PI()->mul(Decimal::fromString("2")));
697703

698-
// PI has only 32 siginficant numbers
704+
// PI has only 32 significant numbers
699705
$significantNumbers = is_null($scale) ? 32 : $scale;
700706

701707
// Next use Maclaurin's theorem to approximate sin with high enough accuracy
702708
// note that the accuracy is depended on the accuracy of the given PI constant
703709
$faculty = Decimal::fromString("1"); // Calculates the faculty under the sign
704-
$loopCounter = 1; // Calculates the iteration we are in
705710
$xPowerN = Decimal::fromString("1"); // Calculates x^n
706711
$approx = Decimal::fromString("1"); // keeps track of our approximation for sin(x)
707712

708-
while (true) {
713+
$change = InfiniteDecimal::getPositiveInfinite();
714+
715+
for ($i = 1; !$change->floor($significantNumbers)->isZero(); $i++) {
709716
// update x^n and n! for this walkthrough
710717
$xPowerN = $xPowerN->mul($x);
711-
$faculty = $faculty->mul(Decimal::fromString((string) $loopCounter));
718+
$faculty = $faculty->mul(Decimal::fromString((string) $i));
712719

713720
// only do calculations if n is uneven
714721
// otherwise result is zero anyways
715-
if ($loopCounter % 2 === 0) {
722+
if ($i % 2 === 0) {
716723
// calculate the absolute change in this iteration.
717724
$change = $xPowerN->div($faculty);
718725

719-
// change should be added if x mod 4 == 1 and subtracted if x mod 4 == 3
720-
if ($loopCounter % 4 === 0) {
726+
// change should be added if x mod 4 == 0 and subtracted if x mod 4 == 2
727+
if ($i % 4 === 0) {
721728
$approx = $approx->add($change);
722729
} else {
723730
$approx = $approx->sub($change);
724731
}
725-
726-
// Terminate the method if our change is sufficiently small
727-
if ($change->floor($significantNumbers )->equals($zero)) {
728-
return $approx->round($significantNumbers);
729-
}
730732
}
731-
732-
733-
$loopCounter++;
734733
}
734+
735+
return $approx->round($significantNumbers);
735736
}
736737

737738
/**
@@ -749,11 +750,15 @@ public function tan($scale = null) {
749750
);
750751
}
751752

752-
return $this->sin($scale + 2)
753-
->div($cos)
754-
->floor($scale);
753+
return $this->sin($scale + 2)->div($cos)->round($scale);
755754
}
756755

756+
/**
757+
* Indicates if the passed parameter has the same sign as the method's bound object.
758+
*
759+
* @param Decimal $b
760+
* @return bool
761+
*/
757762
public function hasSameSign(Decimal $b) {
758763
return $this->isPositive() && $b->isPositive() || $this->isNegative() && $b->isNegative();
759764
}

src/DecimalConstants.php

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66

77

88
/**
9-
* Static class that holds many important numeric constants
9+
* git statu class that holds many important numeric constants
1010
*
1111
* @author Andreu Correa Casablanca <[email protected]>
1212
*/
13-
class DecimalConstants
13+
final class DecimalConstants
1414
{
1515
private static $PI = null;
1616
private static $E = null;
@@ -38,64 +38,69 @@ private function __clone()
3838

3939
/**
4040
* Returns the Pi number.
41+
* @return Decimal
4142
*/
4243
public static function PI()
4344
{
44-
if (static::$PI === null) {
45-
static::$PI = Decimal::fromString(
45+
if (self::$PI === null) {
46+
self::$PI = Decimal::fromString(
4647
"3.14159265358979323846264338327950"
4748
);
4849
}
49-
return static::$PI;
50+
return self::$PI;
5051
}
5152

5253
/**
5354
* Returns the Euler's E number.
55+
* @return Decimal
5456
*/
5557
public static function E()
5658
{
57-
if (static::$E === null) {
58-
static::$E = Decimal::fromString(
59+
if (self::$E === null) {
60+
self::$E = Decimal::fromString(
5961
"2.71828182845904523536028747135266"
6062
);
6163
}
62-
return static::$E;
64+
return self::$E;
6365
}
6466

6567
/**
6668
* Returns the Euler-Mascheroni constant.
69+
* @return Decimal
6770
*/
6871
public static function EulerMascheroni()
6972
{
70-
if (static::$EulerMascheroni === null) {
71-
static::$EulerMascheroni = Decimal::fromString(
73+
if (self::$EulerMascheroni === null) {
74+
self::$EulerMascheroni = Decimal::fromString(
7275
"0.57721566490153286060651209008240"
7376
);
7477
}
75-
return static::$EulerMascheroni;
78+
return self::$EulerMascheroni;
7679
}
7780

7881
/**
7982
* Returns the Golden Ration, also named Phi.
83+
* @return Decimal
8084
*/
8185
public static function GoldenRatio()
8286
{
83-
if (static::$GoldenRatio === null) {
84-
static::$GoldenRatio = Decimal::fromString(
87+
if (self::$GoldenRatio === null) {
88+
self::$GoldenRatio = Decimal::fromString(
8589
"1.61803398874989484820458683436564"
8690
);
8791
}
88-
return static::$GoldenRatio;
92+
return self::$GoldenRatio;
8993
}
9094

9195
/**
9296
* Returns the Light of Speed measured in meters / second.
97+
* @return Decimal
9398
*/
9499
public static function LightSpeed()
95100
{
96-
if (static::$LightSpeed === null) {
97-
static::$LightSpeed = Decimal::fromInteger(299792458);
101+
if (self::$LightSpeed === null) {
102+
self::$LightSpeed = Decimal::fromInteger(299792458);
98103
}
99-
return static::$LightSpeed;
104+
return self::$LightSpeed;
100105
}
101106
}

tests/Decimal/DecimalCeilTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,10 @@ public function testNoUsefulCeil()
3333
$this->assertTrue(Decimal::fromString('3.45')->ceil(2)->equals(Decimal::fromString('3.45')));
3434
$this->assertTrue(Decimal::fromString('3.45')->ceil(3)->equals(Decimal::fromString('3.45')));
3535
}
36+
37+
public function testNegativeCeil()
38+
{
39+
$this->assertTrue(Decimal::fromFloat(-3.4)->ceil()->equals(Decimal::fromFloat(-3.0)));
40+
$this->assertTrue(Decimal::fromFloat(-3.6)->ceil()->equals(Decimal::fromFloat(-3.0)));
41+
}
3642
}

tests/Decimal/DecimalFloorTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,10 @@ public function testNoUsefulFloor()
3737
$this->assertTrue(Decimal::fromString('3.45')->floor(2)->equals(Decimal::fromString('3.45')));
3838
$this->assertTrue(Decimal::fromString('3.45')->floor(3)->equals(Decimal::fromString('3.45')));
3939
}
40+
41+
public function testNegativeFloor()
42+
{
43+
$this->assertTrue(Decimal::fromFloat(-3.4)->floor()->equals(Decimal::fromFloat(-4.0)));
44+
$this->assertTrue(Decimal::fromFloat(-3.6)->floor()->equals(Decimal::fromFloat(-4.0)));
45+
}
4046
}

tests/Decimal/DecimalModTest.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,16 @@ public function modProvider() {
1212
array('10', '3', '1'),
1313
array('34', '3.4', '0'),
1414
array('15.1615', '3.156156', '2.536876'),
15-
array('15.1615', '3.156156', '2.5365', 3),
15+
array('15.1615', '3.156156', '2.5369', 4),
16+
array('-3.4', '-2', '-1.4'),
1617
array('3.4', '-2', '-0.6'),
1718
array('-3.4', '2', '0.6')
1819
);
1920
}
2021
/**
2122
* @dataProvider modProvider
2223
*/
23-
public function testMod($number, $mod, $answer, $scale = null) {
24+
public function testFiniteFiniteMod($number, $mod, $answer, $scale = null) {
2425
$numberDec = Decimal::fromString($number);
2526
$modDec = Decimal::fromString($mod);
2627
$decimalAnswer = $numberDec->mod($modDec, $scale);

tests/Decimal/DecimalTanTest.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ class DecimalTanTest extends PHPUnit_Framework_TestCase
1111
public function tanProvider() {
1212
// Some values providede by mathematica
1313
return array(
14-
array('1', '1.55740772465490', '14'),
15-
array('123.123', '0.68543903342472368', '17'),
16-
array('15000000000', '-0.95779983511717825557', '20')
14+
array('1', '1.55740772465490', 14),
15+
array('123.123', '0.68543903342472368', 17),
16+
array('15000000000', '-0.95779983511717825557', 20)
1717

1818
);
1919
}
@@ -25,7 +25,10 @@ public function testSimple($nr, $answer, $digits)
2525
{
2626
$x = Decimal::fromString($nr);
2727
$tanX = $x->tan($digits);
28-
$this->assertTrue(Decimal::fromString($answer)->equals($tanX));
28+
$this->assertTrue(
29+
Decimal::fromString($answer)->equals($tanX),
30+
'tan('.$nr.') must be equal to '.$answer.', but was '.$tanX
31+
);
2932
}
3033

3134
public function testTanPiTwoDiv()

0 commit comments

Comments
 (0)