# [PATCH] Fix for 64-bit arch errors in zend_operators.c

- Thread: https://externals.io/message/4462
- Original archive: https://news-web.php.net/php.internals/4462
- Started by Ard Biesheuvel on 2003-09-13T15:01:39+00:00
- Messages: 2
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #4462 — Ard Biesheuvel — 2003-09-13T15:01:39+00:00

URL: https://externals.io/message/4462#4462

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

---

## #4479 — Zeev Suraski — 2003-09-14T17:37:15+00:00

URL: https://externals.io/message/4462#4479 · In reply to: #4462

The patch looks good - I committed it.  Thanks!

Zeev

At 18:01 13/09/2003, Ard Biesheuvel wrote:
>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
>
>
>
>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;
