Skip to content

RFC: Raising zero to the power of negative number #13128

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
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
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ PHP NEWS
http_clear_last_response_headers() that allows retrieving the same content
as the magic $http_response_header variable.
. Add php_base64_encode_ex() API. (Remi)
. Implemented "Raising zero to the power of negative number" RFC (Jorg Sowa)

- XML:
. Added XML_OPTION_PARSE_HUGE parser option. (nielsdos)
Expand Down
2 changes: 2 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ PHP 8.4 UPGRADE NOTES
- Standard:
. Calling stream_context_set_option() with 2 arguments is deprecated.
Use stream_context_set_options() instead.
. Raising zero to the power of negative number is deprecated.

========================================
5. Changed Functions
Expand Down Expand Up @@ -524,6 +525,7 @@ PHP 8.4 UPGRADE NOTES
. Added the http_get_last_response_headers() and
http_clear_last_response_headers() that allows retrieving the same content
as the magic $http_response_header variable.
. Added function fpow() following rules of IEEE 754.

- XSL:
. Added XSLTProcessor::registerPhpFunctionNS().
Expand Down
5 changes: 3 additions & 2 deletions Zend/tests/runtime_compile_time_binary_operands.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ if (getenv("SKIP_SLOW_TESTS")) die('skip slow test');
?>
--FILE--
<?php
error_reporting(E_ALL ^ E_DEPRECATED);

