@@ -571,21 +571,32 @@ public function ceil($scale = 0)
571
571
return $ this ;
572
572
}
573
573
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
+ {
574
583
$ rounded = bcadd ($ this ->value , '0 ' , $ scale );
575
584
576
585
$ rlen = strlen ($ rounded );
577
586
$ tlen = strlen ($ this ->value );
578
587
579
- $ mustCeil = false ;
588
+ $ mustTruncate = false ;
580
589
for ($ i =$ tlen -1 ; $ i >= $ rlen ; $ i --) {
581
590
if ((int )$ this ->value [$ i ] > 0 ) {
582
- $ mustCeil = true ;
591
+ $ mustTruncate = true ;
583
592
break ;
584
593
}
585
594
}
586
595
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 );
589
600
}
590
601
591
602
return self ::fromString ($ rounded , $ scale );
@@ -602,6 +613,10 @@ public function floor($scale = 0)
602
613
return $ this ;
603
614
}
604
615
616
+ if ($ this ->isNegative ()) {
617
+ return $ this ->innerTruncate ($ scale , false );
618
+ }
619
+
605
620
return self ::fromString (bcadd ($ this ->value , '0 ' , $ scale ));
606
621
}
607
622
@@ -620,14 +635,14 @@ public function abs()
620
635
621
636
/**
622
637
* Calculate modulo with a decimal
623
- * @param $d
638
+ * @param Decimal $d
639
+ * @param integer $scale
624
640
* @return $this % $d
625
641
*/
626
642
public function mod (Decimal $ d , $ scale = null )
627
643
{
628
- // integer division
629
644
$ div = $ this ->div ($ d , 1 )->floor ();
630
- return $ this ->sub ($ div ->mul ($ d , $ scale) );
645
+ return $ this ->sub ($ div ->mul ($ d) , $ scale );
631
646
}
632
647
633
648
/**
@@ -639,47 +654,40 @@ public function mod(Decimal $d, $scale = null)
639
654
*/
640
655
public function sin ($ scale = null ) {
641
656
// 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 " )));
645
658
646
- // PI has only 32 siginficant numbers
659
+ // PI has only 32 significant numbers
647
660
$ significantNumbers = is_null ($ scale ) ? 32 : $ scale ;
648
661
649
662
// Next use Maclaurin's theorem to approximate sin with high enough accuracy
650
663
// note that the accuracy is depended on the accuracy of the given PI constant
651
664
$ faculty = Decimal::fromString ("1 " ); // Calculates the faculty under the sign
652
- $ loopCounter = 1 ; // Calculates the iteration we are in
653
665
$ xPowerN = Decimal::fromString ("1 " ); // Calculates x^n
654
666
$ approx = Decimal::fromString ("0 " ); // keeps track of our approximation for sin(x)
655
667
656
- while (true ) {
668
+ $ change = InfiniteDecimal::getPositiveInfinite ();
669
+
670
+ for ($ i = 1 ; !$ change ->round ($ significantNumbers )->isZero (); $ i ++) {
657
671
// update x^n and n! for this walkthrough
658
672
$ xPowerN = $ xPowerN ->mul ($ x );
659
- $ faculty = $ faculty ->mul (Decimal::fromString ((string ) $ loopCounter ));
673
+ $ faculty = $ faculty ->mul (Decimal::fromString ((string ) $ i ));
660
674
661
675
// only do calculations if n is uneven
662
676
// otherwise result is zero anyways
663
- if ($ loopCounter % 2 === 1 ) {
677
+ if ($ i % 2 === 1 ) {
664
678
// calculate the absolute change in this iteration.
665
679
$ change = $ xPowerN ->div ($ faculty );
666
680
667
681
// 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 ) {
669
683
$ approx = $ approx ->add ($ change );
670
684
} else {
671
685
$ approx = $ approx ->sub ($ change );
672
686
}
673
-
674
- // Terminate the method if our change is sufficiently small
675
- if ($ change ->floor ($ significantNumbers )->equals ($ zero )) {
676
- return $ approx ->round ($ significantNumbers );
677
- }
678
687
}
679
-
680
-
681
- $ loopCounter ++;
682
688
}
689
+
690
+ return $ approx ->round ($ significantNumbers );
683
691
}
684
692
685
693
/**
@@ -691,47 +699,40 @@ public function sin($scale = null) {
691
699
*/
692
700
public function cos ($ scale = null ) {
693
701
// 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 " )));
697
703
698
- // PI has only 32 siginficant numbers
704
+ // PI has only 32 significant numbers
699
705
$ significantNumbers = is_null ($ scale ) ? 32 : $ scale ;
700
706
701
707
// Next use Maclaurin's theorem to approximate sin with high enough accuracy
702
708
// note that the accuracy is depended on the accuracy of the given PI constant
703
709
$ faculty = Decimal::fromString ("1 " ); // Calculates the faculty under the sign
704
- $ loopCounter = 1 ; // Calculates the iteration we are in
705
710
$ xPowerN = Decimal::fromString ("1 " ); // Calculates x^n
706
711
$ approx = Decimal::fromString ("1 " ); // keeps track of our approximation for sin(x)
707
712
708
- while (true ) {
713
+ $ change = InfiniteDecimal::getPositiveInfinite ();
714
+
715
+ for ($ i = 1 ; !$ change ->floor ($ significantNumbers )->isZero (); $ i ++) {
709
716
// update x^n and n! for this walkthrough
710
717
$ xPowerN = $ xPowerN ->mul ($ x );
711
- $ faculty = $ faculty ->mul (Decimal::fromString ((string ) $ loopCounter ));
718
+ $ faculty = $ faculty ->mul (Decimal::fromString ((string ) $ i ));
712
719
713
720
// only do calculations if n is uneven
714
721
// otherwise result is zero anyways
715
- if ($ loopCounter % 2 === 0 ) {
722
+ if ($ i % 2 === 0 ) {
716
723
// calculate the absolute change in this iteration.
717
724
$ change = $ xPowerN ->div ($ faculty );
718
725
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 ) {
721
728
$ approx = $ approx ->add ($ change );
722
729
} else {
723
730
$ approx = $ approx ->sub ($ change );
724
731
}
725
-
726
- // Terminate the method if our change is sufficiently small
727
- if ($ change ->floor ($ significantNumbers )->equals ($ zero )) {
728
- return $ approx ->round ($ significantNumbers );
729
- }
730
732
}
731
-
732
-
733
- $ loopCounter ++;
734
733
}
734
+
735
+ return $ approx ->round ($ significantNumbers );
735
736
}
736
737
737
738
/**
@@ -749,11 +750,15 @@ public function tan($scale = null) {
749
750
);
750
751
}
751
752
752
- return $ this ->sin ($ scale + 2 )
753
- ->div ($ cos )
754
- ->floor ($ scale );
753
+ return $ this ->sin ($ scale + 2 )->div ($ cos )->round ($ scale );
755
754
}
756
755
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
+ */
757
762
public function hasSameSign (Decimal $ b ) {
758
763
return $ this ->isPositive () && $ b ->isPositive () || $ this ->isNegative () && $ b ->isNegative ();
759
764
}
0 commit comments