Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 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
54 changes: 41 additions & 13 deletions ext/bcmath/tests/bcpow.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,35 @@ bcmath
bcmath.scale=0
--FILE--
<?php
require(__DIR__ . "/run_bcmath_tests_function.inc");
// slightly modified run_bcmath_tests_function.inc for bcpow testing
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to not copy&paste the code. Instead, modify run_bcmath_tests_function.inc to catch exceptions,and if there is one, just print the exception message instead of the result. This will reduce code duplication and would still test these cases. It also generalises nicer.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, fixed it in 2e44b1f

const STRING_PADDING = 30;
function run_bcmath_tests(
$firstTerms,
$secondTerms,
$symbol,
$bcmath_function
) {
$scales = [0, 10];
foreach ($scales as $scale) {
foreach ($firstTerms as $firstTerm) {
echo "Number \"$firstTerm\" (scale $scale)\n";
foreach ($secondTerms as $secondTerm) {
if (in_array($firstTerm, ['0', '-0'], true) && $secondTerm[0] === '-') {
$ret = 'skip negative power of zero';
} else {
$ret = $bcmath_function($firstTerm, $secondTerm, $scale);
}
echo $firstTerm,
" $symbol ",
str_pad($secondTerm, STRING_PADDING),
" = ",
$ret,
"\n";
}
echo "\n";
}
}
}

$exponents = ["15", "-15", "1", "-9", "0", "-0"];
$baseNumbers = array_merge($exponents, [
Expand Down Expand Up @@ -60,19 +88,19 @@ Number "-9" (scale 0)

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

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

Number "14.14" (scale 0)
14.14 ** 15 = 180609729388653367
Expand Down Expand Up @@ -188,19 +216,19 @@ Number "-9" (scale 10)

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

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

Number "14.14" (scale 10)
14.14 ** 15 = 180609729388653367.2586094856
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