@@ -633,6 +633,123 @@ public function mod(Decimal $d, $scale = null)
633
633
return $ this ->sub ($ div ->mul ($ d , $ scale ));
634
634
}
635
635
636
+ /**
637
+ * Calculates the sine of this method with the highest possible accuracy
638
+ * Note that accuracy is limited by the accuracy of predefined PI;
639
+ *
640
+ * @param null $scale
641
+ * @return Decimal sin($this)
642
+ */
643
+ public function sin ($ scale = null ) {
644
+ // First normalise the number in the [0, 2PI] domain
645
+ $ twoPi = NumConstants::PI ()->mul (Decimal::fromString ("2 " ));
646
+ $ x = $ this ->mod ($ twoPi );
647
+ $ zero = Decimal::fromString ("0 " );
648
+
649
+ // PI has only 32 siginficant numbers
650
+ $ significantNumbers = is_null ($ scale ) ? 32 : $ scale ;
651
+
652
+ // Next use Maclaurin's theorem to approximate sin with high enough accuracy
653
+ // note that the accuracy is depended on the accuracy of the given PI constant
654
+ $ faculty = Decimal::fromString ("1 " ); // Calculates the faculty under the sign
655
+ $ loopCounter = 1 ; // Calculates the iteration we are in
656
+ $ xPowerN = Decimal::fromString ("1 " ); // Calculates x^n
657
+ $ approx = Decimal::fromString ("0 " ); // keeps track of our approximation for sin(x)
658
+
659
+ while (true ) {
660
+ // update x^n and n! for this walkthrough
661
+ $ xPowerN = $ xPowerN ->mul ($ x );
662
+ $ faculty = $ faculty ->mul (Decimal::fromString ((string ) $ loopCounter ));
663
+
664
+ // only do calculations if n is uneven
665
+ // otherwise result is zero anyways
666
+ if ($ loopCounter % 2 === 1 ) {
667
+ // calculate the absolute change in this iteration.
668
+ $ change = $ xPowerN ->div ($ faculty );
669
+
670
+ // change should be added if x mod 4 == 1 and subtracted if x mod 4 == 3
671
+ if ($ loopCounter % 4 === 1 ) {
672
+ $ approx = $ approx ->add ($ change );
673
+ } else {
674
+ $ approx = $ approx ->sub ($ change );
675
+ }
676
+
677
+ // Terminate the method if our change is sufficiently small
678
+ if ($ change ->floor ($ significantNumbers )->equals ($ zero )) {
679
+ return $ approx ->round ($ significantNumbers );
680
+ }
681
+ }
682
+
683
+
684
+ $ loopCounter ++;
685
+ }
686
+ }
687
+
688
+ /**
689
+ * Calculates the cosine of this method with the highest possible accuracy
690
+ * Note that accuracy is limited by the accuracy of predefined PI;
691
+ *
692
+ * @param null $scale
693
+ * @return Decimal cos($this)
694
+ */
695
+ public function cos ($ scale = null ) {
696
+ // First normalise the number in the [0, 2PI] domain
697
+ $ twoPi = NumConstants::PI ()->mul (Decimal::fromString ("2 " ));
698
+ $ x = $ this ->mod ($ twoPi );
699
+ $ zero = Decimal::fromString ("0 " );
700
+
701
+ // PI has only 32 siginficant numbers
702
+ $ significantNumbers = is_null ($ scale ) ? 32 : $ scale ;
703
+
704
+ // Next use Maclaurin's theorem to approximate sin with high enough accuracy
705
+ // note that the accuracy is depended on the accuracy of the given PI constant
706
+ $ faculty = Decimal::fromString ("1 " ); // Calculates the faculty under the sign
707
+ $ loopCounter = 1 ; // Calculates the iteration we are in
708
+ $ xPowerN = Decimal::fromString ("1 " ); // Calculates x^n
709
+ $ approx = Decimal::fromString ("1 " ); // keeps track of our approximation for sin(x)
710
+
711
+ while (true ) {
712
+ // update x^n and n! for this walkthrough
713
+ $ xPowerN = $ xPowerN ->mul ($ x );
714
+ $ faculty = $ faculty ->mul (Decimal::fromString ((string ) $ loopCounter ));
715
+
716
+ // only do calculations if n is uneven
717
+ // otherwise result is zero anyways
718
+ if ($ loopCounter % 2 === 0 ) {
719
+ // calculate the absolute change in this iteration.
720
+ $ change = $ xPowerN ->div ($ faculty );
721
+
722
+ // change should be added if x mod 4 == 1 and subtracted if x mod 4 == 3
723
+ if ($ loopCounter % 4 === 0 ) {
724
+ $ approx = $ approx ->add ($ change );
725
+ } else {
726
+ $ approx = $ approx ->sub ($ change );
727
+ }
728
+
729
+ // Terminate the method if our change is sufficiently small
730
+ if ($ change ->floor ($ significantNumbers )->equals ($ zero )) {
731
+ return $ approx ->round ($ significantNumbers );
732
+ }
733
+ }
734
+
735
+
736
+ $ loopCounter ++;
737
+ }
738
+ }
739
+
740
+ /**
741
+ * Calculates the tangent of this method with the highest possible accuracy
742
+ * Note that accuracy is limited by the accuracy of predefined PI;
743
+ *
744
+ * @param null $scale
745
+ * @return Decimal tan($this)
746
+ */
747
+ public function tan ($ scale = null ) {
748
+ return $ this ->sin ($ scale + 2 )
749
+ ->div ($ this ->cos ($ scale + 2 ))
750
+ ->floor ($ scale );
751
+ }
752
+
636
753
public function hasSameSign (Decimal $ b ) {
637
754
return $ this ->isPositive () && $ b ->isPositive () || $ this ->isNegative () && $ b ->isNegative ();
638
755
}
0 commit comments