please look at this: https://bugs.php.net/bug.php?id=64290

thanks

On Sun, Feb 24, 2013 at 12:23 AM, Gustavo André dos Santos Lopes
<cataphr...@php.net> wrote:
> Commit:    77566edbafb969e166239b3fbc929588c6630ee9
> Author:    Gustavo Lopes <glo...@nebm.ist.utl.pt>         Sun, 17 Feb 2013 
> 23:40:26 +0100
> Parents:   64a2a8a7536de781aac015e7392cb56308d8aed0
> Branches:  PHP-5.5 master
>
> Link:       
> http://git.php.net/?p=php-src.git;a=commitdiff;h=77566edbafb969e166239b3fbc929588c6630ee9
>
> Log:
> Fix zend_dval_to_lval outside 64bit integers range
>
> PHP should preserve the least significant bits when casting from double
> to long. Zend.m4 contains this:
>
> AC_DEFINE([ZEND_DVAL_TO_LVAL_CAST_OK], 1, [Define if double cast to long 
> preserves least significant bits])
>
> If ZEND_DVAL_TO_LVAL_CAST_OK is not defined, zend_operators.h had an
> inline implementation of zend_dval_to_lval() that would do a cast to an
> int64_t (when sizeof(long) == 4), then a cast to unsigned long and
> finally the cast to long.
>
> While this works well for doubles inside the range of values of the type
> used in the first cast (int64_t in the 32-bit version and unsigned long
> in the 64-bit version), if outside the range, it is undefined behavior
> that WILL give varying and not particularly useful results.
>
> This commit uses fmod() to first put the double in a range that can
> safely be cast to unsigned long and then casts this unsigned long to
> long. This last cast is implementation defined, but it's very likely
> that this gives the expected result (i.e. the internal 2's complement
> representation is unchanged) on all platforms that PHP supports. In any
> case, the previous implementationa already had this assumption.
>
> This alternative code path is indeed significantly slower than simply
> casting the double (almost an order of magnitude), but that should not
> matter because casting doubles with a very high absolute value is a
> rare event.
>
> Changed paths:
>   M  Zend/tests/bug39018.phpt
>   A  Zend/tests/dval_to_lval_32.phpt
>   A  Zend/tests/dval_to_lval_64.phpt
>   M  Zend/zend_operators.h
>
>
> Diff:
> diff --git a/Zend/tests/bug39018.phpt b/Zend/tests/bug39018.phpt
> index 32566ba..a00e1fb 100644
> --- a/Zend/tests/bug39018.phpt
> +++ b/Zend/tests/bug39018.phpt
> @@ -64,6 +64,8 @@ print "\nDone\n";
>  --EXPECTF--
>  Notice: String offset cast occurred in %s on line %d
>
> +Notice: Uninitialized string offset: %s in %s on line 6
> +
>  Notice: Uninitialized string offset: 0 in %s on line %d
>
>  Notice: Uninitialized string offset: 0 in %s on line %d
> diff --git a/Zend/tests/dval_to_lval_32.phpt b/Zend/tests/dval_to_lval_32.phpt
> new file mode 100644
> index 0000000..ddb16cc
> --- /dev/null
> +++ b/Zend/tests/dval_to_lval_32.phpt
> @@ -0,0 +1,29 @@
> +--TEST--
> +zend_dval_to_lval preserves low bits  (32 bit long)
> +--SKIPIF--
> +<?php
> +if (PHP_INT_SIZE != 4)
> +        die("skip for machines with 32-bit longs");
> +?>
> +--FILE--
> +<?php
> +       /* test doubles around -4e21 */
> +       $values = [
> +               -4000000000000001048576.,
> +               -4000000000000000524288.,
> +               -4000000000000000000000.,
> +               -3999999999999999475712.,
> +               -3999999999999998951424.,
> +       ];
> +
> +       foreach ($values as $v) {
> +               var_dump((int)$v);
> +       }
> +
> +?>
> +--EXPECT--
> +int(-2056257536)
> +int(-2055733248)
> +int(-2055208960)
> +int(-2054684672)
> +int(-2054160384)
> diff --git a/Zend/tests/dval_to_lval_64.phpt b/Zend/tests/dval_to_lval_64.phpt
> new file mode 100644
> index 0000000..da7f56d
> --- /dev/null
> +++ b/Zend/tests/dval_to_lval_64.phpt
> @@ -0,0 +1,29 @@
> +--TEST--
> +zend_dval_to_lval preserves low bits  (64 bit long)
> +--SKIPIF--
> +<?php
> +if (PHP_INT_SIZE != 8)
> +        die("skip for machines with 64-bit longs");
> +?>
> +--FILE--
> +<?php
> +       /* test doubles around -4e21 */
> +       $values = [
> +               -4000000000000001048576.,
> +               -4000000000000000524288.,
> +               -4000000000000000000000.,
> +               -3999999999999999475712.,
> +               -3999999999999998951424.,
> +       ];
> +
> +       foreach ($values as $v) {
> +               var_dump((int)$v);
> +       }
> +
> +?>
> +--EXPECT--
> +int(2943463994971652096)
> +int(2943463994972176384)
> +int(2943463994972700672)
> +int(2943463994973224960)
> +int(2943463994973749248)
> diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h
> index 93c60e4..a3a432f 100644
> --- a/Zend/zend_operators.h
> +++ b/Zend/zend_operators.h
> @@ -68,22 +68,36 @@ END_EXTERN_C()
>
>  #if ZEND_DVAL_TO_LVAL_CAST_OK
>  # define zend_dval_to_lval(d) ((long) (d))
> -#elif SIZEOF_LONG == 4 && defined(HAVE_ZEND_LONG64)
> +#elif SIZEOF_LONG == 4
>  static zend_always_inline long zend_dval_to_lval(double d)
>  {
>         if (d > LONG_MAX || d < LONG_MIN) {
> -               return (long)(unsigned long)(zend_long64) d;
> +               double  two_pow_32 = pow(2., 32.),
> +                               dmod;
> +
> +               dmod = fmod(d, two_pow_32);
> +               if (dmod < 0) {
> +                       dmod += two_pow_32;
> +               }
> +               return (long)(unsigned long)dmod;
>         }
> -       return (long) d;
> +       return (long)d;
>  }
>  #else
>  static zend_always_inline long zend_dval_to_lval(double d)
>  {
>         /* >= as (double)LONG_MAX is outside signed range */
> -       if (d >= LONG_MAX) {
> -               return (long)(unsigned long) d;
> +       if (d >= LONG_MAX || d < LONG_MIN) {
> +               double  two_pow_64 = pow(2., 64.),
> +                               dmod;
> +
> +               dmod = fmod(d, two_pow_64);
> +               if (dmod < 0) {
> +                       dmod += two_pow_64;
> +               }
> +               return (long)(unsigned long)dmod;
>         }
> -       return (long) d;
> +       return (long)d;
>  }
>  #endif
>  /* }}} */
>
>
> --
> PHP CVS Mailing List (http://www.php.net/)
> To unsubscribe, visit: http://www.php.net/unsub.php
>



-- 
Laruence  Xinchen Hui
http://www.laruence.com/

--
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to