|
16 | 16 | */
|
17 | 17 | class Decimal
|
18 | 18 | {
|
| 19 | + const CLASSIC_DECIMAL_NUMBER_REGEXP = '/^([+\-]?)0*(([1-9][0-9]*|[0-9])(\.[0-9]+)?)$/'; |
| 20 | + const EXP_NOTATION_NUMBER_REGEXP = '/([+\-]?)0*([0-9](\.[0-9]+)?)[eE]([+\-]?)(\d+)/'; |
| 21 | + const EXP_NUM_GROUPS_NUMBER_REGEXP = "/^ (?P<int> \d*) (?: \. (?P<dec> \d+) ) E (?P<sign>[\+\-]) (?P<exp>\d+) $/x"; |
| 22 | + |
19 | 23 | /**
|
20 | 24 | * Internal numeric value
|
21 | 25 | * @var string
|
@@ -90,7 +94,7 @@ public static function fromFloat(float $fltValue, int $scale = null, bool $remov
|
90 | 94 | $defaultScale = 16;
|
91 | 95 |
|
92 | 96 | $strValue = (string) $fltValue;
|
93 |
| - if (\preg_match("/^ (?P<int> \d*) (?: \. (?P<dec> \d+) ) E (?P<sign>[\+\-]) (?P<exp>\d+) $/x", $strValue, $capture)) { |
| 97 | + if (\preg_match(self::EXP_NUM_GROUPS_NUMBER_REGEXP, $strValue, $capture)) { |
94 | 98 | if ($scale === null) {
|
95 | 99 | if ($capture['sign'] == '-') {
|
96 | 100 | $scale = $capture['exp'] + \strlen($capture['dec']);
|
@@ -122,37 +126,26 @@ public static function fromString(string $strValue, int $scale = null, bool $rem
|
122 | 126 | {
|
123 | 127 | self::paramsValidation($strValue, $scale);
|
124 | 128 |
|
125 |
| - if (\preg_match('/^([+\-]?)0*(([1-9][0-9]*|[0-9])(\.[0-9]+)?)$/', $strValue, $captures) === 1) { |
| 129 | + if (\preg_match(self::CLASSIC_DECIMAL_NUMBER_REGEXP, $strValue, $captures) === 1) { |
126 | 130 |
|
127 | 131 | // Now it's time to strip leading zeros in order to normalize inner values
|
128 | 132 | $value = self::normalizeSign($captures[1]) . $captures[2];
|
129 | 133 | $min_scale = isset($captures[4]) ? \max(0, \strlen($captures[4]) - 1) : 0;
|
130 | 134 |
|
131 |
| - } elseif (\preg_match('/([+\-]?)0*([0-9](\.[0-9]+)?)[eE]([+\-]?)(\d+)/', $strValue, $captures) === 1) { |
132 |
| - |
133 |
| - $mantissa_scale = \max(\strlen($captures[3]) - 1, 0); |
134 |
| - |
135 |
| - $exp_val = (int)$captures[5]; |
136 |
| - |
137 |
| - if (self::normalizeSign($captures[4]) === '') { |
138 |
| - $min_scale = \max($mantissa_scale - $exp_val, 0); |
139 |
| - $tmp_multiplier = \bcpow('10', (string)$exp_val); |
140 |
| - } else { |
141 |
| - $min_scale = $mantissa_scale + $exp_val; |
142 |
| - $tmp_multiplier = \bcpow('10', (string)-$exp_val, $exp_val); |
143 |
| - } |
144 |
| - |
145 |
| - $value = self::normalizeSign($captures[1]) . \bcmul( |
| 135 | + } elseif (\preg_match(self::EXP_NOTATION_NUMBER_REGEXP, $strValue, $captures) === 1) { |
| 136 | + list($min_scale, $value) = self::fromExpNotationString( |
| 137 | + $scale, |
| 138 | + $captures[1], |
146 | 139 | $captures[2],
|
147 |
| - $tmp_multiplier, |
148 |
| - \max($min_scale, $scale !== null ? $scale : 0) |
| 140 | + $captures[3], |
| 141 | + $captures[4], |
| 142 | + (int)$captures[5] |
149 | 143 | );
|
150 |
| - |
151 | 144 | } else {
|
152 | 145 | throw new NaNInputError('strValue must be a number');
|
153 | 146 | }
|
154 | 147 |
|
155 |
| - $scale = ($scale!==null) ? $scale : $min_scale; |
| 148 | + $scale = (null !== $scale) ? $scale : $min_scale; |
156 | 149 | if ($scale < $min_scale) {
|
157 | 150 | $value = self::innerRound($value, $scale);
|
158 | 151 | }
|
@@ -1092,6 +1085,40 @@ public function __toString(): string
|
1092 | 1085 | return $this->value;
|
1093 | 1086 | }
|
1094 | 1087 |
|
| 1088 | + /* |
| 1089 | + * |
| 1090 | + */ |
| 1091 | + private static function fromExpNotationString( |
| 1092 | + int $scale = null, |
| 1093 | + string $sign, |
| 1094 | + string $mantissa, |
| 1095 | + string $mantissaDecimals, |
| 1096 | + string $expSign, |
| 1097 | + int $expVal |
| 1098 | + ): array |
| 1099 | + { |
| 1100 | + $mantissaScale = \max(\strlen($mantissaDecimals) - 1, 0); |
| 1101 | + |
| 1102 | + if (self::normalizeSign($expSign) === '') { |
| 1103 | + $minScale = \max($mantissaScale - $expVal, 0); |
| 1104 | + $tmp_multiplier = \bcpow('10', (string)$expVal); |
| 1105 | + } else { |
| 1106 | + $minScale = $mantissaScale + $expVal; |
| 1107 | + $tmp_multiplier = \bcpow('10', (string)-$expVal, $expVal); |
| 1108 | + } |
| 1109 | + |
| 1110 | + $value = ( |
| 1111 | + self::normalizeSign($sign) . |
| 1112 | + \bcmul( |
| 1113 | + $mantissa, |
| 1114 | + $tmp_multiplier, |
| 1115 | + \max($minScale, $scale ?? 0) |
| 1116 | + ) |
| 1117 | + ); |
| 1118 | + |
| 1119 | + return [$minScale, $value]; |
| 1120 | + } |
| 1121 | + |
1095 | 1122 | /**
|
1096 | 1123 | * "Rounds" the decimal string to have at most $scale digits after the point
|
1097 | 1124 | *
|
|
0 commit comments