Failed Conditions
Pull Request — master (#3876)
by Abdul Malik
22:45 queued 13:31
created

ConvertDecimal   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 204
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 62
dl 0
loc 204
ccs 57
cts 57
cp 1
rs 10
c 0
b 0
f 0
wmc 26
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;
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_STRING, expecting ',' or ';' on line 10 at column 47
Loading history...
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