Skip to content

Commit 23afe57

Browse files
jorgsowailuuu1994
authored andcommitted
Added deprecation Division by zero when using power with zero as base and negative exponent
RFC: https://wiki.php.net/rfc/raising_zero_to_power_of_negative_number Closes phpGH-13128
1 parent 92b9543 commit 23afe57

14 files changed

+564
-15
lines changed

UPGRADING

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ PHP 8.4 UPGRADE NOTES
352352
- Standard:
353353
. Calling stream_context_set_option() with 2 arguments is deprecated.
354354
Use stream_context_set_options() instead.
355+
. Raising zero to the power of negative number is deprecated.
355356

356357
========================================
357358
5. Changed Functions
@@ -545,6 +546,7 @@ PHP 8.4 UPGRADE NOTES
545546
. Added the http_get_last_response_headers() and
546547
http_clear_last_response_headers() that allows retrieving the same content
547548
as the magic $http_response_header variable.
549+
. Added function fpow() following rules of IEEE 754.
548550

549551
- XSL:
550552
. Added XSLTProcessor::registerPhpFunctionNS().

Zend/tests/runtime_compile_time_binary_operands.phpt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ if (getenv("SKIP_SLOW_TESTS")) die('skip slow test');
88
?>
99
--FILE--
1010
<?php
11+
error_reporting(E_ALL ^ E_DEPRECATED);
1112

