You are here

public static function Calculator::round in Price 2.0.x

Same name and namespace in other branches
  1. 8 src/Calculator.php \Drupal\price\Calculator::round()
  2. 3.x src/Calculator.php \Drupal\price\Calculator::round()
  3. 2.x src/Calculator.php \Drupal\price\Calculator::round()
  4. 3.0.x src/Calculator.php \Drupal\price\Calculator::round()

Rounds the given number.

Replicates PHP's support for rounding to the nearest even/odd number even if that number is decimal ($precision > 0).

Parameters

string $number: The number.

int $precision: The number of decimals to round to.

int $mode: The rounding mode. One of the following constants: PHP_ROUND_HALF_UP, PHP_ROUND_HALF_DOWN, PHP_ROUND_HALF_EVEN, PHP_ROUND_HALF_ODD.

Return value

string The rounded number.

Throws

\InvalidArgumentException Thrown when an invalid (non-numeric or negative) precision is given.

1 call to Calculator::round()
Rounder::round in src/Rounder.php
Rounds the given price to its currency precision.

File

src/Calculator.php, line 158

Class

Calculator
Provides helpers for bcmath-based arithmetic.

Namespace

Drupal\price

Code

public static function round(string $number, int $precision = 0, int $mode = PHP_ROUND_HALF_UP) : string {
  self::assertNumberFormat($number);
  if (!is_numeric($precision) || $precision < 0) {
    throw new \InvalidArgumentException('The provided precision should be a positive number');
  }

  // Round the number in both directions (up/down) before choosing one.
  $rounding_increment = bcdiv('1', pow(10, $precision), $precision);
  if (self::compare($number, '0') == 1) {
    $rounded_up = bcadd($number, $rounding_increment, $precision);
  }
  else {
    $rounded_up = bcsub($number, $rounding_increment, $precision);
  }
  $rounded_down = bcsub($number, 0, $precision);

  // The rounding direction is based on the first decimal after $precision.
  $number_parts = explode('.', $number);
  $decimals = !empty($number_parts[1]) ? $number_parts[1] : '0';
  $relevant_decimal = isset($decimals[$precision]) ? $decimals[$precision] : 0;
  if ($relevant_decimal < 5) {
    $number = $rounded_down;
  }
  elseif ($relevant_decimal == 5) {
    if ($mode == PHP_ROUND_HALF_UP) {
      $number = $rounded_up;
    }
    elseif ($mode == PHP_ROUND_HALF_DOWN) {
      $number = $rounded_down;
    }
    elseif ($mode == PHP_ROUND_HALF_EVEN) {
      $integer = bcmul($rounded_up, pow(10, $precision), 0);
      $number = bcmod($integer, '2') == 0 ? $rounded_up : $rounded_down;
    }
    elseif ($mode == PHP_ROUND_HALF_ODD) {
      $integer = bcmul($rounded_up, pow(10, $precision), 0);
      $number = bcmod($integer, '2') != 0 ? $rounded_up : $rounded_down;
    }
  }
  elseif ($relevant_decimal > 5) {
    $number = $rounded_up;
  }
  return $number;
}