@@ -369,7 +369,7 @@ public function div (BigNumber $b, $scale = null)
369
369
370
370
/**
371
371
* Returns the square root of this object
372
- *
372
+ * @param integer $scale
373
373
* @return Decimal
374
374
*/
375
375
public function sqrt ($ scale = null )
@@ -384,10 +384,56 @@ public function sqrt ($scale = null)
384
384
385
385
return self ::fromString (
386
386
bcsqrt ($ this ->value , $ sqrt_scale +1 ),
387
- $ scale
387
+ $ sqrt_scale
388
388
);
389
389
}
390
390
391
+ /**
392
+ * Powers this value to $b
393
+ *
394
+ * @param Decimal $b exponent
395
+ * @param integer $scale
396
+ * @return Decimal
397
+ */
398
+ public function pow (Decimal $ b , $ scale = null )
399
+ {
400
+ if ($ this ->isZero ()) {
401
+ if ($ b ->isPositive ()) {
402
+ return Decimal::fromDecimal ($ this , $ scale );
403
+ } else {
404
+ return NaN::getNaN ();
405
+ }
406
+ } elseif ($ b ->isZero ()) {
407
+ return Decimal::fromInteger (1 , $ scale );
408
+ } elseif ($ b ->scale == 0 ) {
409
+ $ pow_scale = $ scale === null ?
410
+ max ($ this ->scale , $ b ->scale ) : max ($ this ->scale , $ b ->scale , $ scale );
411
+
412
+ return self ::fromString (
413
+ bcpow ($ this ->value , $ b ->value , $ pow_scale +1 ),
414
+ $ pow_scale
415
+ );
416
+ } else {
417
+ if ($ this ->isPositive ()) {
418
+ $ pow_scale = $ scale === null ?
419
+ max ($ this ->scale , $ b ->scale ) : max ($ this ->scale , $ b ->scale , $ scale );
420
+
421
+ $ truncated_b = bcadd ($ b ->value , '0 ' , 0 );
422
+ $ remaining_b = bcsub ($ b ->value , $ truncated_b , $ b ->scale );
423
+
424
+ $ first_pow_approx = bcpow ($ this ->value , $ truncated_b , $ pow_scale +1 );
425
+ $ intermediate_root = self ::innerPowWithLittleExponent ($ this ->value , $ remaining_b , $ b ->scale , $ pow_scale +1 );
426
+
427
+ return Decimal::fromString (
428
+ bcmul ($ first_pow_approx , $ intermediate_root , $ pow_scale +1 ),
429
+ $ pow_scale
430
+ );
431
+ } else { // elseif ($this->isNegative())
432
+ return NaN::getNaN ();
433
+ }
434
+ }
435
+ }
436
+
391
437
/**
392
438
* Returns the object's logarithm in base 10
393
439
* @param integer $scale
@@ -422,7 +468,7 @@ public function isZero ($scale = null)
422
468
*/
423
469
public function isPositive ()
424
470
{
425
- return ($ this ->value [0 ] !== '- ' );
471
+ return ($ this ->value [0 ] !== '- ' && ! $ this -> isZero () );
426
472
}
427
473
428
474
/**
@@ -647,4 +693,52 @@ private static function innerLog10 ($value, $in_scale, $out_scale)
647
693
return '0 ' ;
648
694
}
649
695
}
696
+
697
+
698
+ private static function innerPowWithLittleExponent ($ base , $ exponent , $ exp_scale , $ out_scale )
699
+ {
700
+ $ inner_scale = ceil ($ exp_scale *log (10 )/log (2 ))+1 ;
701
+
702
+ $ result_a = '1 ' ;
703
+ $ result_b = '0 ' ;
704
+
705
+ $ actual_index = 0 ;
706
+ $ exponent_remaining = $ exponent ;
707
+
708
+ while (bccomp ($ result_a , $ result_b , $ out_scale ) !== 0 && bccomp ($ exponent_remaining , '0 ' , $ inner_scale ) !== 0 ) {
709
+ $ result_b = $ result_a ;
710
+ $ index_info = self ::computeSquareIndex ($ exponent_remaining , $ actual_index , $ exp_scale , $ inner_scale );
711
+ $ exponent_remaining = $ index_info [1 ];
712
+ $ result_a = bcmul ($ result_a , self ::compute2NRoot ($ base , $ index_info [0 ], 2 *($ out_scale +1 )), 2 *($ out_scale +1 ));
713
+ }
714
+
715
+ return self ::innerRound ($ result_a , $ out_scale );
716
+ }
717
+
718
+
719
+ private static function computeSquareIndex ($ exponent_remaining , $ actual_index , $ exp_scale , $ inner_scale )
720
+ {
721
+ $ actual_rt = bcpow ('0.5 ' , $ actual_index , $ exp_scale );
722
+ $ r = bcsub ($ exponent_remaining , $ actual_rt , $ inner_scale );
723
+
724
+ while (bccomp ($ r , 0 , $ exp_scale ) === -1 ) {
725
+ ++$ actual_index ;
726
+ $ actual_rt = bcmul ('0.5 ' , $ actual_rt , $ inner_scale );
727
+ $ r = bcsub ($ exponent_remaining , $ actual_rt , $ inner_scale );
728
+ }
729
+
730
+ return [$ actual_index , $ r ];
731
+ }
732
+
733
+
734
+ private static function compute2NRoot ($ base , $ index , $ out_scale )
735
+ {
736
+ $ result = $ base ;
737
+
738
+ for ($ i =0 ; $ i <$ index ; $ i ++) {
739
+ $ result = bcsqrt ($ result , ($ out_scale +1 )*($ index -$ i )+1 );
740
+ }
741
+
742
+ return self ::innerRound ($ result , $ out_scale );
743
+ }
650
744
}
0 commit comments