php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #62240 5.4.3 regression, converting from float to int gives incorrect output
Submitted: 2012-06-06 08:28 UTC Modified: 2012-06-09 05:11 UTC
Votes:1
Avg. Score:5.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:1 (100.0%)
Same OS:1 (100.0%)
From: zuallauz at gmail dot com Assigned:
Status: Not a bug Package: *General Issues
PHP Version: 5.4.3 OS: Ubuntu 12.04 32-bit
Private report: No CVE-ID: None
 [2012-06-06 08:28 UTC] zuallauz at gmail dot com
Description:
------------
Ok this code correctly works on 5.3.3 but I have since upgraded to 5.4.3 and it now incorrectly casts the float to an integer.

It occurs when converting the output from the log() function to an integer. It should be a simple float to int conversion. However in the current stable PHP it outputs the wrong integer for no apparent reason.

I have compiled PHP 5.4.3 release with the following if that helps:

./configure --with-zlib --with-gd --with-jpeg-dir --with-png-dir --enable-gd-native-ttf --with-freetype-dir --enable-bcmath --enable-sockets --with-openssl --with-pdo-mysql=mysqlnd --with-mysql-sock=/var/run/mysqld/mysqld.sock --enable-mbstring --enable-mbregex --with-curl --with-apxs2=/usr/local/apache2/bin/apxs --with-config-file-path=/usr/local/apache2/conf

I consider this a critical bug so please fix ASAP.

Test script:
---------------
$logarithm = log(8, 2);
var_dump($logarithm);

$int = (int) $logarithm;
var_dump($int);

Expected result:
----------------
Expected output as it is in PHP 5.3.3:
float(3)
int(3)

Actual result:
--------------
Actual output in PHP 5.4.3:
float(3)
int(2)

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2012-06-06 08:41 UTC] rasmus@php.net
I am not able to reproduce this in PHP 5.4.4
 [2012-06-06 08:53 UTC] zuallauz at gmail dot com
Ok, so is 5.4.4 likely to be released this month? I will have to put a manual hack in my app until it is released I think.
 [2012-06-06 12:57 UTC] cataphract@php.net
-Status: Open +Status: Not a bug
 [2012-06-06 12:57 UTC] cataphract@php.net
The only change in log() between these two versions is the handling of the case 
where base == 1, as you can see with:

git diff php-5.3.3 php-5.4.3 -- ext/standard/math.c

log() is taken from math.h, so I guess the explanation here is that you were 
using a different libm.
 [2012-06-06 14:05 UTC] rasmus@php.net
Yes, I doubt upgrading to 5.4.4 is likely to fix your case. I have no idea why 
your libmath log() call isn't returning a more accurate value here. I tested this 
on a 64-bit Ubuntu 12.04 and couldn't reproduce it.
 [2012-06-07 07:25 UTC] zuallauz at gmail dot com
-Operating System: Ubuntu 12.04 +Operating System: Ubuntu 12.04 32-bit
 [2012-06-07 07:25 UTC] zuallauz at gmail dot com
I have put in a quick hack for now by converting from float to string first then converting to int. This seems to get the correct output:

$num = (int) (string) log(8, 2);
var_dump($num);
Output: int(3)

I don't think the problem lies in the log() function at all as that correctly retrieves the float(3) result. It's the conversion from the float to integer that is doing the incorrect calculation. Likely the log(8, 2) does not result in a perfect 3, it might actually be 2.9999999999999999 so there could be a conversion error happening.

My test machine is running Ubuntu server 32-bit, so maybe the 64-bit edition has enough precision to do the integer conversion properly. I was talking to a colleague today and he mentioned there were some float to integer optimisations done in 5.3.11 that changed things?
 [2012-06-07 07:50 UTC] zuallauz at gmail dot com
Actually I may just use the code below now that I've been reading up on the perils of converting from float to int in PHP:

$num = log(8, 2);
$num = (int) number_format($num, 0, '.', '');
 [2012-06-07 08:08 UTC] rasmus@php.net
Well, float to int conversion issues isn't a PHP-specific thing. Try this little 
C program on your machine, for example:


#include <stdio.h>
#include <math.h>
int main(char *argv[], int argc) {
	printf("%.64f\n",log(8)/log(2));
}

If you name the file, "a.c" you can just type: make a
then run it with: ./a

My output on 64-bit Ubuntu:
10:07am x220:~> ./a
3.0000000000000000000000000000000000000000000000000000000000000000

