Esempio n. 1
0
 /**
  * Performs a rounded division.
  *
  * Rounding is performed when the remainder of the division is not zero.
  *
  * @param string $a            The dividend.
  * @param string $b            The divisor.
  * @param int    $roundingMode The rounding mode.
  *
  * @return string
  *
  * @throws \InvalidArgumentException  If the rounding mode is invalid.
  * @throws RoundingNecessaryException If RoundingMode::UNNECESSARY is provided but rounding is necessary.
  */
 public function divRound($a, $b, $roundingMode)
 {
     list($quotient, $remainder) = $this->divQR($a, $b);
     $hasDiscardedFraction = $remainder !== '0';
     $isPositiveOrZero = ($a[0] === '-') === ($b[0] === '-');
     $discardedFractionSign = function () use($remainder, $b) {
         $r = $this->abs($this->mul($remainder, '2'));
         $b = $this->abs($b);
         return $this->cmp($r, $b);
     };
     $increment = false;
     switch ($roundingMode) {
         case RoundingMode::UNNECESSARY:
             if ($hasDiscardedFraction) {
                 throw RoundingNecessaryException::roundingNecessary();
             }
             break;
         case RoundingMode::UP:
             $increment = $hasDiscardedFraction;
             break;
         case RoundingMode::DOWN:
             break;
         case RoundingMode::CEILING:
             $increment = $hasDiscardedFraction && $isPositiveOrZero;
             break;
         case RoundingMode::FLOOR:
             $increment = $hasDiscardedFraction && !$isPositiveOrZero;
             break;
         case RoundingMode::HALF_UP:
             $increment = $discardedFractionSign() >= 0;
             break;
         case RoundingMode::HALF_DOWN:
             $increment = $discardedFractionSign() > 0;
             break;
         case RoundingMode::HALF_CEILING:
             $increment = $isPositiveOrZero ? $discardedFractionSign() >= 0 : $discardedFractionSign() > 0;
             break;
         case RoundingMode::HALF_FLOOR:
             $increment = $isPositiveOrZero ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0;
             break;
         case RoundingMode::HALF_EVEN:
             $lastDigit = (int) substr($quotient, -1);
             $lastDigitIsEven = $lastDigit % 2 === 0;
             $increment = $lastDigitIsEven ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0;
             break;
         default:
             throw new \InvalidArgumentException('Invalid rounding mode.');
     }
     if ($increment) {
         return $this->add($quotient, $isPositiveOrZero ? '1' : '-1');
     }
     return $quotient;
 }
Esempio n. 2
0
 /**
  * @param int         $roundingMode The rounding mode.
  * @param BigDecimal  $number       The number to round.
  * @param string      $divisor      The divisor.
  * @param string|null $two          The expected rounding to a scale of two, or null if an exception is expected.
  * @param string|null $one          The expected rounding to a scale of one, or null if an exception is expected.
  * @param string|null $zero         The expected rounding to a scale of zero, or null if an exception is expected.
  */
 private function doTestRoundingMode($roundingMode, BigDecimal $number, $divisor, $two, $one, $zero)
 {
     foreach ([$zero, $one, $two] as $scale => $expected) {
         if ($expected === null) {
             $this->setExpectedException(RoundingNecessaryException::getNamespace());
         }
         $actual = $number->dividedBy($divisor, $scale, $roundingMode);
         if ($expected !== null) {
             $this->assertBigDecimalInternalValues($expected, $scale, $actual);
         }
     }
 }
Esempio n. 3
0
 /**
  * @param integer     $roundingMode The rounding mode.
  * @param BigInteger  $number       The number to round.
  * @param string      $divisor      The divisor.
  * @param string|null $ten          The expected rounding to a scale of two, or null if an exception is expected.
  * @param string|null $hundred          The expected rounding to a scale of one, or null if an exception is expected.
  * @param string|null $thousand         The expected rounding to a scale of zero, or null if an exception is expected.
  */
 private function doTestDividedByWithRoundingMode($roundingMode, BigInteger $number, $divisor, $ten, $hundred, $thousand)
 {
     foreach ([$ten, $hundred, $thousand] as $expected) {
         $divisor .= '0';
         if ($expected === null) {
             $this->setExpectedException(RoundingNecessaryException::getNamespace());
         }
         $actual = $number->dividedBy($divisor, $roundingMode);
         if ($expected !== null) {
             $this->assertBigIntegerEquals($expected, $actual);
         }
     }
 }
Esempio n. 4
0
 /**
  * @return array
  */
 public function providerToScale()
 {
     return [['1/8', 3, RoundingMode::UNNECESSARY, '0.125'], ['1/16', 3, RoundingMode::UNNECESSARY, RoundingNecessaryException::getNamespace()], ['1/16', 3, RoundingMode::HALF_DOWN, '0.062'], ['1/16', 3, RoundingMode::HALF_UP, '0.063'], ['1/9', 30, RoundingMode::DOWN, '0.111111111111111111111111111111'], ['1/9', 30, RoundingMode::UP, '0.111111111111111111111111111112'], ['1/9', 100, RoundingMode::UNNECESSARY, RoundingNecessaryException::getNamespace()]];
 }