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

Closes #16694
Fixes #16236
This commit is contained in:
Saki Takamachi 2024-11-20 00:16:03 +09:00
parent 60e4f48fca
commit 2c8662d6f9
No known key found for this signature in database
GPG key ID: 770426E17EBBB3DD
8 changed files with 147 additions and 20 deletions

View file

@ -615,7 +615,10 @@ PHP_FUNCTION(bcpow)
goto cleanup; 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)); RETVAL_NEW_STR(bc_num2str_ex(result, scale));
@ -1141,7 +1144,10 @@ static zend_result bcmath_number_pow_internal(
} }
return FAILURE; 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); bc_rm_trailing_zeros(*ret);
if (scale_expand) { if (scale_expand) {
size_t diff = *scale - (*ret)->n_scale; size_t diff = *scale - (*ret)->n_scale;

View file

@ -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); 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); void bc_raise_bc_exponent(bc_num base, bc_num exponent, bc_num *resul, size_t scale);

View file

@ -40,10 +40,10 @@ void bc_square_ex(bc_num n1, bc_num *result, size_t scale_min) {
*(result) = square_ex; *(result) = square_ex;
} }
/* Raise NUM1 to the NUM2 power. The result is placed in RESULT. /* Raise "base" to the "exponent" power. The result is placed in RESULT.
Maximum exponent is LONG_MAX. If a NUM2 is not an integer, Maximum exponent is LONG_MAX. If a "exponent" is not an integer,
only the integer part is used. */ 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; bc_num temp, power;
size_t rscale; size_t rscale;
size_t pwrscale; size_t pwrscale;
@ -54,7 +54,7 @@ void bc_raise(bc_num num1, long exponent, bc_num *result, size_t scale) {
if (exponent == 0) { if (exponent == 0) {
bc_free_num (result); bc_free_num (result);
*result = bc_copy_num(BCG(_one_)); *result = bc_copy_num(BCG(_one_));
return; return true;
} }
/* Other initializations. */ /* Other initializations. */
@ -64,12 +64,12 @@ void bc_raise(bc_num num1, long exponent, bc_num *result, size_t scale) {
rscale = scale; rscale = scale;
} else { } else {
is_neg = false; 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. */ /* Set initial value of temp. */
power = bc_copy_num(num1); power = bc_copy_num(base);
pwrscale = num1->n_scale; pwrscale = base->n_scale;
while ((exponent & 1) == 0) { while ((exponent & 1) == 0) {
pwrscale = 2 * pwrscale; pwrscale = 2 * pwrscale;
bc_square_ex(power, &power, pwrscale); bc_square_ex(power, &power, pwrscale);
@ -92,7 +92,11 @@ void bc_raise(bc_num num1, long exponent, bc_num *result, size_t scale) {
/* Assign the value. */ /* Assign the value. */
if (is_neg) { 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); bc_free_num (&temp);
} else { } else {
bc_free_num (result); bc_free_num (result);
@ -100,6 +104,7 @@ void bc_raise(bc_num num1, long exponent, bc_num *result, size_t scale) {
(*result)->n_scale = MIN(scale, (*result)->n_scale); (*result)->n_scale = MIN(scale, (*result)->n_scale);
} }
bc_free_num (&power); bc_free_num (&power);
return true;
} }
/* This is used internally by BCMath */ /* This is used internally by BCMath */

View file

@ -60,17 +60,17 @@ Number "-9" (scale 0)
Number "0" (scale 0) Number "0" (scale 0)
0 ** 15 = 0 0 ** 15 = 0
0 ** -15 = 0 0 ** -15 = Negative power of zero
0 ** 1 = 0 0 ** 1 = 0
0 ** -9 = 0 0 ** -9 = Negative power of zero
0 ** 0 = 1 0 ** 0 = 1
0 ** -0 = 1 0 ** -0 = 1
Number "-0" (scale 0) Number "-0" (scale 0)
-0 ** 15 = 0 -0 ** 15 = 0
-0 ** -15 = 0 -0 ** -15 = Negative power of zero
-0 ** 1 = 0 -0 ** 1 = 0
-0 ** -9 = 0 -0 ** -9 = Negative power of zero
-0 ** 0 = 1 -0 ** 0 = 1
-0 ** -0 = 1 -0 ** -0 = 1
@ -188,17 +188,17 @@ Number "-9" (scale 10)
Number "0" (scale 10) Number "0" (scale 10)
0 ** 15 = 0.0000000000 0 ** 15 = 0.0000000000
0 ** -15 = 0.0000000000 0 ** -15 = Negative power of zero
0 ** 1 = 0.0000000000 0 ** 1 = 0.0000000000
0 ** -9 = 0.0000000000 0 ** -9 = Negative power of zero
0 ** 0 = 1.0000000000 0 ** 0 = 1.0000000000
0 ** -0 = 1.0000000000 0 ** -0 = 1.0000000000
Number "-0" (scale 10) Number "-0" (scale 10)
-0 ** 15 = 0.0000000000 -0 ** 15 = 0.0000000000
-0 ** -15 = 0.0000000000 -0 ** -15 = Negative power of zero
-0 ** 1 = 0.0000000000 -0 ** 1 = 0.0000000000
-0 ** -9 = 0.0000000000 -0 ** -9 = Negative power of zero
-0 ** 0 = 1.0000000000 -0 ** 0 = 1.0000000000
-0 ** -0 = 1.0000000000 -0 ** -0 = 1.0000000000

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -13,11 +13,16 @@ function run_bcmath_tests(
foreach ($firstTerms as $firstTerm) { foreach ($firstTerms as $firstTerm) {
echo "Number \"$firstTerm\" (scale $scale)\n"; echo "Number \"$firstTerm\" (scale $scale)\n";
foreach ($secondTerms as $secondTerm) { foreach ($secondTerms as $secondTerm) {
try {
$ret = $bcmath_function($firstTerm, $secondTerm, $scale);
} catch (Throwable $e) {
$ret = $e->getMessage();
}
echo $firstTerm, echo $firstTerm,
" $symbol ", " $symbol ",
str_pad($secondTerm, STRING_PADDING), str_pad($secondTerm, STRING_PADDING),
" = ", " = ",
$bcmath_function($firstTerm, $secondTerm, $scale), $ret,
"\n"; "\n";
} }
echo "\n"; echo "\n";