It would be interesting to see your output.
 [2012-06-07 08:48 UTC] zuallauz at gmail dot com
Hmm, I get the same output with 64 zeros:

3.0000000000000000000000000000000000000000000000000000000000000000
 [2012-06-07 09:14 UTC] rasmus@php.net
That's interesting because that is exactly what is happening when you call PHP's 
log() function. log(8,2) in PHP ends up being log(8)/log(2) in C. Did you compile 
your PHP yourself? If not, could you try grabbing the 5.4.3 tarball and doing a 
simple: ./configure && make
Building just the cli version is enough. Then run your log(8,2) test with 
sapi/cli/php test.php
 [2012-06-07 10:38 UTC] zuallauz at gmail dot com
Yeah originally I had compiled my own PHP using the flags in the first post. I re-downloaded php-5.4.3.tar.bz2 from PHP.net and just did a basic ./configure && make then ran log(8,2) using sapi/cli/php test.php and the output was still the same incorrect result:

float(3)
int(2)

However I have just tried the same thing on my other machine running 32bit Ubuntu 10.04 with 5.4.3 and it outputs correctly:

float(3)
int(3)

So maybe there's a screw loose/bad memory in the first machine or something. I don't have an explanation for it. Probably not a bug after all, sorry!
 [2012-06-07 14:08 UTC] rasmus@php.net
I doubt it is bad memory. What's the difference between the two machines? Same 
architecture? Intel vs. AMD perhaps? Different glibc versions? Different compiler 
versions? It would be interesting to know what would cause this on some machines 
but not others. What about the little test C program? Does that return the same 
result on both machines?
 [2012-06-08 09:16 UTC] zuallauz at gmail dot com
Yeah Memtest came back with no errors. The test C program returns the same result on both machines. Difference between the machines:

Working machine:
Intel Pentium 4 single core @2Ghz desktop
Ubuntu 10.04 32 bit
glibc 2.11.1
gcc 4.4

Not working machine:
Intel dual core T2300 @1.66Ghz laptop
Ubuntu 12.04 32 bit
glibc 2.15
gcc 4.6
 [2012-06-08 09:54 UTC] rasmus@php.net
Strange. The PHP log function just looks like this (from ext/standard/math.c):

PHP_FUNCTION(log)
{
        double num, base = 0;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|d", &num, &base) 
== FAILURE) {
                return;
        }
        if (ZEND_NUM_ARGS() == 1) {
                RETURN_DOUBLE(log(num));
        }
        if (base <= 0.0) {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "base must be 
greater than 0");
                RETURN_FALSE;
        }
        if (base == 1) {
                RETURN_DOUBLE(php_get_nan());
        } else {
                RETURN_DOUBLE(log(num) / log(base));
        }
}

Since you are calling it as log(8,2) you are hitting the last case there. So the 
only code executed is:

RETURN_DOUBLE(log(num) / log(base));

And the RETURN_DOUBLE macro just sets the return value to the double returned by 
dividing those two log calls. There should be no difference between the little 
test program and PHP here.

Although..  I think gcc might be playing tricks on us here because I used a 
constant. Try this instead:

#include <stdio.h>
#include <math.h>
int main(char *argv[], int argc) {
	double base = 2.0;
	double num = 8.0;
	printf("%.64f\n",log(num)/log(base));
}

Then compile it using:

gcc a.c -o a -lm

Do you get the same result on both machines?
 [2012-06-08 22:29 UTC] zuallauz at gmail dot com
I ran that new bit of C code:

First machine (that works properly) came back with:

3.0000000000000000000000000000000000000000000000000000000000000000

Second machine (not working machine) came back with:

2.9999999999999995559107901499373838305473327636718750000000000000

I think we found the problem! In PHP I think I read in the docs that 'When converting from float to integer, the number will be rounded towards zero.' so that must be why PHP returns a 2 on this machine.

Hmm so what's the best way to fix?
 [2012-06-09 05:11 UTC] rasmus@php.net
Well, you weren't rounding. When you do (int)2.9 that's truncation, not 
rounding. So your fix would be to round it. Floating point numbers can't always 
be represented accurately, so you have to account for that in your code. There 
is nothing we can do in PHP to fix this short of not using the built-in floating 
point feature, and if we go around that it will be really slow. You can choose 
to do that yourself by using bcmath/gmp yourself if you prefer, of course.

You will find that any other language you try on that machine will give you the 
same "incorrect" result.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue May 21 12:01:34 2024 UTC