php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #81627 floats < PHP_INT_MIN are converted to positive ints
Submitted: 2021-11-16 15:49 UTC Modified: 2021-11-22 15:20 UTC
From: shaohua dot li at inf dot ethz dot ch Assigned: cmb (profile)
Status: Not a bug Package: Scripting Engine problem
PHP Version: 8.1Git-2021-11-16 (Git) OS: Ubuntu 20.04.3 LTS
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: shaohua dot li at inf dot ethz dot ch
New email:
PHP Version: OS:

 

 [2021-11-16 15:49 UTC] shaohua dot li at inf dot ethz dot ch
Description:
------------
Hi there,

I compiled php-src twice with clang13 -O0 and -O2 (default). However, for the following code sample, the two `./sapi/cli/php` would evaluate it differently.
For "clang13 -O0" compiled one, "bug" would be printed. However, "clang13 -O2" wouldn't.

Test script:
---------------
<?php
function test() {
    $n = $a = 0;
    while($a <= 0) {
        $a &= $a-- + $a;
        if (++$n > 59) die("bug\n");
    }
}
test();
?>


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2021-11-16 23:24 UTC] requinix@php.net
-Status: Open +Status: Not a bug
 [2021-11-16 23:24 UTC] requinix@php.net
> $a &= $a-- + $a;

That statements has two modifications to $a: the -- and the &=. PHP does not guarantee in what order those will happen.

https://en.wikipedia.org/wiki/Sequence_point
 [2021-11-17 08:53 UTC] shaohua dot li at inf dot ethz dot ch
Hi,

Even if I decouple the two operations into two statements, the issue still exists. Also, for the robustness, correctness, and consistency of php, the outputs should be the same.

Test script:
----------------
<?php
function test() {
    $n = 0;
    $a = 0;
    while($a <= 0) {
        $a &= $a + $a;
        $a--;
        if (++$n > 59) die("bug\n");
    }
}
test();
?>
 [2021-11-17 10:32 UTC] requinix@php.net
-Summary: Incorrect result of php +Summary: Incorrect result of php bitwise and with floats using clang13 -O2 -Status: Not a bug +Status: Open
 [2021-11-17 10:32 UTC] requinix@php.net
I assume you do *not* get the float-to-int deprecation warning when $n=58? PHP doesn't support bitwise AND with floats and will round them to ints, but that comes with branch prediction so -O2 may be running afoul of that.

And that there is the limit of my knowledge on this matter.
 [2021-11-17 11:11 UTC] shaohua dot li at inf dot ethz dot ch
Yes, I noticed the warning. I'm just worried that shouldn't all compilers/optimizations emit consistent results even if it's an error?

I also tried gcc11 with -O0 and -O2, on which php emits the same results as clang13 -O2.
 [2021-11-17 11:32 UTC] cmb@php.net
-Package: *General Issues +Package: Scripting Engine problem
 [2021-11-17 11:32 UTC] cmb@php.net
Casting very small floats to int may change the sign:
<https://3v4l.org/Ku5YH>.  I don't think this is particularly
related to clang, nor to branch prediction, but the different
behavior might rather be related to whether
ZEND_DVAL_TO_LVAL_CAST_OK is defined or not[1].  If it is defined,
we cast to zend_long, and for double values outside the range of
zend_long, the behavior is undefined.

On Windows, where ZEND_DVAL_TO_LVAL_CAST_OK is never defined,
zend_dval_to_lval_slow()[2] yields an erroneous result anyway.

[1] <https://github.com/php/php-src/blob/php-7.4.26/Zend/Zend.m4#L158-L187>
[2] <https://github.com/php/php-src/blob/php-7.4.26/Zend/zend_operators.c#L3268-L3280>
 [2021-11-17 12:24 UTC] cmb@php.net
The 32bit implementation of zend_dval_to_lval_slow() has an
explicit comment[1] which says "we're going to make this number
positive".  I don't understand the reasoning, but apparently that
is a deliberate design decision.  Then again I don't understand
why we don't saturate[2].

[1] <https://github.com/php/php-src/blob/php-7.4.26/Zend/zend_operators.c#L3261-L3262>
[2] <https://github.com/php/php-src/commit/77566edbafb969e166239b3fbc929588c6630ee9>
 [2021-11-17 12:42 UTC] cmb@php.net
-Summary: Incorrect result of php bitwise and with floats using clang13 -O2 +Summary: floats < PHP_INT_MIN are converted to positive ints
 [2021-11-17 12:42 UTC] cmb@php.net
Well, should have (also) read the fine manual[1]:

| If the float is beyond the boundaries of int (usually +/-
| 2.15e+9 = 2^31 on 32-bit platforms and +/- 9.22e+18 = 2^63 on
| 64-bit platforms), the result is undefined, since the float
| doesn't have enough precision to give an exact int result.

According to that, the reported issue is not a bug.  On the other
hand, it would allow us to change the behavior to something more
reasonable.

[1] <https://www.php.net/manual/en/language.types.integer.php#language.types.integer.casting.from-float>
 [2021-11-19 01:08 UTC] antonino dot spampinato86 at gmail dot com
When you see "bug" on the screen it indicates an incorrect decrement as it only uses -1, run this code.
In the past I have read but I have lost the ticket, it could also be linked to something else (maybe optimization).

Never break int 60
function test() {
    $n = 0;
    $a = 0;
    $break = false;;
    while($a <= 0) {
        //if($a !== 0)
        //$a = $a - (-1);
        $a &= $a + ($a);
        $a--;
        if (++$n > 59) {
        $break = true;
        break;
        }
    }
return array($a, $n, $break);
}
var_dump(test());

Expected Result:
Deprecated: Implicit conversion from float -1.8446743800977043E+19 to int loses precision in /in/iBpbI on line 10
array(3) {
  [0]=>
  int(135292502015)
  [1]=>
  int(59)
  [2]=>
  bool(false)
}

Break int 60 and error decrement always -1
function test() {
    $n = 0;
    $a = 0;
    $break = false;;
    while($a <= 0) {
        if($a !== 0)
        $a = $a - (-1);
        $a &= $a + ($a);
        //$a--;
        if (++$n > 59) {
        $break = true;
        break;
        }
    }
return array($a, $n, $break);
}
var_dump(test());
Expected -1 Result:
array(3) {
  [0]=>
  int(0)
  [1]=>
  int(60)
  [2]=>
  bool(true)
}
if don't use manual decrement and the same output for $a-- is to equal Expected -1 Result this bug.
 [2021-11-22 15:20 UTC] cmb@php.net
-Status: Open +Status: Not a bug -Assigned To: +Assigned To: cmb
 [2021-11-22 15:20 UTC] cmb@php.net
As per my comment above[1], I'm closing this as not a bug, since
changing the behavior would require the RFC process[2], even
though the behavior is documented as being undefined.

[1] <https://bugs.php.net/bug.php?id=81627#1637152922>
[2] <https://wiki.php.net/rfc/howto>
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 16:01:28 2024 UTC