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
|
|
|
public const LARGEST_OCTAL_IN_DECIMAL = 536_870_911; |
|
|
|
|
11
|
|
|
public const SMALLEST_OCTAL_IN_DECIMAL = -536_870_912; |
12
|
|
|
public const LARGEST_BINARY_IN_DECIMAL = 511; |
13
|
|
|
public const SMALLEST_BINARY_IN_DECIMAL = -512; |
14
|
|
|
public const LARGEST_HEX_IN_DECIMAL = 549_755_813_887; |
15
|
|
|
public const SMALLEST_HEX_IN_DECIMAL = -549_755_813_888; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* toBinary. |
19
|
|
|
* |
20
|
|
|
* Return a decimal value as binary. |
21
|
|
|
* |
22
|
|
|
* Excel Function: |
23
|
|
|
* DEC2BIN(x[,places]) |
24
|
|
|
* |
25
|
|
|
* @param array|bool|float|int|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 null|array|float|int|string $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
|
209 |
|
public static function toBinary($value, $places = null): array|string |
49
|
|
|
{ |
50
|
209 |
|
if (is_array($value) || is_array($places)) { |
51
|
33 |
|
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places); |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
try { |
55
|
209 |
|
$value = self::validateValue($value); |
56
|
203 |
|
$value = self::validateDecimal($value); |
57
|
200 |
|
$places = self::validatePlaces($places); |
58
|
21 |
|
} catch (Exception $e) { |
59
|
21 |
|
return $e->getMessage(); |
60
|
|
|
} |
61
|
|
|
|
62
|
188 |
|
$value = (int) floor((float) $value); |
63
|
188 |
|
if ($value > self::LARGEST_BINARY_IN_DECIMAL || $value < self::SMALLEST_BINARY_IN_DECIMAL) { |
64
|
48 |
|
return ExcelError::NAN(); |
65
|
|
|
} |
66
|
|
|
|
67
|
140 |
|
$r = decbin($value); |
68
|
|
|
// Two's Complement |
69
|
140 |
|
$r = substr($r, -10); |
70
|
|
|
|
71
|
140 |
|
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|bool|float|int|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 null|array|float|int|string $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
|
95 |
|
public static function toHex($value, $places = null): array|string |
106
|
|
|
{ |
107
|
95 |
|
if (is_array($value) || is_array($places)) { |
108
|
32 |
|
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
try { |
112
|
95 |
|
$value = self::validateValue($value); |
113
|
89 |
|
$value = self::validateDecimal($value); |
114
|
86 |
|
$places = self::validatePlaces($places); |
115
|
21 |
|
} catch (Exception $e) { |
116
|
21 |
|
return $e->getMessage(); |
117
|
|
|
} |
118
|
|
|
|
119
|
74 |
|
$value = floor((float) $value); |
120
|
74 |
|
if ($value > self::LARGEST_HEX_IN_DECIMAL || $value < self::SMALLEST_HEX_IN_DECIMAL) { |
121
|
6 |
|
return ExcelError::NAN(); |
122
|
|
|
} |
123
|
68 |
|
$r = strtoupper(dechex((int) $value)); |
124
|
68 |
|
$r = self::hex32bit($value, $r); |
125
|
|
|
|
126
|
68 |
|
return self::nbrConversionFormat($r, $places); |
127
|
|
|
} |
128
|
|
|
|
129
|
69 |
|
public static function hex32bit(float $value, string $hexstr, bool $force = false): string |
130
|
|
|
{ |
131
|
69 |
|
if (PHP_INT_SIZE === 4 || $force) { |
132
|
1 |
|
if ($value >= 2 ** 32) { |
133
|
1 |
|
$quotient = (int) ($value / (2 ** 32)); |
134
|
|
|
|
135
|
1 |
|
return strtoupper(substr('0' . dechex($quotient), -2) . $hexstr); |
136
|
|
|
} |
137
|
1 |
|
if ($value < -(2 ** 32)) { |
138
|
1 |
|
$quotient = 256 - (int) ceil((-$value) / (2 ** 32)); |
139
|
|
|
|
140
|
1 |
|
return strtoupper(substr('0' . dechex($quotient), -2) . substr("00000000$hexstr", -8)); |
141
|
|
|
} |
142
|
1 |
|
if ($value < 0) { |
143
|
1 |
|
return "FF$hexstr"; |
144
|
|
|
} |
145
|
|
|
} |
146
|
|
|
|
147
|
68 |
|
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|bool|float|int|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
|
130 |
|
public static function toOctal($value, $places = null): array|string |
182
|
|
|
{ |
183
|
130 |
|
if (is_array($value) || is_array($places)) { |
184
|
25 |
|
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places); |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
try { |
188
|
130 |
|
$value = self::validateValue($value); |
189
|
124 |
|
$value = self::validateDecimal($value); |
190
|
121 |
|
$places = self::validatePlaces($places); |
191
|
15 |
|
} catch (Exception $e) { |
192
|
15 |
|
return $e->getMessage(); |
193
|
|
|
} |
194
|
|
|
|
195
|
115 |
|
$value = (int) floor((float) $value); |
196
|
115 |
|
if ($value > self::LARGEST_OCTAL_IN_DECIMAL || $value < self::SMALLEST_OCTAL_IN_DECIMAL) { |
197
|
15 |
|
return ExcelError::NAN(); |
198
|
|
|
} |
199
|
100 |
|
$r = decoct($value); |
200
|
100 |
|
$r = substr($r, -10); |
201
|
|
|
|
202
|
100 |
|
return self::nbrConversionFormat($r, $places); |
203
|
|
|
} |
204
|
|
|
|
205
|
416 |
|
protected static function validateDecimal(string $value): string |
206
|
|
|
{ |
207
|
416 |
|
if (strlen($value) > preg_match_all('/[-0123456789.]/', $value)) { |
208
|
9 |
|
throw new Exception(ExcelError::VALUE()); |
209
|
|
|
} |
210
|
|
|
|
211
|
407 |
|
return $value; |
212
|
|
|
} |
213
|
|
|
} |
214
|
|
|
|