php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #73358 round floating-point array indices instead of truncating them
Submitted: 2016-10-20 15:49 UTC Modified: 2016-10-25 15:02 UTC
From: x-alexander93 at yandex dot ru Assigned:
Status: Open Package: Arrays related
PHP Version: 5.6.27 OS: Debian 8.6, Windows 7
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: x-alexander93 at yandex dot ru
New email:
PHP Version: OS:

 

 [2016-10-20 15:49 UTC] x-alexander93 at yandex dot ru
Description:
------------
Hi!
I've found a bug with (possible?) float to int conversion. Well, I don't know to which category it could be more closely related.
I found the same results on Debian 8.6, CentOS 7 (with the latest 5.6 version from remi repo) and on the old XAMPP install with PHP 5.5 under Windows 7.
No PEAR or PECL packets were installed. Everything from standard PHP library (like imagick etc).
Here is the result of test script execution:
http://pastebin.com/JWJby27F

Test script:
---------------
<?php
// from 246
$from = "8.2" * "30";
// to 342
$to = "11.4" * "30";
$a = [];
for ($i = $from; $i <= $to; $i++) {
    if ($i == 246) {
        echo "No output at all!";
    }
    if ($i == 250) {
        echo "Still no output...";
    }
    if ($i == 256) {
        echo "Still no output...";
    }
    if ($i == 257) {
        echo "It works for {$i} and further numbers";
    }
    var_dump($i);
    $a[$i] = "test" . $i;
}
var_dump($a);
// array index number doesn't match the assigned value number (despite $i is a float without a fractional part)
// also there is no element with index 256 in the $a array...

Expected result:
----------------
1)Equality operator should return true for all numbers within $from..$to range
2)Array indexes should not be skipped during assigment

Actual result:
--------------
1)Equality operator returns false on ($i == number) when number is lower than 257.
2)Array index number doesn't match the assigned $i value number
3)Index with number 256 was missed at all in the $a array

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-10-20 16:26 UTC] requinix@php.net
-Summary: Bug with float to int conversion +Summary: $array[245.999] truncates the index (245) instead of rounding it (246) -Package: *General Issues +Package: Arrays related
 [2016-10-20 16:26 UTC] requinix@php.net
Floating point values have a limited precision. Hence a value might 
not have the same string representation after any processing. That also
includes writing a floating point value in your script and directly 
printing it without any mathematical operations.

If you would like to know more about "floats" and what IEEE
754 is, read this:
http://www.floating-point-gui.de/

Thank you for your interest in PHP.


https://3v4l.org/lF8P9

8.2 * 30 is not 246 but a tiny bit less than that: 245.999999ish. This is typical of arithmetic with floating-point values and occurs in languages besides PHP.

In PHP, numbers with fractional parts are not supported as array indices. If you try to use them then they will be treated as integers - truncating the floating-point value to just the integral part. Thus $a[245.999999] is treated as $a[245] and that will appear in your array when you didn't expect it.

The "tiny bit less" remains true until 256 (255.999999....), after which the representation changes and either (a) it becomes integral or (b) it is slightly *more* than the expected value and the difference is beyond the precision=20 set in that example script. I'm guessing the former. This effectively skips the 256 index.

The moral is to round() calculated values to a reasonable precision as warranted by your application. Rounding $from and $too is the easiest option.

$from = round("8.2" * "30"); // expecting an integer so round to 0 decimal digits
$to = round("11.4" * "30");


With that said, there is an opportunity for PHP's behavior to change: round floating-point array indices instead of truncating them. Personally I think that's a good idea but I'll leave the decision to someone else.
 [2016-10-25 15:02 UTC] cmb@php.net
-Summary: $array[245.999] truncates the index (245) instead of rounding it (246) +Summary: round floating-point array indices instead of truncating them -Type: Bug +Type: Feature/Change Request
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Fri Oct 18 10:01:27 2019 UTC