Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 400 and 401]

   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
   4  
   5  use PhpOffice\PhpSpreadsheet\Calculation\Exception;
   6  use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
   7  
   8  class ConvertDecimal extends ConvertBase
   9  {
  10      const LARGEST_OCTAL_IN_DECIMAL = 536870911;
  11      const SMALLEST_OCTAL_IN_DECIMAL = -536870912;
  12      const LARGEST_BINARY_IN_DECIMAL = 511;
  13      const SMALLEST_BINARY_IN_DECIMAL = -512;
  14      const LARGEST_HEX_IN_DECIMAL = 549755813887;
  15      const SMALLEST_HEX_IN_DECIMAL = -549755813888;
  16  
  17      /**
  18       * toBinary.
  19       *
  20       * Return a decimal value as binary.
  21       *
  22       * Excel Function:
  23       *        DEC2BIN(x[,places])
  24       *
  25       * @param array|string $value The decimal integer you want to convert. If number is negative,
  26       *                          valid place values are ignored and DEC2BIN returns a 10-character
  27       *                          (10-bit) binary number in which the most significant bit is the sign
  28       *                          bit. The remaining 9 bits are magnitude bits. Negative numbers are
  29       *                          represented using two's-complement notation.
  30       *                      If number < -512 or if number > 511, DEC2BIN returns the #NUM! error
  31       *                          value.
  32       *                      If number is nonnumeric, DEC2BIN returns the #VALUE! error value.
  33       *                      If DEC2BIN requires more than places characters, it returns the #NUM!
  34       *                          error value.
  35       *                      Or can be an array of values
  36       * @param array|int $places The number of characters to use. If places is omitted, DEC2BIN uses
  37       *                          the minimum number of characters necessary. Places is useful for
  38       *                          padding the return value with leading 0s (zeros).
  39       *                      If places is not an integer, it is truncated.
  40       *                      If places is nonnumeric, DEC2BIN returns the #VALUE! error value.
  41       *                      If places is zero or negative, DEC2BIN returns the #NUM! error value.
  42       *                      Or can be an array of values
  43       *
  44       * @return array|string Result, or an error
  45       *         If an array of numbers is passed as an argument, then the returned result will also be an array
  46       *            with the same dimensions
  47       */
  48      public static function toBinary($value, $places = null)
  49      {
  50          if (is_array($value) || is_array($places)) {
  51              return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
  52          }
  53  
  54          try {
  55              $value = self::validateValue($value);
  56              $value = self::validateDecimal($value);
  57              $places = self::validatePlaces($places);
  58          } catch (Exception $e) {
  59              return $e->getMessage();
  60          }
  61  
  62          $value = (int) floor((float) $value);
  63          if ($value > self::LARGEST_BINARY_IN_DECIMAL || $value < self::SMALLEST_BINARY_IN_DECIMAL) {
  64              return ExcelError::NAN();
  65          }
  66  
  67          $r = decbin($value);
  68          // Two's Complement
  69          $r = substr($r, -10);
  70  
  71          return self::nbrConversionFormat($r, $places);
  72      }
  73  
  74      /**
  75       * toHex.
  76       *
  77       * Return a decimal value as hex.
  78       *
  79       * Excel Function:
  80       *        DEC2HEX(x[,places])
  81       *
  82       * @param array|string $value The decimal integer you want to convert. If number is negative,
  83       *                          places is ignored and DEC2HEX returns a 10-character (40-bit)
  84       *                          hexadecimal number in which the most significant bit is the sign
  85       *                          bit. The remaining 39 bits are magnitude bits. Negative numbers
  86       *                          are represented using two's-complement notation.
  87       *                      If number < -549,755,813,888 or if number > 549,755,813,887,
  88       *                          DEC2HEX returns the #NUM! error value.
  89       *                      If number is nonnumeric, DEC2HEX returns the #VALUE! error value.
  90       *                      If DEC2HEX requires more than places characters, it returns the
  91       *                          #NUM! error value.
  92       *                      Or can be an array of values
  93       * @param array|int $places The number of characters to use. If places is omitted, DEC2HEX uses
  94       *                          the minimum number of characters necessary. Places is useful for
  95       *                          padding the return value with leading 0s (zeros).
  96       *                      If places is not an integer, it is truncated.
  97       *                      If places is nonnumeric, DEC2HEX returns the #VALUE! error value.
  98       *                      If places is zero or negative, DEC2HEX returns the #NUM! error value.
  99       *                      Or can be an array of values
 100       *
 101       * @return array|string Result, or an error
 102       *         If an array of numbers is passed as an argument, then the returned result will also be an array
 103       *            with the same dimensions
 104       */
 105      public static function toHex($value, $places = null)
 106      {
 107          if (is_array($value) || is_array($places)) {
 108              return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
 109          }
 110  
 111          try {
 112              $value = self::validateValue($value);
 113              $value = self::validateDecimal($value);
 114              $places = self::validatePlaces($places);
 115          } catch (Exception $e) {
 116              return $e->getMessage();
 117          }
 118  
 119          $value = floor((float) $value);
 120          if ($value > self::LARGEST_HEX_IN_DECIMAL || $value < self::SMALLEST_HEX_IN_DECIMAL) {
 121              return ExcelError::NAN();
 122          }
 123          $r = strtoupper(dechex((int) $value));
 124          $r = self::hex32bit($value, $r);
 125  
 126          return self::nbrConversionFormat($r, $places);
 127      }
 128  
 129      public static function hex32bit(float $value, string $hexstr, bool $force = false): string
 130      {
 131          if (PHP_INT_SIZE === 4 || $force) {
 132              if ($value >= 2 ** 32) {
 133                  $quotient = (int) ($value / (2 ** 32));
 134  
 135                  return strtoupper(substr('0' . dechex($quotient), -2) . $hexstr);
 136              }
 137              if ($value < -(2 ** 32)) {
 138                  $quotient = 256 - (int) ceil((-$value) / (2 ** 32));
 139  
 140                  return strtoupper(substr('0' . dechex($quotient), -2) . substr("00000000$hexstr", -8));
 141              }
 142              if ($value < 0) {
 143                  return "FF$hexstr";
 144              }
 145          }
 146  
 147          return $hexstr;
 148      }
 149  
 150      /**
 151       * toOctal.
 152       *
 153       * Return an decimal value as octal.
 154       *
 155       * Excel Function:
 156       *        DEC2OCT(x[,places])
 157       *
 158       * @param array|string $value The decimal integer you want to convert. If number is negative,
 159       *                          places is ignored and DEC2OCT returns a 10-character (30-bit)
 160       *                          octal number in which the most significant bit is the sign bit.
 161       *                          The remaining 29 bits are magnitude bits. Negative numbers are
 162       *                          represented using two's-complement notation.
 163       *                      If number < -536,870,912 or if number > 536,870,911, DEC2OCT
 164       *                          returns the #NUM! error value.
 165       *                      If number is nonnumeric, DEC2OCT returns the #VALUE! error value.
 166       *                      If DEC2OCT requires more than places characters, it returns the
 167       *                          #NUM! error value.
 168       *                      Or can be an array of values
 169       * @param array|int $places The number of characters to use. If places is omitted, DEC2OCT uses
 170       *                          the minimum number of characters necessary. Places is useful for
 171       *                          padding the return value with leading 0s (zeros).
 172       *                      If places is not an integer, it is truncated.
 173       *                      If places is nonnumeric, DEC2OCT returns the #VALUE! error value.
 174       *                      If places is zero or negative, DEC2OCT returns the #NUM! error value.
 175       *                      Or can be an array of values
 176       *
 177       * @return array|string Result, or an error
 178       *         If an array of numbers is passed as an argument, then the returned result will also be an array
 179       *            with the same dimensions
 180       */
 181      public static function toOctal($value, $places = null)
 182      {
 183          if (is_array($value) || is_array($places)) {
 184              return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
 185          }
 186  
 187          try {
 188              $value = self::validateValue($value);
 189              $value = self::validateDecimal($value);
 190              $places = self::validatePlaces($places);
 191          } catch (Exception $e) {
 192              return $e->getMessage();
 193          }
 194  
 195          $value = (int) floor((float) $value);
 196          if ($value > self::LARGEST_OCTAL_IN_DECIMAL || $value < self::SMALLEST_OCTAL_IN_DECIMAL) {
 197              return ExcelError::NAN();
 198          }
 199          $r = decoct($value);
 200          $r = substr($r, -10);
 201  
 202          return self::nbrConversionFormat($r, $places);
 203      }
 204  
 205      protected static function validateDecimal(string $value): string
 206      {
 207          if (strlen($value) > preg_match_all('/[-0123456789.]/', $value)) {
 208              throw new Exception(ExcelError::VALUE());
 209          }
 210  
 211          return $value;
 212      }
 213  }