$binaryOperators = [
"==",
Expand Down Expand Up @@ -122,7 +123,7 @@ function prepareBinaryLine($op1, $op2, $cmp, $operator) {
try {
$result = makeParam($cmp());
$line .= "if (" . ($result === "(NAN)" ? "!is_nan($compare)" : "$compare !== $result") . ") { $error }";
} catch (Error $e) {
} catch (Throwable $e) {
$msg = makeParam($e->getMessage());
$line .= "try { $compare; $error } catch (Error \$e) { if (\$e->getMessage() !== $msg) { $error } }";
}
Expand All @@ -138,7 +139,7 @@ function prepareUnaryLine($op, $cmp, $operator) {
try {
$result = makeParam($cmp());
$line .= "if (" . ($result === "(NAN)" ? "!is_nan($compare)" : "$compare !== $result") . ") { $error }";
} catch (Error $e) {
} catch (Throwable $e) {
$msg = makeParam($e->getMessage());
$line .= "try { $compare; $error } catch (Error \$e) { if (\$e->getMessage() !== $msg) { $error } }";
}
Expand Down
6 changes: 5 additions & 1 deletion Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -7714,7 +7714,7 @@ static zend_string *zend_begin_func_decl(znode *result, zend_op_array *op_array,
zend_string *separator = zend_empty_string;
zend_string *function = filename;
char *parens = "";

if (CG(active_op_array) && CG(active_op_array)->function_name) {
if (CG(active_op_array)->fn_flags & ZEND_ACC_CLOSURE) {
/* If the parent function is a closure, don't redundantly
Expand Down Expand Up @@ -8980,6 +8980,10 @@ ZEND_API bool zend_binary_op_produces_error(uint32_t opcode, const zval *op1, co
/* Division by zero throws an error. */
return 1;
}
if ((opcode == ZEND_POW) && zval_get_long(op1) == 0 && zval_get_long(op2) < 0) {
/* 0 ** (<0) throws a division by zero error. */
return 1;
}
if ((opcode == ZEND_SL || opcode == ZEND_SR) && zval_get_long(op2) < 0) {
/* Shift by negative number throws an error. */
return 1;
Expand Down
26 changes: 20 additions & 6 deletions Zend/zend_operators.c
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,20 @@ ZEND_API zend_result ZEND_FASTCALL mul_function(zval *result, zval *op1, zval *o
}
/* }}} */

static void ZEND_COLD zend_power_base_0_exponent_lt_0_error(void)
{
zend_error(E_DEPRECATED, "Power of base 0 and negative exponent is deprecated");
}

static double safe_pow(double base, double exponent)
{
if (UNEXPECTED(base == 0.0 && exponent < 0.0)) {
zend_power_base_0_exponent_lt_0_error();
}

return pow(base, exponent);
}

static zend_result ZEND_FASTCALL pow_function_base(zval *result, zval *op1, zval *op2) /* {{{ */
{
uint8_t type_pair = TYPE_PAIR(Z_TYPE_P(op1), Z_TYPE_P(op2));
Expand All @@ -1311,32 +1325,32 @@ static zend_result ZEND_FASTCALL pow_function_base(zval *result, zval *op1, zval
--i;
ZEND_SIGNED_MULTIPLY_LONG(l1, l2, l1, dval, overflow);
if (overflow) {
ZVAL_DOUBLE(result, dval * pow(l2, i));
ZVAL_DOUBLE(result, dval * safe_pow(l2, i));
return SUCCESS;
}
} else {
i /= 2;
ZEND_SIGNED_MULTIPLY_LONG(l2, l2, l2, dval, overflow);
if (overflow) {
ZVAL_DOUBLE(result, (double)l1 * pow(dval, i));
ZVAL_DOUBLE(result, (double)l1 * safe_pow(dval, i));
return SUCCESS;
}
}
}
/* i == 0 */
ZVAL_LONG(result, l1);
} else {
ZVAL_DOUBLE(result, pow((double)Z_LVAL_P(op1), (double)Z_LVAL_P(op2)));
ZVAL_DOUBLE(result, safe_pow((double)Z_LVAL_P(op1), (double)Z_LVAL_P(op2)));
}
return SUCCESS;
} else if (EXPECTED(type_pair == TYPE_PAIR(IS_DOUBLE, IS_DOUBLE))) {
ZVAL_DOUBLE(result, pow(Z_DVAL_P(op1), Z_DVAL_P(op2)));
ZVAL_DOUBLE(result, safe_pow(Z_DVAL_P(op1), Z_DVAL_P(op2)));
return SUCCESS;
} else if (EXPECTED(type_pair == TYPE_PAIR(IS_LONG, IS_DOUBLE))) {
ZVAL_DOUBLE(result, pow((double)Z_LVAL_P(op1), Z_DVAL_P(op2)));
ZVAL_DOUBLE(result, safe_pow((double)Z_LVAL_P(op1), Z_DVAL_P(op2)));
return SUCCESS;
} else if (EXPECTED(type_pair == TYPE_PAIR(IS_DOUBLE, IS_LONG))) {
ZVAL_DOUBLE(result, pow(Z_DVAL_P(op1), (double)Z_LVAL_P(op2)));
ZVAL_DOUBLE(result, safe_pow(Z_DVAL_P(op1), (double)Z_LVAL_P(op2)));
return SUCCESS;
} else {
return FAILURE;
Expand Down
5 changes: 5 additions & 0 deletions ext/standard/basic_functions.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -3274,6 +3274,11 @@ function fmod(float $num1, float $num2): float {}
*/
function fdiv(float $num1, float $num2): float {}

/**
* @compile-time-eval
*/
function fpow(float $num1, float $num2): float {}

/* microtime.c */

#ifdef HAVE_GETTIMEOFDAY
Expand Down
6 changes: 5 additions & 1 deletion ext/standard/basic_functions_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions ext/standard/math.c
Original file line number Diff line number Diff line change
Expand Up @@ -1389,6 +1389,20 @@ PHP_FUNCTION(fdiv)
}
/* }}} */

/* {{{ Perform floating-point exponentiation with IEEE-754 semantics. */
PHP_FUNCTION(fpow)
{
double base, exponent;

ZEND_PARSE_PARAMETERS_START(2, 2)
Z_PARAM_DOUBLE(base)
Z_PARAM_DOUBLE(exponent)
ZEND_PARSE_PARAMETERS_END();

RETURN_DOUBLE(pow(base, exponent));
}
/* }}} */

/* {{{ Returns the integer quotient of the division of dividend by divisor */
PHP_FUNCTION(intdiv)
{
Expand Down
4 changes: 3 additions & 1 deletion ext/standard/tests/math/bug45712.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ var_dump($inf==='abc');
var_dump($inf===$inf);

?>
--EXPECT--
--EXPECTF--
float(NAN)
bool(true)
bool(false)
Expand All @@ -57,6 +57,8 @@ bool(false)
bool(false)
bool(false)
bool(false)

Deprecated: Power of base 0 and negative exponent is deprecated in %s on line %d
float(INF)
bool(true)
bool(false)
Expand Down
Loading
Loading