diff options
| -rw-r--r-- | Zend/tests/bug39018.phpt | 2 | ||||
| -rw-r--r-- | Zend/tests/dval_to_lval_32.phpt | 29 | ||||
| -rw-r--r-- | Zend/tests/dval_to_lval_64.phpt | 29 | ||||
| -rw-r--r-- | Zend/zend_operators.h | 26 |
4 files changed, 80 insertions, 6 deletions
diff --git a/Zend/tests/bug39018.phpt b/Zend/tests/bug39018.phpt index 32566ba864..a00e1fb819 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 0000000000..ddb16ccb82 --- /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 0000000000..da7f56d81c --- /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 93c60e4901..a3a432ff15 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 /* }}} */ |