1213
$binaryOperators = [
1314
"==",
@@ -122,7 +123,7 @@ function prepareBinaryLine($op1, $op2, $cmp, $operator) {
122123
try {
123124
$result = makeParam($cmp());
124125
$line .= "if (" . ($result === "(NAN)" ? "!is_nan($compare)" : "$compare !== $result") . ") { $error }";
125-
} catch (Error $e) {
126+
} catch (Throwable $e) {
126127
$msg = makeParam($e->getMessage());
127128
$line .= "try { $compare; $error } catch (Error \$e) { if (\$e->getMessage() !== $msg) { $error } }";
128129
}
@@ -138,7 +139,7 @@ function prepareUnaryLine($op, $cmp, $operator) {
138139
try {
139140
$result = makeParam($cmp());
140141
$line .= "if (" . ($result === "(NAN)" ? "!is_nan($compare)" : "$compare !== $result") . ") { $error }";
141-
} catch (Error $e) {
142+
} catch (Throwable $e) {
142143
$msg = makeParam($e->getMessage());
143144
$line .= "try { $compare; $error } catch (Error \$e) { if (\$e->getMessage() !== $msg) { $error } }";
144145
}

Zend/zend_compile.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7726,7 +7726,7 @@ static zend_string *zend_begin_func_decl(znode *result, zend_op_array *op_array,
77267726
zend_string *separator = zend_empty_string;
77277727
zend_string *function = filename;
77287728
char *parens = "";
7729-
7729+
77307730
if (CG(active_op_array) && CG(active_op_array)->function_name) {
77317731
if (CG(active_op_array)->fn_flags & ZEND_ACC_CLOSURE) {
77327732
/* If the parent function is a closure, don't redundantly
@@ -8992,6 +8992,10 @@ ZEND_API bool zend_binary_op_produces_error(uint32_t opcode, const zval *op1, co
89928992
/* Division by zero throws an error. */
89938993
return 1;
89948994
}
8995+
if ((opcode == ZEND_POW) && zval_get_long(op1) == 0 && zval_get_double(op2) < 0) {
8996+
/* 0 ** (<0) throws a division by zero error. */
8997+
return 1;
8998+
}
89958999
if ((opcode == ZEND_SL || opcode == ZEND_SR) && zval_get_long(op2) < 0) {
89969000
/* Shift by negative number throws an error. */
89979001
return 1;

Zend/zend_operators.c

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,6 +1287,20 @@ ZEND_API zend_result ZEND_FASTCALL mul_function(zval *result, zval *op1, zval *o
12871287
}
12881288
/* }}} */
12891289

1290+
static void ZEND_COLD zend_power_base_0_exponent_lt_0_error(void)
1291+
{
1292+
zend_error(E_DEPRECATED, "Power of base 0 and negative exponent is deprecated");
1293+
}
1294+
1295+
static double safe_pow(double base, double exponent)
1296+
{
1297+
if (UNEXPECTED(base == 0.0 && exponent < 0.0)) {
1298+
zend_power_base_0_exponent_lt_0_error();
1299+
}
1300+
1301+
return pow(base, exponent);
1302+
}
1303+
12901304
static zend_result ZEND_FASTCALL pow_function_base(zval *result, zval *op1, zval *op2) /* {{{ */
12911305
{
12921306
uint8_t type_pair = TYPE_PAIR(Z_TYPE_P(op1), Z_TYPE_P(op2));
@@ -1311,32 +1325,32 @@ static zend_result ZEND_FASTCALL pow_function_base(zval *result, zval *op1, zval
13111325
--i;
13121326
ZEND_SIGNED_MULTIPLY_LONG(l1, l2, l1, dval, overflow);
13131327
if (overflow) {
1314-
ZVAL_DOUBLE(result, dval * pow(l2, i));
1328+
ZVAL_DOUBLE(result, dval * safe_pow(l2, i));
13151329
return SUCCESS;
13161330
}
13171331
} else {
13181332
i /= 2;
13191333
ZEND_SIGNED_MULTIPLY_LONG(l2, l2, l2, dval, overflow);
13201334
if (overflow) {
1321-
ZVAL_DOUBLE(result, (double)l1 * pow(dval, i));
1335+
ZVAL_DOUBLE(result, (double)l1 * safe_pow(dval, i));
13221336
return SUCCESS;
13231337
}
13241338
}
13251339
}
13261340
/* i == 0 */
13271341
ZVAL_LONG(result, l1);
13281342
} else {
1329-
ZVAL_DOUBLE(result, pow((double)Z_LVAL_P(op1), (double)Z_LVAL_P(op2)));
1343+
ZVAL_DOUBLE(result, safe_pow((double)Z_LVAL_P(op1), (double)Z_LVAL_P(op2)));
13301344
}
13311345
return SUCCESS;
13321346
} else if (EXPECTED(type_pair == TYPE_PAIR(IS_DOUBLE, IS_DOUBLE))) {
1333-
ZVAL_DOUBLE(result, pow(Z_DVAL_P(op1), Z_DVAL_P(op2)));
1347+
ZVAL_DOUBLE(result, safe_pow(Z_DVAL_P(op1), Z_DVAL_P(op2)));
13341348
return SUCCESS;
13351349
} else if (EXPECTED(type_pair == TYPE_PAIR(IS_LONG, IS_DOUBLE))) {
1336-
ZVAL_DOUBLE(result, pow((double)Z_LVAL_P(op1), Z_DVAL_P(op2)));
1350+
ZVAL_DOUBLE(result, safe_pow((double)Z_LVAL_P(op1), Z_DVAL_P(op2)));
13371351
return SUCCESS;
13381352
} else if (EXPECTED(type_pair == TYPE_PAIR(IS_DOUBLE, IS_LONG))) {
1339-
ZVAL_DOUBLE(result, pow(Z_DVAL_P(op1), (double)Z_LVAL_P(op2)));
1353+
ZVAL_DOUBLE(result, safe_pow(Z_DVAL_P(op1), (double)Z_LVAL_P(op2)));
13401354
return SUCCESS;
13411355
} else {
13421356
return FAILURE;

ext/standard/basic_functions.stub.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3274,6 +3274,11 @@ function fmod(float $num1, float $num2): float {}
32743274
*/
32753275
function fdiv(float $num1, float $num2): float {}
32763276

3277+
/**
3278+
* @compile-time-eval
3279+
*/
3280+
function fpow(float $num1, float $num2): float {}
3281+
32773282
/* microtime.c */
32783283

32793284
#ifdef HAVE_GETTIMEOFDAY

ext/standard/basic_functions_arginfo.h

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/standard/math.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,6 +1389,20 @@ PHP_FUNCTION(fdiv)
13891389
}
13901390
/* }}} */
13911391

1392+
/* {{{ Perform floating-point exponentiation with IEEE-754 semantics. */
1393+
PHP_FUNCTION(fpow)
1394+
{
1395+
double base, exponent;
1396+
1397+
ZEND_PARSE_PARAMETERS_START(2, 2)
1398+
Z_PARAM_DOUBLE(base)
1399+
Z_PARAM_DOUBLE(exponent)
1400+
ZEND_PARSE_PARAMETERS_END();
1401+
1402+
RETURN_DOUBLE(pow(base, exponent));
1403+
}
1404+
/* }}} */
1405+
13921406
/* {{{ Returns the integer quotient of the division of dividend by divisor */
13931407
PHP_FUNCTION(intdiv)
13941408
{

ext/standard/tests/math/bug45712.phpt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ var_dump($inf==='abc');
4242
var_dump($inf===$inf);
4343

4444
?>
45-
--EXPECT--
45+
--EXPECTF--
4646
float(NAN)
4747
bool(true)
4848
bool(false)
@@ -57,6 +57,8 @@ bool(false)
5757
bool(false)
5858
bool(false)
5959
bool(false)
60+
61+
Deprecated: Power of base 0 and negative exponent is deprecated in %s on line %d
6062
float(INF)
6163
bool(true)
6264
bool(false)

0 commit comments

Comments
 (0)