Skip to content

Commit 2c8662d

Browse files
committed
Fixed a bug in BcMath\Number::pow() and bcpow() when raising negative powers of 0. (#16694)
Closes #16694 Fixes #16236
1 parent 60e4f48 commit 2c8662d

File tree

8 files changed

+147
-20
lines changed

8 files changed

+147
-20
lines changed

ext/bcmath/bcmath.c

+8-2
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,10 @@ PHP_FUNCTION(bcpow)
615615
goto cleanup;
616616
}
617617

618-
bc_raise(first, exponent, &result, scale);
618+
if (!bc_raise(first, exponent, &result, scale)) {
619+
zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Negative power of zero");
620+
goto cleanup;
621+
}
619622

620623
RETVAL_NEW_STR(bc_num2str_ex(result, scale));
621624

@@ -1141,7 +1144,10 @@ static zend_result bcmath_number_pow_internal(
11411144
}
11421145
return FAILURE;
11431146
}
1144-
bc_raise(n1, exponent, ret, *scale);
1147+
if (!bc_raise(n1, exponent, ret, *scale)) {
1148+
zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Negative power of zero");
1149+
return FAILURE;
1150+
}
11451151
bc_rm_trailing_zeros(*ret);
11461152
if (scale_expand) {
11471153
size_t diff = *scale - (*ret)->n_scale;

ext/bcmath/libbcmath/src/bcmath.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ typedef enum {
173173

174174
raise_mod_status bc_raisemod(bc_num base, bc_num exponent, bc_num mod, bc_num *result, size_t scale);
175175

176-
void bc_raise(bc_num base, long exponent, bc_num *resul, size_t scale);
176+
bool bc_raise(bc_num base, long exponent, bc_num *result, size_t scale);
177177

178178
void bc_raise_bc_exponent(bc_num base, bc_num exponent, bc_num *resul, size_t scale);
179179

ext/bcmath/libbcmath/src/raise.c

+13-8
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ void bc_square_ex(bc_num n1, bc_num *result, size_t scale_min) {
4040
*(result) = square_ex;
4141
}
4242

43-
/* Raise NUM1 to the NUM2 power. The result is placed in RESULT.
44-
Maximum exponent is LONG_MAX. If a NUM2 is not an integer,
43+
/* Raise "base" to the "exponent" power. The result is placed in RESULT.
44+
Maximum exponent is LONG_MAX. If a "exponent" is not an integer,
4545
only the integer part is used. */
46-
void bc_raise(bc_num num1, long exponent, bc_num *result, size_t scale) {
46+
bool bc_raise(bc_num base, long exponent, bc_num *result, size_t scale) {
4747
bc_num temp, power;
4848
size_t rscale;
4949
size_t pwrscale;
@@ -54,7 +54,7 @@ void bc_raise(bc_num num1, long exponent, bc_num *result, size_t scale) {
5454
if (exponent == 0) {
5555
bc_free_num (result);
5656
*result = bc_copy_num(BCG(_one_));
57-
return;
57+
return true;
5858
}
5959

6060
/* Other initializations. */
@@ -64,12 +64,12 @@ void bc_raise(bc_num num1, long exponent, bc_num *result, size_t scale) {
6464
rscale = scale;
6565
} else {
6666
is_neg = false;
67-
rscale = MIN (num1->n_scale * exponent, MAX(scale, num1->n_scale));
67+
rscale = MIN (base->n_scale * exponent, MAX(scale, base->n_scale));
6868
}
6969

7070
/* Set initial value of temp. */
71-
power = bc_copy_num(num1);
72-
pwrscale = num1->n_scale;
71+
power = bc_copy_num(base);
72+
pwrscale = base->n_scale;
7373
while ((exponent & 1) == 0) {
7474
pwrscale = 2 * pwrscale;
7575
bc_square_ex(power, &power, pwrscale);
@@ -92,14 +92,19 @@ void bc_raise(bc_num num1, long exponent, bc_num *result, size_t scale) {
9292

9393
/* Assign the value. */
9494
if (is_neg) {
95-
bc_divide(BCG(_one_), temp, result, rscale);
95+
if (bc_divide(BCG(_one_), temp, result, rscale) == false) {
96+
bc_free_num (&temp);
97+
bc_free_num (&power);
98+
return false;
99+
}
96100
bc_free_num (&temp);
97101
} else {
98102
bc_free_num (result);
99103
*result = temp;
100104
(*result)->n_scale = MIN(scale, (*result)->n_scale);
101105
}
102106
bc_free_num (&power);
107+
return true;
103108
}
104109

105110
/* This is used internally by BCMath */

ext/bcmath/tests/bcpow.phpt

+8-8
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,17 @@ Number "-9" (scale 0)
6060

6161
Number "0" (scale 0)
6262
0 ** 15 = 0
63-
0 ** -15 = 0
63+
0 ** -15 = Negative power of zero
6464
0 ** 1 = 0
65-
0 ** -9 = 0
65+
0 ** -9 = Negative power of zero
6666
0 ** 0 = 1
6767
0 ** -0 = 1
6868

6969
Number "-0" (scale 0)
7070
-0 ** 15 = 0
71-
-0 ** -15 = 0
71+
-0 ** -15 = Negative power of zero
7272
-0 ** 1 = 0
73-
-0 ** -9 = 0
73+
-0 ** -9 = Negative power of zero
7474
-0 ** 0 = 1
7575
-0 ** -0 = 1
7676

@@ -188,17 +188,17 @@ Number "-9" (scale 10)
188188

189189
Number "0" (scale 10)
190190
0 ** 15 = 0.0000000000
191-
0 ** -15 = 0.0000000000
191+
0 ** -15 = Negative power of zero
192192
0 ** 1 = 0.0000000000
193-
0 ** -9 = 0.0000000000
193+
0 ** -9 = Negative power of zero
194194
0 ** 0 = 1.0000000000
195195
0 ** -0 = 1.0000000000
196196

197197
Number "-0" (scale 10)
198198
-0 ** 15 = 0.0000000000
199-
-0 ** -15 = 0.0000000000
199+
-0 ** -15 = Negative power of zero
200200
-0 ** 1 = 0.0000000000
201-
-0 ** -9 = 0.0000000000
201+
-0 ** -9 = Negative power of zero
202202
-0 ** 0 = 1.0000000000
203203
-0 ** -0 = 1.0000000000
204204

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
bcpow() negative power of zero
3+
--EXTENSIONS--
4+
bcmath
5+
--INI--
6+
bcmath.scale=0
7+
--FILE--
8+
<?php
9+
$exponents = ["-15", "-1", "-9"];
10+
$baseNumbers = ['0', '-0'];
11+
12+
foreach ($baseNumbers as $baseNumber) {
13+
foreach ($exponents as $exponent) {
14+
try {
15+
echo bcpow($baseNumber, $exponent), "\n";
16+
} catch (Error $e) {
17+
echo $e->getMessage(), "\n";
18+
}
19+
}
20+
}
21+
22+
?>
23+
--EXPECT--
24+
Negative power of zero
25+
Negative power of zero
26+
Negative power of zero
27+
Negative power of zero
28+
Negative power of zero
29+
Negative power of zero
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
--TEST--
2+
BcMath\Number pow(): negative power of zero
3+
--EXTENSIONS--
4+
bcmath
5+
--FILE--
6+
<?php
7+
8+
$values = [0, '0'];
9+
10+
$exponents = [
11+
[-3, 'int'],
12+
['-2', 'string'],
13+
[new BcMath\Number('-2'), 'object'],
14+
];
15+
16+
foreach ($values as $value) {
17+
$num = new BcMath\Number($value);
18+
19+
foreach ($exponents as [$exponent, $type]) {
20+
echo "{$value} ** {$exponent}: {$type}\n";
21+
try {
22+
$num->pow($exponent);
23+
} catch (Error $e) {
24+
echo $e->getMessage() . "\n";
25+
}
26+
}
27+
}
28+
?>
29+
--EXPECT--
30+
0 ** -3: int
31+
Negative power of zero
32+
0 ** -2: string
33+
Negative power of zero
34+
0 ** -2: object
35+
Negative power of zero
36+
0 ** -3: int
37+
Negative power of zero
38+
0 ** -2: string
39+
Negative power of zero
40+
0 ** -2: object
41+
Negative power of zero
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
--TEST--
2+
BcMath\Number pow: negative power of zero by operator
3+
--EXTENSIONS--
4+
bcmath
5+
--FILE--
6+
<?php
7+
8+
$values = [0, '0'];
9+
10+
$exponents = [
11+
[-3, 'int'],
12+
['-2', 'string'],
13+
[new BcMath\Number('-2'), 'object'],
14+
];
15+
16+
foreach ($values as $value) {
17+
$num = new BcMath\Number($value);
18+
19+
foreach ($exponents as [$exponent, $type]) {
20+
echo "{$value} ** {$exponent}: {$type}\n";
21+
try {
22+
$num ** $exponent;
23+
} catch (Error $e) {
24+
echo $e->getMessage() . "\n";
25+
}
26+
}
27+
}
28+
?>
29+
--EXPECT--
30+
0 ** -3: int
31+
Negative power of zero
32+
0 ** -2: string
33+
Negative power of zero
34+
0 ** -2: object
35+
Negative power of zero
36+
0 ** -3: int
37+
Negative power of zero
38+
0 ** -2: string
39+
Negative power of zero
40+
0 ** -2: object
41+
Negative power of zero

ext/bcmath/tests/run_bcmath_tests_function.inc

+6-1
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,16 @@ function run_bcmath_tests(
1313
foreach ($firstTerms as $firstTerm) {
1414
echo "Number \"$firstTerm\" (scale $scale)\n";
1515
foreach ($secondTerms as $secondTerm) {
16+
try {
17+
$ret = $bcmath_function($firstTerm, $secondTerm, $scale);
18+
} catch (Throwable $e) {
19+
$ret = $e->getMessage();
20+
}
1621
echo $firstTerm,
1722
" $symbol ",
1823
str_pad($secondTerm, STRING_PADDING),
1924
" = ",
20-
$bcmath_function($firstTerm, $secondTerm, $scale),
25+
$ret,
2126
"\n";
2227
}
2328
echo "\n";

0 commit comments

Comments
 (0)