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