Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:4462 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 62353 invoked by uid 1010); 13 Sep 2003 15:00:59 -0000 Delivered-To: ezmlm-scan-internals@lists.php.net Delivered-To: ezmlm-internals@lists.php.net Received: (qmail 62330 invoked by uid 1007); 13 Sep 2003 15:00:58 -0000 To: internals@lists.php.net Message-ID: <3F633153.5060907@php.net> Date: Sat, 13 Sep 2003 17:01:39 +0200 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.5b) Gecko/20030901 Thunderbird/0.2 X-Accept-Language: en-us, en MIME-Version: 1.0 CC: =?ISO-8859-1?Q?Marcus_B=F6rger?= Content-Type: multipart/mixed; boundary="------------080104000508040900060706" X-Posted-By: 80.126.21.70 Subject: [PATCH] Fix for 64-bit arch errors in zend_operators.c From: abies@php.net (Ard Biesheuvel) --------------080104000508040900060706 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit Hello group, Currently, the +, - en * operations in zend_operators in C do their calculcations on long arguments with doubles, and check if the result would fit in a long. This failed for me on FreeBSD/alpha (in some of the math tests), because doubles are not accurate enough to represent LONG_MIN or LONG_MAX. This causes the comparison '((double) LONG_MAX) + 1 > (double) LONG_MAX' to result in 'false' because they have the same double representation. Since a lot of tests use LONG_MAX in comparisons, this caused a lot of them to fail on this architecture. One solution could be to use long doubles, but in my opinion it is better to check the sign bit directly. A patch is attached. -- Ard --------------080104000508040900060706 Content-Type: text/plain; name="zend_operators.64bit.diff" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="zend_operators.64bit.diff" Index: zend_operators.c =================================================================== RCS file: /repository/ZendEngine2/zend_operators.c,v retrieving revision 1.160 diff -u -r1.160 zend_operators.c --- zend_operators.c 17 Aug 2003 19:11:48 -0000 1.160 +++ zend_operators.c 13 Sep 2003 13:15:30 -0000 @@ -33,6 +33,8 @@ #include "ext/bcmath/number.h" #endif +#define LONG_SIGN_MASK (1L << (8*SIZEOF_LONG-1)) + ZEND_API int zend_atoi(const char *str, int str_len) { int retval; @@ -664,13 +666,16 @@ if (op1->type == IS_LONG && op2->type == IS_LONG) { - double dval = (double) op1->value.lval + (double) op2->value.lval; + long lval = op1->value.lval + op2->value.lval; + + /* check for overflow by comparing sign bits */ + if ( (op1->value.lval & LONG_SIGN_MASK) == (op2->value.lval & LONG_SIGN_MASK) + && (op1->value.lval & LONG_SIGN_MASK) != (lval & LONG_SIGN_MASK)) { - if ((dval > (double) LONG_MAX) || (dval < (double) LONG_MIN)) { - result->value.dval = dval; + result->value.dval = (double) op1->value.lval + (double) op2->value.lval; result->type = IS_DOUBLE; } else { - result->value.lval = op1->value.lval + op2->value.lval; + result->value.lval = lval; result->type = IS_LONG; } return SUCCESS; @@ -701,13 +706,16 @@ zendi_convert_scalar_to_number(op2, op2_copy, result); if (op1->type == IS_LONG && op2->type == IS_LONG) { - double dval = (double) op1->value.lval - (double) op2->value.lval; + long lval = op1->value.lval - op2->value.lval; + + /* check for overflow by comparing sign bits */ + if ( (op1->value.lval & LONG_SIGN_MASK) != (op2->value.lval & LONG_SIGN_MASK) + && (op1->value.lval & LONG_SIGN_MASK) != (lval & LONG_SIGN_MASK)) { - if ((dval < (double) LONG_MIN) || (dval > (double) LONG_MAX)) { - result->value.dval = dval; + result->value.dval = (double) op1->value.lval - (double) op2->value.lval; result->type = IS_DOUBLE; } else { - result->value.lval = op1->value.lval - op2->value.lval; + result->value.lval = lval; result->type = IS_LONG; } return SUCCESS; @@ -738,13 +746,14 @@ zendi_convert_scalar_to_number(op2, op2_copy, result); if (op1->type == IS_LONG && op2->type == IS_LONG) { - double dval = (double) op1->value.lval * (double) op2->value.lval; - - if ((dval > (double) LONG_MAX) || (dval < (double) LONG_MIN)) { - result->value.dval = dval; + long lval = op1->value.lval * op2->value.lval; + + /* check for overflow by applying the reverse calculation */ + if (op1->value.lval != 0 && lval / op1->value.lval != op2->value.lval) { + result->value.dval = (double) op1->value.lval * (double) op2->value.lval; result->type = IS_DOUBLE; } else { - result->value.lval = op1->value.lval * op2->value.lval; + result->value.lval = lval; result->type = IS_LONG; } return SUCCESS; --------------080104000508040900060706--