Skip to content

Fixed GH-16236: Fixed a bug in BcMath\Number::pow() and bcpow() when raising negative powers of 0. #16694

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions ext/bcmath/bcmath.c
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,10 @@ PHP_FUNCTION(bcpow)
goto cleanup;
}

bc_raise(first, exponent, &result, scale);
if (!bc_raise(first, exponent, &result, scale)) {
zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Negative power of zero");
goto cleanup;
}

RETVAL_NEW_STR(bc_num2str_ex(result, scale));

Expand Down Expand Up @@ -1141,7 +1144,10 @@ static zend_result bcmath_number_pow_internal(
}
return FAILURE;
}
bc_raise(n1, exponent, ret, *scale);
if (!bc_raise(n1, exponent, ret, *scale)) {
zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Negative power of zero");
return FAILURE;
}
bc_rm_trailing_zeros(*ret);
if (scale_expand) {
size_t diff = *scale - (*ret)->n_scale;
Expand Down
2 changes: 1 addition & 1 deletion ext/bcmath/libbcmath/src/bcmath.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ typedef enum {

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

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

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

Expand Down
21 changes: 13 additions & 8 deletions ext/bcmath/libbcmath/src/raise.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ void bc_square_ex(bc_num n1, bc_num *result, size_t scale_min) {
*(result) = square_ex;
}

/* Raise NUM1 to the NUM2 power. The result is placed in RESULT.
Maximum exponent is LONG_MAX. If a NUM2 is not an integer,
/* Raise "base" to the "exponent" power. The result is placed in RESULT.
Maximum exponent is LONG_MAX. If a "exponent" is not an integer,
only the integer part is used. */
void bc_raise(bc_num num1, long exponent, bc_num *result, size_t scale) {
bool bc_raise(bc_num base, long exponent, bc_num *result, size_t scale) {
bc_num temp, power;
size_t rscale;
size_t pwrscale;
Expand All @@ -54,7 +54,7 @@ void bc_raise(bc_num num1, long exponent, bc_num *result, size_t scale) {
if (exponent == 0) {
bc_free_num (result);
*result = bc_copy_num(BCG(_one_));
return;
return true;
}

/* Other initializations. */
Expand All @@ -64,12 +64,12 @@ void bc_raise(bc_num num1, long exponent, bc_num *result, size_t scale) {
rscale = scale;
} else {
is_neg = false;
rscale = MIN (num1->n_scale * exponent, MAX(scale, num1->n_scale));
rscale = MIN (base->n_scale * exponent, MAX(scale, base->n_scale));
}

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

/* Assign the value. */
if (is_neg) {
bc_divide(BCG(_one_), temp, result, rscale);
if (bc_divide(BCG(_one_), temp, result, rscale) == false) {
bc_free_num (&temp);
bc_free_num (&power);
return false;
}
bc_free_num (&temp);
} else {
bc_free_num (result);
*result = temp;
(*result)->n_scale = MIN(scale, (*result)->n_scale);
}
bc_free_num (&power);
return true;
}

/* This is used internally by BCMath */
Expand Down
16 changes: 8 additions & 8 deletions ext/bcmath/tests/bcpow.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,17 @@ Number "-9" (scale 0)

Number "0" (scale 0)
0 ** 15 = 0
0 ** -15 = 0
0 ** -15 = Negative power of zero
0 ** 1 = 0
0 ** -9 = 0
0 ** -9 = Negative power of zero
0 ** 0 = 1
0 ** -0 = 1

Number "-0" (scale 0)
-0 ** 15 = 0
-0 ** -15 = 0
-0 ** -15 = Negative power of zero
-0 ** 1 = 0
-0 ** -9 = 0
-0 ** -9 = Negative power of zero
-0 ** 0 = 1
-0 ** -0 = 1

Expand Down Expand Up @@ -188,17 +188,17 @@ Number "-9" (scale 10)

Number "0" (scale 10)
0 ** 15 = 0.0000000000
0 ** -15 = 0.0000000000
0 ** -15 = Negative power of zero
0 ** 1 = 0.0000000000
0 ** -9 = 0.0000000000
0 ** -9 = Negative power of zero
0 ** 0 = 1.0000000000
0 ** -0 = 1.0000000000

Number "-0" (scale 10)
-0 ** 15 = 0.0000000000
-0 ** -15 = 0.0000000000
-0 ** -15 = Negative power of zero
-0 ** 1 = 0.0000000000
-0 ** -9 = 0.0000000000
-0 ** -9 = Negative power of zero
-0 ** 0 = 1.0000000000
-0 ** -0 = 1.0000000000

Expand Down
29 changes: 29 additions & 0 deletions ext/bcmath/tests/bcpow_div_by_zero.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
--TEST--
bcpow() negative power of zero
--EXTENSIONS--
bcmath
--INI--
bcmath.scale=0
--FILE--
<?php
$exponents = ["-15", "-1", "-9"];
$baseNumbers = ['0', '-0'];

foreach ($baseNumbers as $baseNumber) {
foreach ($exponents as $exponent) {
try {
echo bcpow($baseNumber, $exponent), "\n";
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
}
}

?>
--EXPECT--
Negative power of zero
Negative power of zero
Negative power of zero
Negative power of zero
Negative power of zero
Negative power of zero
41 changes: 41 additions & 0 deletions ext/bcmath/tests/number/methods/pow_div_by_zero.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
--TEST--
BcMath\Number pow(): negative power of zero
--EXTENSIONS--
bcmath
--FILE--
<?php

$values = [0, '0'];

$exponents = [
[-3, 'int'],
['-2', 'string'],
[new BcMath\Number('-2'), 'object'],
];

foreach ($values as $value) {
$num = new BcMath\Number($value);

foreach ($exponents as [$exponent, $type]) {
echo "{$value} ** {$exponent}: {$type}\n";
try {
$num->pow($exponent);
} catch (Error $e) {
echo $e->getMessage() . "\n";
}
}
}
?>
--EXPECT--
0 ** -3: int
Negative power of zero
0 ** -2: string
Negative power of zero
0 ** -2: object
Negative power of zero
0 ** -3: int
Negative power of zero
0 ** -2: string
Negative power of zero
0 ** -2: object
Negative power of zero
41 changes: 41 additions & 0 deletions ext/bcmath/tests/number/operators/pow_div_by_zero.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
--TEST--
BcMath\Number pow: negative power of zero by operator
--EXTENSIONS--
bcmath
--FILE--
<?php

$values = [0, '0'];

$exponents = [
[-3, 'int'],
['-2', 'string'],
[new BcMath\Number('-2'), 'object'],
];

foreach ($values as $value) {
$num = new BcMath\Number($value);

foreach ($exponents as [$exponent, $type]) {
echo "{$value} ** {$exponent}: {$type}\n";
try {
$num ** $exponent;
} catch (Error $e) {
echo $e->getMessage() . "\n";
}
}
}
?>
--EXPECT--
0 ** -3: int
Negative power of zero
0 ** -2: string
Negative power of zero
0 ** -2: object
Negative power of zero
0 ** -3: int
Negative power of zero
0 ** -2: string
Negative power of zero
0 ** -2: object
Negative power of zero
7 changes: 6 additions & 1 deletion ext/bcmath/tests/run_bcmath_tests_function.inc
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,16 @@ function run_bcmath_tests(
foreach ($firstTerms as $firstTerm) {
echo "Number \"$firstTerm\" (scale $scale)\n";
foreach ($secondTerms as $secondTerm) {
try {
$ret = $bcmath_function($firstTerm, $secondTerm, $scale);
} catch (Throwable $e) {
$ret = $e->getMessage();
}
echo $firstTerm,
" $symbol ",
str_pad($secondTerm, STRING_PADDING),
" = ",
$bcmath_function($firstTerm, $secondTerm, $scale),
$ret,
"\n";
}
echo "\n";
Expand Down