| Total Complexity | 95 |
| Total Lines | 716 |
| Duplicated Lines | 0 % |
| Coverage | 100% |
| Changes | 0 | ||
Complex classes like Real often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Real, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 19 | class Real extends Element |
||
| 20 | { |
||
| 21 | use UniversalClass; |
||
| 22 | use PrimitiveType; |
||
| 23 | |||
| 24 | /** |
||
| 25 | * Regex pattern to parse NR1 form number. |
||
| 26 | * |
||
| 27 | * @var string |
||
| 28 | */ |
||
| 29 | const NR1_REGEX = '/^\s*' . |
||
| 30 | '(?<s>[+\-])?' . // sign |
||
| 31 | '(?<i>\d+)' . // integer |
||
| 32 | '$/'; |
||
| 33 | |||
| 34 | /** |
||
| 35 | * Regex pattern to parse NR2 form number. |
||
| 36 | * |
||
| 37 | * @var string |
||
| 38 | */ |
||
| 39 | const NR2_REGEX = '/^\s*' . |
||
| 40 | '(?<s>[+\-])?' . // sign |
||
| 41 | '(?<d>(?:\d+[\.,]\d*)|(?:\d*[\.,]\d+))' . // decimal number |
||
| 42 | '$/'; |
||
| 43 | |||
| 44 | /** |
||
| 45 | * Regex pattern to parse NR3 form number. |
||
| 46 | * |
||
| 47 | * @var string |
||
| 48 | */ |
||
| 49 | const NR3_REGEX = '/^\s*' . |
||
| 50 | '(?<ms>[+\-])?' . // mantissa sign |
||
| 51 | '(?<m>(?:\d+[\.,]\d*)|(?:\d*[\.,]\d+))' . // mantissa |
||
| 52 | '[Ee](?<es>[+\-])?' . // exponent sign |
||
| 53 | '(?<e>\d+)' . // exponent |
||
| 54 | '$/'; |
||
| 55 | |||
| 56 | /** |
||
| 57 | * Regex pattern to parse PHP exponent number format. |
||
| 58 | * |
||
| 59 | * @see http://php.net/manual/en/language.types.float.php |
||
| 60 | * |
||
| 61 | * @var string |
||
| 62 | */ |
||
| 63 | const PHP_EXPONENT_DNUM = '/^' . |
||
| 64 | '(?<ms>[+\-])?' . // sign |
||
| 65 | '(?<m>' . |
||
| 66 | '\d+' . // LNUM |
||
| 67 | '|' . |
||
| 68 | '(?:\d*\.\d+|\d+\.\d*)' . // DNUM |
||
| 69 | ')[eE]' . |
||
| 70 | '(?<es>[+\-])?(?<e>\d+)' . // exponent |
||
| 71 | '$/'; |
||
| 72 | |||
| 73 | /** |
||
| 74 | * Exponent when value is positive or negative infinite. |
||
| 75 | * |
||
| 76 | * @var int |
||
| 77 | */ |
||
| 78 | const INF_EXPONENT = 2047; |
||
| 79 | |||
| 80 | /** |
||
| 81 | * Exponent bias for IEEE 754 double precision float. |
||
| 82 | * |
||
| 83 | * @var int |
||
| 84 | */ |
||
| 85 | const EXP_BIAS = -1023; |
||
| 86 | |||
| 87 | /** |
||
| 88 | * Signed integer mantissa. |
||
| 89 | * |
||
| 90 | * @var BigInt |
||
| 91 | */ |
||
| 92 | private $_mantissa; |
||
| 93 | |||
| 94 | /** |
||
| 95 | * Signed integer exponent. |
||
| 96 | * |
||
| 97 | * @var BigInt |
||
| 98 | */ |
||
| 99 | private $_exponent; |
||
| 100 | |||
| 101 | /** |
||
| 102 | * Abstract value base. |
||
| 103 | * |
||
| 104 | * Must be 2 or 10. |
||
| 105 | * |
||
| 106 | * @var int |
||
| 107 | */ |
||
| 108 | private $_base; |
||
| 109 | |||
| 110 | /** |
||
| 111 | * Whether to encode strictly in DER. |
||
| 112 | * |
||
| 113 | * @var bool |
||
| 114 | */ |
||
| 115 | private $_strictDer; |
||
| 116 | |||
| 117 | /** |
||
| 118 | * Number as a native float. |
||
| 119 | * |
||
| 120 | * @internal Lazily initialized |
||
| 121 | * |
||
| 122 | * @var null|float |
||
| 123 | */ |
||
| 124 | private $_float; |
||
| 125 | |||
| 126 | /** |
||
| 127 | * Constructor. |
||
| 128 | * |
||
| 129 | * @param \GMP|int|string $mantissa Integer mantissa |
||
| 130 | * @param \GMP|int|string $exponent Integer exponent |
||
| 131 | * @param int $base Base, 2 or 10 |
||
| 132 | */ |
||
| 133 | 56 | public function __construct($mantissa, $exponent, int $base = 10) |
|
| 134 | { |
||
| 135 | 56 | if (10 !== $base && 2 !== $base) { |
|
| 136 | 1 | throw new \UnexpectedValueException('Base must be 2 or 10.'); |
|
| 137 | } |
||
| 138 | 55 | $this->_typeTag = self::TYPE_REAL; |
|
| 139 | 55 | $this->_strictDer = true; |
|
| 140 | 55 | $this->_mantissa = new BigInt($mantissa); |
|
| 141 | 55 | $this->_exponent = new BigInt($exponent); |
|
| 142 | 55 | $this->_base = $base; |
|
| 143 | 55 | } |
|
| 144 | |||
| 145 | /** |
||
| 146 | * {@inheritdoc} |
||
| 147 | */ |
||
| 148 | 1 | public function __toString(): string |
|
| 149 | { |
||
| 150 | 1 | return sprintf('%g', $this->floatVal()); |
|
| 151 | } |
||
| 152 | |||
| 153 | /** |
||
| 154 | * Create base 2 real number from float. |
||
| 155 | * |
||
| 156 | * @param float $number |
||
| 157 | * |
||
| 158 | * @return self |
||
| 159 | */ |
||
| 160 | 36 | public static function fromFloat(float $number): self |
|
| 161 | { |
||
| 162 | 36 | if (is_infinite($number)) { |
|
| 163 | 4 | return self::_fromInfinite($number); |
|
| 164 | } |
||
| 165 | 32 | if (is_nan($number)) { |
|
| 166 | 1 | throw new \UnexpectedValueException('NaN values not supported.'); |
|
| 167 | } |
||
| 168 | 31 | [$m, $e] = self::_parse754Double(pack('E', $number)); |
|
| 169 | 31 | return new self($m, $e, 2); |
|
| 170 | } |
||
| 171 | |||
| 172 | /** |
||
| 173 | * Create base 10 real number from string. |
||
| 174 | * |
||
| 175 | * @param string $number Real number in base-10 textual form |
||
| 176 | * |
||
| 177 | * @return self |
||
| 178 | */ |
||
| 179 | 10 | public static function fromString(string $number): self |
|
| 180 | { |
||
| 181 | 10 | [$m, $e] = self::_parseString($number); |
|
| 182 | 9 | return new self($m, $e, 10); |
|
|
1 ignored issue
–
show
|
|||
| 183 | } |
||
| 184 | |||
| 185 | /** |
||
| 186 | * Get self with strict DER flag set or unset. |
||
| 187 | * |
||
| 188 | * @param bool $strict whether to encode strictly in DER |
||
| 189 | * |
||
| 190 | * @return self |
||
| 191 | */ |
||
| 192 | 20 | public function withStrictDER(bool $strict): self |
|
| 193 | { |
||
| 194 | 20 | $obj = clone $this; |
|
| 195 | 20 | $obj->_strictDer = $strict; |
|
| 196 | 20 | return $obj; |
|
| 197 | } |
||
| 198 | |||
| 199 | /** |
||
| 200 | * Get the mantissa. |
||
| 201 | * |
||
| 202 | * @return BigInt |
||
| 203 | */ |
||
| 204 | 1 | public function mantissa(): BigInt |
|
| 205 | { |
||
| 206 | 1 | return $this->_mantissa; |
|
| 207 | } |
||
| 208 | |||
| 209 | /** |
||
| 210 | * Get the exponent. |
||
| 211 | * |
||
| 212 | * @return BigInt |
||
| 213 | */ |
||
| 214 | 3 | public function exponent(): BigInt |
|
| 215 | { |
||
| 216 | 3 | return $this->_exponent; |
|
| 217 | } |
||
| 218 | |||
| 219 | /** |
||
| 220 | * Get the base. |
||
| 221 | * |
||
| 222 | * @return int |
||
| 223 | */ |
||
| 224 | 1 | public function base(): int |
|
| 225 | { |
||
| 226 | 1 | return $this->_base; |
|
| 227 | } |
||
| 228 | |||
| 229 | /** |
||
| 230 | * Get number as a float. |
||
| 231 | * |
||
| 232 | * @return float |
||
| 233 | */ |
||
| 234 | 46 | public function floatVal(): float |
|
| 235 | { |
||
| 236 | 46 | if (!isset($this->_float)) { |
|
| 237 | 46 | $m = $this->_mantissa->intVal(); |
|
| 238 | 46 | $e = $this->_exponent->intVal(); |
|
| 239 | 46 | $this->_float = (float) ($m * pow($this->_base, $e)); |
|
| 240 | } |
||
| 241 | 46 | return $this->_float; |
|
|
1 ignored issue
–
show
|
|||
| 242 | } |
||
| 243 | |||
| 244 | /** |
||
| 245 | * Get number as a NR3 form string conforming to DER rules. |
||
| 246 | * |
||
| 247 | * @return string |
||
| 248 | */ |
||
| 249 | 5 | public function nr3Val(): string |
|
| 250 | { |
||
| 251 | // convert to base 10 |
||
| 252 | 5 | if (2 === $this->_base) { |
|
| 253 | 1 | [$m, $e] = self::_parseString(sprintf('%15E', $this->floatVal())); |
|
| 254 | } else { |
||
| 255 | 4 | $m = $this->_mantissa->gmpObj(); |
|
| 256 | 4 | $e = $this->_exponent->gmpObj(); |
|
| 257 | } |
||
| 258 | // shift trailing zeroes from the mantissa to the exponent |
||
| 259 | // (X.690 07-2002, section 11.3.2.4) |
||
| 260 | 5 | while (0 != $m && 0 == $m % 10) { |
|
| 261 | 1 | $m /= 10; |
|
| 262 | 1 | ++$e; |
|
| 263 | } |
||
| 264 | // if exponent is zero, it must be prefixed with a "+" sign |
||
| 265 | // (X.690 07-2002, section 11.3.2.6) |
||
| 266 | 5 | if (0 == $e) { |
|
| 267 | 1 | $es = '+'; |
|
| 268 | } else { |
||
| 269 | 4 | $es = $e < 0 ? '-' : ''; |
|
| 270 | } |
||
| 271 | 5 | return sprintf('%s.E%s%s', gmp_strval($m), $es, gmp_strval(gmp_abs($e))); |
|
| 272 | } |
||
| 273 | |||
| 274 | /** |
||
| 275 | * {@inheritdoc} |
||
| 276 | */ |
||
| 277 | 43 | protected function _encodedContentDER(): string |
|
| 278 | { |
||
| 279 | 43 | if (self::INF_EXPONENT == $this->_exponent->gmpObj()) { |
|
|
1 ignored issue
–
show
|
|||
| 280 | 5 | return $this->_encodeSpecial(); |
|
| 281 | } |
||
| 282 | // if the real value is the value zero, there shall be no contents |
||
| 283 | // octets in the encoding. (X.690 07-2002, section 8.5.2) |
||
| 284 | 38 | if (0 == $this->_mantissa->gmpObj()) { |
|
|
1 ignored issue
–
show
|
|||
| 285 | 2 | return ''; |
|
| 286 | } |
||
| 287 | 36 | if (10 === $this->_base) { |
|
| 288 | 1 | return $this->_encodeDecimal(); |
|
| 289 | } |
||
| 290 | 35 | return $this->_encodeBinary(); |
|
| 291 | } |
||
| 292 | |||
| 293 | /** |
||
| 294 | * Encode in binary format. |
||
| 295 | * |
||
| 296 | * @return string |
||
| 297 | */ |
||
| 298 | 35 | protected function _encodeBinary(): string |
|
| 299 | { |
||
| 300 | 35 | [$base, $sign, $m, $e] = $this->_prepareBinaryEncoding(); |
|
| 301 | 35 | $zero = gmp_init(0, 10); |
|
| 302 | 35 | $byte = 0x80; |
|
| 303 | 35 | if ($sign < 0) { |
|
| 304 | 14 | $byte |= 0x40; |
|
| 305 | } |
||
| 306 | // normalization: mantissa must be 0 or odd |
||
| 307 | 35 | if (2 === $base) { |
|
| 308 | // while last bit is zero |
||
| 309 | 22 | while ($m > 0 && 0 === gmp_cmp($m & 0x01, $zero)) { |
|
| 310 | 1 | $m >>= 1; |
|
| 311 | 1 | ++$e; |
|
| 312 | } |
||
| 313 | 13 | } elseif (8 === $base) { |
|
| 314 | 5 | $byte |= 0x10; |
|
| 315 | // while last 3 bits are zero |
||
| 316 | 5 | while ($m > 0 && 0 === gmp_cmp($m & 0x07, $zero)) { |
|
| 317 | 1 | $m >>= 3; |
|
| 318 | 1 | ++$e; |
|
| 319 | } |
||
| 320 | } else { // base === 16 |
||
| 321 | 8 | $byte |= 0x20; |
|
| 322 | // while last 4 bits are zero |
||
| 323 | 8 | while ($m > 0 && 0 === gmp_cmp($m & 0x0f, $zero)) { |
|
| 324 | 2 | $m >>= 4; |
|
| 325 | 2 | ++$e; |
|
| 326 | } |
||
| 327 | } |
||
| 328 | // scale factor |
||
| 329 | 35 | $scale = 0; |
|
| 330 | 35 | while ($m > 0 && 0 === gmp_cmp($m & 0x01, $zero)) { |
|
| 331 | 1 | $m >>= 1; |
|
| 332 | 1 | ++$scale; |
|
| 333 | } |
||
| 334 | 35 | $byte |= ($scale & 0x03) << 2; |
|
| 335 | // encode exponent |
||
| 336 | 35 | $exp_bytes = (new BigInt($e))->signedOctets(); |
|
| 337 | 35 | $exp_len = strlen($exp_bytes); |
|
| 338 | 35 | if ($exp_len > 0xff) { |
|
| 339 | 1 | throw new \RangeException('Exponent encoding is too long.'); |
|
| 340 | } |
||
| 341 | 34 | if ($exp_len <= 3) { |
|
| 342 | 32 | $byte |= ($exp_len - 1) & 0x03; |
|
| 343 | 32 | $bytes = chr($byte); |
|
| 344 | } else { |
||
| 345 | 2 | $byte |= 0x03; |
|
| 346 | 2 | $bytes = chr($byte) . chr($exp_len); |
|
| 347 | } |
||
| 348 | 34 | $bytes .= $exp_bytes; |
|
| 349 | // encode mantissa |
||
| 350 | 34 | $bytes .= (new BigInt($m))->unsignedOctets(); |
|
|
1 ignored issue
–
show
|
|||
| 351 | 34 | return $bytes; |
|
| 352 | } |
||
| 353 | |||
| 354 | /** |
||
| 355 | * Encode in decimal format. |
||
| 356 | * |
||
| 357 | * @return strign |
||
| 358 | */ |
||
| 359 | 1 | protected function _encodeDecimal(): string |
|
| 360 | { |
||
| 361 | // encode in NR3 decimal encoding |
||
| 362 | 1 | return chr(0x03) . $this->nr3Val(); |
|
| 363 | } |
||
| 364 | |||
| 365 | /** |
||
| 366 | * Encode special value. |
||
| 367 | * |
||
| 368 | * @return string |
||
| 369 | */ |
||
| 370 | 5 | protected function _encodeSpecial(): string |
|
| 371 | { |
||
| 372 | 5 | switch ($this->_mantissa->intVal()) { |
|
| 373 | // positive infitinity |
||
| 374 | 5 | case 1: |
|
| 375 | 2 | return chr(0x40); |
|
| 376 | // negative infinity |
||
| 377 | case -1: |
||
| 378 | 2 | return chr(0x41); |
|
| 379 | } |
||
| 380 | 1 | throw new \LogicException('Invalid special value.'); |
|
| 381 | } |
||
| 382 | |||
| 383 | /** |
||
| 384 | * {@inheritdoc} |
||
| 385 | */ |
||
| 386 | 47 | protected static function _decodeFromDER(Identifier $identifier, |
|
| 387 | string $data, int &$offset): ElementBase |
||
| 388 | { |
||
| 389 | 47 | $idx = $offset; |
|
| 390 | 47 | $length = Length::expectFromDER($data, $idx)->intLength(); |
|
| 391 | // if length is zero, value is zero (spec 8.5.2) |
||
| 392 | 47 | if (!$length) { |
|
| 393 | 2 | $obj = new self(0, 0, 10); |
|
| 394 | } else { |
||
| 395 | 45 | $bytes = substr($data, $idx, $length); |
|
| 396 | 45 | $byte = ord($bytes[0]); |
|
| 397 | 45 | if (0x80 & $byte) { // bit 8 = 1 |
|
| 398 | 37 | $obj = self::_decodeBinaryEncoding($bytes); |
|
| 399 | 8 | } elseif (0x00 === $byte >> 6) { // bit 8 = 0, bit 7 = 0 |
|
| 400 | 2 | $obj = self::_decodeDecimalEncoding($bytes); |
|
| 401 | } else { // bit 8 = 0, bit 7 = 1 |
||
| 402 | 6 | $obj = self::_decodeSpecialRealValue($bytes); |
|
| 403 | } |
||
| 404 | } |
||
| 405 | 40 | $offset = $idx + $length; |
|
| 406 | 40 | return $obj; |
|
| 407 | } |
||
| 408 | |||
| 409 | /** |
||
| 410 | * Decode binary encoding. |
||
| 411 | * |
||
| 412 | * @param string $data |
||
| 413 | */ |
||
| 414 | 37 | protected static function _decodeBinaryEncoding(string $data) |
|
| 415 | { |
||
| 416 | 37 | $byte = ord($data[0]); |
|
| 417 | // bit 7 is set if mantissa is negative |
||
| 418 | 37 | $neg = (bool) (0x40 & $byte); |
|
| 419 | // encoding base in bits 6 and 5 |
||
| 420 | 37 | switch (($byte >> 4) & 0x03) { |
|
| 421 | 37 | case 0b00: |
|
| 422 | 23 | $base = 2; |
|
| 423 | 23 | break; |
|
| 424 | 14 | case 0b01: |
|
| 425 | 5 | $base = 8; |
|
| 426 | 5 | break; |
|
| 427 | 9 | case 0b10: |
|
| 428 | 8 | $base = 16; |
|
| 429 | 8 | break; |
|
| 430 | default: |
||
| 431 | 1 | throw new DecodeException( |
|
| 432 | 1 | 'Reserved REAL binary encoding base not supported.'); |
|
| 433 | } |
||
| 434 | // scaling factor in bits 4 and 3 |
||
| 435 | 36 | $scale = ($byte >> 2) & 0x03; |
|
| 436 | 36 | $idx = 1; |
|
| 437 | // content length in bits 2 and 1 |
||
| 438 | 36 | $len = ($byte & 0x03) + 1; |
|
| 439 | // if both bits are set, the next octet encodes the length |
||
| 440 | 36 | if ($len > 3) { |
|
| 441 | 2 | if (strlen($data) < 2) { |
|
| 442 | 1 | throw new DecodeException( |
|
| 443 | 1 | 'Unexpected end of data while decoding REAL exponent length.'); |
|
| 444 | } |
||
| 445 | 1 | $len = ord($data[1]); |
|
| 446 | 1 | $idx = 2; |
|
| 447 | } |
||
| 448 | 35 | if (strlen($data) < $idx + $len) { |
|
| 449 | 1 | throw new DecodeException( |
|
| 450 | 1 | 'Unexpected end of data while decoding REAL exponent.'); |
|
| 451 | } |
||
| 452 | // decode exponent |
||
| 453 | 34 | $octets = substr($data, $idx, $len); |
|
| 454 | 34 | $exp = BigInt::fromSignedOctets($octets)->gmpObj(); |
|
| 455 | 34 | if (8 === $base) { |
|
| 456 | 5 | $exp *= 3; |
|
| 457 | 29 | } elseif (16 === $base) { |
|
| 458 | 8 | $exp *= 4; |
|
| 459 | } |
||
| 460 | 34 | if (strlen($data) <= $idx + $len) { |
|
| 461 | 1 | throw new DecodeException( |
|
| 462 | 1 | 'Unexpected end of data while decoding REAL mantissa.'); |
|
| 463 | } |
||
| 464 | // decode mantissa |
||
| 465 | 33 | $octets = substr($data, $idx + $len); |
|
| 466 | 33 | $n = BigInt::fromUnsignedOctets($octets)->gmpObj(); |
|
| 467 | 33 | $n *= 2 ** $scale; |
|
| 468 | 33 | if ($neg) { |
|
| 469 | 14 | $n = gmp_neg($n); |
|
| 470 | } |
||
| 471 | 33 | return new self($n, $exp, 2); |
|
|
1 ignored issue
–
show
|
|||
| 472 | } |
||
| 473 | |||
| 474 | /** |
||
| 475 | * Decode decimal encoding. |
||
| 476 | * |
||
| 477 | * @param string $data |
||
| 478 | * |
||
| 479 | * @throws \RuntimeException |
||
| 480 | * |
||
| 481 | * @return self |
||
| 482 | */ |
||
| 483 | 2 | protected static function _decodeDecimalEncoding(string $data): self |
|
| 484 | { |
||
| 485 | 2 | $nr = ord($data[0]) & 0x3f; |
|
| 486 | 2 | if (!in_array($nr, [1, 2, 3])) { |
|
| 487 | 1 | throw new DecodeException('Unsupported decimal encoding form.'); |
|
| 488 | } |
||
| 489 | 1 | $str = substr($data, 1); |
|
| 490 | 1 | return self::fromString($str); |
|
| 491 | } |
||
| 492 | |||
| 493 | /** |
||
| 494 | * Decode special encoding. |
||
| 495 | * |
||
| 496 | * @param string $data |
||
| 497 | * |
||
| 498 | * @return self |
||
| 499 | */ |
||
| 500 | 6 | protected static function _decodeSpecialRealValue(string $data): self |
|
| 514 | } |
||
| 515 | |||
| 516 | /** |
||
| 517 | * Prepare value for binary encoding. |
||
| 518 | * |
||
| 519 | * @return array (int) base, (int) sign, (\GMP) mantissa and (\GMP) exponent |
||
| 520 | */ |
||
| 521 | 35 | protected function _prepareBinaryEncoding(): array |
|
| 541 | } |
||
| 542 | |||
| 543 | /** |
||
| 544 | * Initialize from INF or -INF. |
||
| 545 | * |
||
| 546 | * @param float $inf |
||
| 547 | * |
||
| 548 | * @return self |
||
| 549 | */ |
||
| 550 | 4 | private static function _fromInfinite(float $inf): self |
|
| 551 | { |
||
| 552 | 4 | return new self($inf === -INF ? -1 : 1, self::INF_EXPONENT, 2); |
|
| 553 | } |
||
| 554 | |||
| 555 | /** |
||
| 556 | * Parse IEEE 754 big endian formatted double precision float to base 2 |
||
| 557 | * mantissa and exponent. |
||
| 558 | * |
||
| 559 | * @param string $octets 64 bits |
||
| 560 | * |
||
| 561 | * @return \GMP[] Tuple of mantissa and exponent |
||
| 562 | */ |
||
| 563 | 31 | private static function _parse754Double(string $octets): array |
|
| 564 | { |
||
| 565 | 31 | $n = gmp_import($octets, 1, GMP_MSW_FIRST | GMP_BIG_ENDIAN); |
|
| 566 | // sign bit |
||
| 567 | 31 | $neg = gmp_testbit($n, 63); |
|
|
1 ignored issue
–
show
|
|||
| 568 | // 11 bits of biased exponent |
||
| 569 | 31 | $exp = (gmp_and($n, '0x7ff0000000000000') >> 52) + self::EXP_BIAS; |
|
|
1 ignored issue
–
show
|
|||
| 570 | // 52 bits of mantissa |
||
| 571 | 31 | $man = gmp_and($n, '0xfffffffffffff'); |
|
| 572 | // zero, ASN.1 doesn't differentiate -0 from +0 |
||
| 573 | 31 | if (self::EXP_BIAS == $exp && 0 == $man) { |
|
| 574 | 2 | return [gmp_init(0, 10), gmp_init(0, 10)]; |
|
|
1 ignored issue
–
show
|
|||
| 575 | } |
||
| 576 | // denormalized value, shift binary point |
||
| 577 | 29 | if (self::EXP_BIAS == $exp) { |
|
| 578 | 4 | ++$exp; |
|
| 579 | } |
||
| 580 | // normalized value, insert implicit leading one before the binary point |
||
| 581 | else { |
||
| 582 | 25 | gmp_setbit($man, 52); |
|
| 583 | } |
||
| 584 | // find the last fraction bit that is set |
||
| 585 | 29 | $last = gmp_scan1($man, 0); |
|
| 586 | 29 | $bits_for_fraction = 52 - $last; |
|
| 587 | // adjust mantissa and exponent so that we have integer values |
||
| 588 | 29 | $man >>= $last; |
|
| 589 | 29 | $exp -= $bits_for_fraction; |
|
| 590 | // negate mantissa if number was negative |
||
| 591 | 29 | if ($neg) { |
|
| 592 | 15 | $man = gmp_neg($man); |
|
| 593 | } |
||
| 594 | 29 | return [$man, $exp]; |
|
|
1 ignored issue
–
show
|
|||
| 595 | } |
||
| 596 | |||
| 597 | /** |
||
| 598 | * Parse textual REAL number to base 10 mantissa and exponent. |
||
| 599 | * |
||
| 600 | * @param string $str Number |
||
| 601 | * |
||
| 602 | * @return \GMP[] Tuple of mantissa and exponent |
||
| 603 | */ |
||
| 604 | 11 | private static function _parseString(string $str): array |
|
| 605 | { |
||
| 606 | // PHP exponent format |
||
| 607 | 11 | if (preg_match(self::PHP_EXPONENT_DNUM, $str, $match)) { |
|
| 608 | 2 | [$m, $e] = self::_parsePHPExponentMatch($match); |
|
| 609 | } |
||
| 610 | // NR3 format |
||
| 611 | 9 | elseif (preg_match(self::NR3_REGEX, $str, $match)) { |
|
| 612 | 3 | [$m, $e] = self::_parseNR3Match($match); |
|
| 613 | } |
||
| 614 | // NR2 format |
||
| 615 | 6 | elseif (preg_match(self::NR2_REGEX, $str, $match)) { |
|
| 616 | 2 | [$m, $e] = self::_parseNR2Match($match); |
|
| 617 | } |
||
| 618 | // NR1 format |
||
| 619 | 4 | elseif (preg_match(self::NR1_REGEX, $str, $match)) { |
|
| 620 | 3 | [$m, $e] = self::_parseNR1Match($match); |
|
| 621 | } |
||
| 622 | // invalid number |
||
| 623 | else { |
||
| 624 | 1 | throw new \UnexpectedValueException( |
|
| 625 | 1 | "{$str} could not be parsed to REAL."); |
|
| 626 | } |
||
| 627 | // normalize so that mantsissa has no trailing zeroes |
||
| 628 | 10 | while (0 != $m && 0 == $m % 10) { |
|
| 629 | 1 | $m /= 10; |
|
| 630 | 1 | ++$e; |
|
| 631 | } |
||
| 632 | 10 | return [$m, $e]; |
|
|
1 ignored issue
–
show
|
|||
| 633 | } |
||
| 634 | |||
| 635 | /** |
||
| 636 | * Parse PHP form float to base 10 mantissa and exponent. |
||
| 637 | * |
||
| 638 | * @param array $match Regexp match |
||
| 639 | * |
||
| 640 | * @return \GMP[] Tuple of mantissa and exponent |
||
| 641 | */ |
||
| 642 | 2 | private static function _parsePHPExponentMatch(array $match): array |
|
| 661 | } |
||
| 662 | |||
| 663 | /** |
||
| 664 | * Parse NR3 form number to base 10 mantissa and exponent. |
||
| 665 | * |
||
| 666 | * @param array $match Regexp match |
||
| 667 | * |
||
| 668 | * @return \GMP[] Tuple of mantissa and exponent |
||
| 669 | */ |
||
| 670 | 3 | private static function _parseNR3Match(array $match): array |
|
| 691 | } |
||
| 692 | |||
| 693 | /** |
||
| 694 | * Parse NR2 form number to base 10 mantissa and exponent. |
||
| 695 | * |
||
| 696 | * @param array $match Regexp match |
||
| 697 | * |
||
| 698 | * @return \GMP[] Tuple of mantissa and exponent |
||
| 699 | */ |
||
| 700 | 2 | private static function _parseNR2Match(array $match): array |
|
| 717 | } |
||
| 718 | |||
| 719 | /** |
||
| 720 | * Parse NR1 form number to base 10 mantissa and exponent. |
||
| 721 | * |
||
| 722 | * @param array $match Regexp match |
||
| 723 | * |
||
| 724 | * @return \GMP[] Tuple of mantissa and exponent |
||
| 725 | */ |
||
| 726 | 3 | private static function _parseNR1Match(array $match): array |
|
| 727 | { |
||
| 728 | 3 | $sign = '-' === $match['s'] ? -1 : 1; |
|
| 737 |