Index: zend.c
===================================================================
RCS file: /repository/ZendEngine2/zend.c,v
retrieving revision 1.386
diff -u -r1.386 zend.c
--- zend.c	11 Dec 2006 21:48:07 -0000	1.386
+++ zend.c	12 Dec 2006 11:43:32 -0000
@@ -380,7 +380,7 @@
 			}
 			break;
 		case IS_RESOURCE:
-			Z_STRVAL_P(expr_copy) = (char *) emalloc(sizeof("Resource id #")-1 + MAX_LENGTH_OF_LONG);
+			Z_STRVAL_P(expr_copy) = (char *) emalloc(sizeof("Resource id #") + MAX_LENGTH_OF_LONG);
 			Z_STRLEN_P(expr_copy) = sprintf(Z_STRVAL_P(expr_copy), "Resource id #%ld", Z_LVAL_P(expr));
 			break;
 		case IS_ARRAY:
Index: zend_exceptions.c
===================================================================
RCS file: /repository/ZendEngine2/zend_exceptions.c,v
retrieving revision 1.111
diff -u -r1.111 zend_exceptions.c
--- zend_exceptions.c	19 Sep 2006 10:38:30 -0000	1.111
+++ zend_exceptions.c	12 Dec 2006 11:44:16 -0000
@@ -491,7 +491,7 @@
 			line = 0;
 		}
 		TRACE_APPEND_ZVAL(*file);
-		s_tmp = emalloc(MAX_LENGTH_OF_LONG + 2 + 1);
+		s_tmp = emalloc(MAX_LENGTH_OF_LONG + 4 + 1);
 		sprintf(s_tmp, "(%ld): ", line);
 		TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
 		efree(s_tmp);
Index: zend_language_scanner.l
===================================================================
RCS file: /repository/ZendEngine2/zend_language_scanner.l,v
retrieving revision 1.160
diff -u -r1.160 zend_language_scanner.l
--- zend_language_scanner.l	23 Nov 2006 22:04:32 -0000	1.160
+++ zend_language_scanner.l	18 Jan 2007 11:02:45 -0000
@@ -1861,38 +1861,43 @@
 
 
 <ST_IN_SCRIPTING>{LNUM} {
-	errno = 0;
-	Z_LVAL_P(zendlval) = strtol(yytext, NULL, 0);
-	if (errno == ERANGE) { /* overflow */
-		Z_DVAL_P(zendlval) = zend_strtod(yytext, NULL);
-		Z_TYPE_P(zendlval) = IS_DOUBLE;
-		return T_DNUMBER;
+	if (yyleng < MAX_LENGTH_OF_LONG - 1) { /* Won't overflow */
+		Z_LVAL_P(zendlval) = strtol(yytext, NULL, 0);
 	} else {
-		Z_TYPE_P(zendlval) = IS_LONG;
-		return T_LNUMBER;
-	}
-}
-
-<ST_IN_SCRIPTING>{HNUM} {
-	errno = 0;
-	Z_LVAL_P(zendlval) = strtoul(yytext, NULL, 16);
-	if (errno == ERANGE) { /* overflow */
-		/* not trying strtod - it returns trash on 0x-es */
-		Z_LVAL_P(zendlval) = LONG_MAX; /* maximal long */
-		zend_error(E_NOTICE,"Hex number is too big: %s", yytext);
-	} else {
-		if (Z_LVAL_P(zendlval) < 0) {
-			/* maintain consistency with the old way */
-			Z_DVAL_P(zendlval) = (unsigned long) Z_LVAL_P(zendlval);
+		errno = 0;
+		Z_LVAL_P(zendlval) = strtol(yytext, NULL, 0);
+		if (errno == ERANGE) { /* Overflow */
+			Z_DVAL_P(zendlval) = zend_strtod(yytext, NULL);
 			Z_TYPE_P(zendlval) = IS_DOUBLE;
 			return T_DNUMBER;
 		}
-		Z_TYPE_P(zendlval) = IS_LONG;
 	}
+
 	Z_TYPE_P(zendlval) = IS_LONG;
 	return T_LNUMBER;
 }
 
+<ST_IN_SCRIPTING>{HNUM} {
+	char *hex = yytext + 2; /* Skip "0x" */
+	int len = yyleng - 2;
+
+	/* Skip any leading 0s */
+	while (*hex == '0') {
+		hex++;
+		len--;
+	}
+
+	if (len < SIZEOF_LONG * 2 || (len == SIZEOF_LONG * 2 && *hex <= '7')) {
+		Z_LVAL_P(zendlval) = strtol(hex, NULL, 16);
+		Z_TYPE_P(zendlval) = IS_LONG;
+		return T_LNUMBER;
+	} else {
+		Z_DVAL_P(zendlval) = zend_hex_strtod(hex, NULL);
+		Z_TYPE_P(zendlval) = IS_DOUBLE;
+		return T_DNUMBER;
+	}
+}
+
 <ST_DOUBLE_QUOTES,ST_BACKQUOTE,ST_HEREDOC>{LNUM}|{HNUM} { /* treat numbers (almost) as strings inside encapsulated strings */
 	if (!zend_copy_scanner_string(zendlval, yytext, yyleng, CG(literal_type), SCNG(output_conv) TSRMLS_CC)) {
 		return 0;
Index: zend_operators.c
===================================================================
RCS file: /repository/ZendEngine2/zend_operators.c,v
retrieving revision 1.261
diff -u -r1.261 zend_operators.c
--- zend_operators.c	16 Dec 2006 00:21:41 -0000	1.261
+++ zend_operators.c	19 Dec 2006 11:10:48 -0000
@@ -126,14 +126,9 @@
 				char *strval;
 
 				strval = Z_STRVAL_P(op);
-				switch ((Z_TYPE_P(op)=is_numeric_string(strval, Z_STRLEN_P(op), &Z_LVAL_P(op), &Z_DVAL_P(op), 1))) {
-					case IS_DOUBLE:
-					case IS_LONG:
-						break;
-					default:
-						Z_LVAL_P(op) = strtol(Z_STRVAL_P(op), NULL, 10);
-						Z_TYPE_P(op) = IS_LONG;
-						break;
+				if ((Z_TYPE_P(op)=is_numeric_string(strval, Z_STRLEN_P(op), &Z_LVAL_P(op), &Z_DVAL_P(op), 1)) == 0) {
+					Z_LVAL_P(op) = 0;
+					Z_TYPE_P(op) = IS_LONG;
 				}
 				STR_FREE(strval);
 				break;
@@ -143,14 +138,9 @@
 				UChar *strval;
 
 				strval = Z_USTRVAL_P(op);
-				switch ((Z_TYPE_P(op)=is_numeric_unicode(strval, Z_USTRLEN_P(op), &Z_LVAL_P(op), &Z_DVAL_P(op), 1))) {
-					case IS_DOUBLE:
-					case IS_LONG:
-						break;
-					default:
-						Z_LVAL_P(op) = zend_u_strtol(Z_USTRVAL_P(op), NULL, 10);
-						Z_TYPE_P(op) = IS_LONG;
-						break;
+				if ((Z_TYPE_P(op)=is_numeric_unicode(strval, Z_USTRLEN_P(op), &Z_LVAL_P(op), &Z_DVAL_P(op), 1)) == 0) {
+					Z_LVAL_P(op) = 0;
+					Z_TYPE_P(op) = IS_LONG;
 				}
 				USTR_FREE(strval);
 				break;
@@ -184,28 +174,18 @@
 		switch (Z_TYPE_P(op)) {										\
 			case IS_STRING:											\
 				{													\
-					switch ((Z_TYPE(holder)=is_numeric_string(Z_STRVAL_P(op), Z_STRLEN_P(op), &Z_LVAL(holder), &Z_DVAL(holder), 1))) {	\
-						case IS_DOUBLE:										\
-						case IS_LONG:										\
-							break;											\
-						default:											\
-							Z_LVAL(holder) = strtol(Z_STRVAL_P(op), NULL, 10);	\
-							Z_TYPE(holder) = IS_LONG;						\
-							break;											\
-					}														\
-					(op) = &(holder);										\
-					break;													\
-				}															\
+					if ((Z_TYPE(holder)=is_numeric_string(Z_STRVAL_P(op), Z_STRLEN_P(op), &Z_LVAL(holder), &Z_DVAL(holder), 1)) == 0) {	\
+						Z_LVAL(holder) = 0;								\
+						Z_TYPE(holder) = IS_LONG;						\
+					}													\
+					(op) = &(holder);									\
+					break;												\
+				}														\
 			case IS_UNICODE: \
 				{ \
-					switch ((Z_TYPE(holder)=is_numeric_unicode(Z_USTRVAL_P(op), Z_USTRLEN_P(op), &Z_LVAL(holder), &Z_DVAL(holder), 1))) { \
-						case IS_DOUBLE: \
-						case IS_LONG: \
-							break; \
-						default: \
-							Z_LVAL(holder) = zend_u_strtol(Z_USTRVAL_P(op), NULL, 10); \
-							Z_TYPE(holder) = IS_LONG; \
-							break; \
+					if ((Z_TYPE(holder)=is_numeric_unicode(Z_USTRVAL_P(op), Z_USTRLEN_P(op), &Z_LVAL(holder), &Z_DVAL(holder), 1)) == 0) { \
+						Z_LVAL(holder) = 0; \
+						Z_TYPE(holder) = IS_LONG; \
 					} \
 					(op) = &(holder);										\
 					break;													\
@@ -976,7 +956,7 @@
 			TSRMLS_FETCH();
 
 			zend_list_delete(Z_LVAL_P(op));
-			Z_STRVAL_P(op) = (char *) emalloc(sizeof("Resource id #")-1 + MAX_LENGTH_OF_LONG);
+			Z_STRVAL_P(op) = (char *) emalloc(sizeof("Resource id #") + MAX_LENGTH_OF_LONG);
 			Z_STRLEN_P(op) = sprintf(Z_STRVAL_P(op), "Resource id #%ld", tmp);
 			break;
 		}
@@ -2638,19 +2618,23 @@
 		(ret2=is_numeric_string(Z_STRVAL_P(s2), Z_STRLEN_P(s2), &lval2, &dval2, 0))) {
 		if ((ret1==IS_DOUBLE) || (ret2==IS_DOUBLE)) {
 			if (ret1!=IS_DOUBLE) {
-				dval1 = zend_strtod(Z_STRVAL_P(s1), NULL);
+				dval1 = (double) lval1;
 			} else if (ret2!=IS_DOUBLE) {
-				dval2 = zend_strtod(Z_STRVAL_P(s2), NULL);
+				dval2 = (double) lval2;
+			} else if (dval1 == dval2 && !zend_finite(dval1)) {
+				/* Both values overflowed and have the same sign,
+				 * so a numeric comparison would be inaccurate */
+				goto string_cmp;
 			}
 			Z_DVAL_P(result) = dval1 - dval2;
 			Z_LVAL_P(result) = ZEND_NORMALIZE_BOOL(Z_DVAL_P(result));
 			Z_TYPE_P(result) = IS_LONG;
 		} else { /* they both have to be long's */
-			Z_LVAL_P(result) = lval1 - lval2;
-			Z_LVAL_P(result) = ZEND_NORMALIZE_BOOL(Z_LVAL_P(result));
+			Z_LVAL_P(result) = lval1 > lval2 ? 1 : (lval1 < lval2 ? -1 : 0);
 			Z_TYPE_P(result) = IS_LONG;
 		}
 	} else {
+string_cmp:
 		Z_LVAL_P(result) = zend_binary_zval_strcmp(s1, s2);
 		Z_LVAL_P(result) = ZEND_NORMALIZE_BOOL(Z_LVAL_P(result));
 		Z_TYPE_P(result) = IS_LONG;
@@ -2682,19 +2666,23 @@
 		(ret2=is_numeric_unicode(Z_USTRVAL_P(s2), Z_USTRLEN_P(s2), &lval2, &dval2, 0))) {
 		if ((ret1==IS_DOUBLE) || (ret2==IS_DOUBLE)) {
 			if (ret1!=IS_DOUBLE) {
-				dval1 = zend_u_strtod(Z_USTRVAL_P(s1), NULL);
+				dval1 = (double) lval1;
 			} else if (ret2!=IS_DOUBLE) {
-				dval2 = zend_u_strtod(Z_USTRVAL_P(s2), NULL);
+				dval2 = (double) lval2;
+			} else if (dval1 == dval2 && !zend_finite(dval1)) {
+				/* Both values overflowed and have the same sign,
+				 * so a numeric comparison would be inaccurate */
+				goto string_cmp;
 			}
 			Z_DVAL_P(result) = dval1 - dval2;
 			Z_LVAL_P(result) = ZEND_NORMALIZE_BOOL(Z_DVAL_P(result));
 			Z_TYPE_P(result) = IS_LONG;
 		} else { /* they both have to be long's */
-			Z_LVAL_P(result) = lval1 - lval2;
-			Z_LVAL_P(result) = ZEND_NORMALIZE_BOOL(Z_LVAL_P(result));
+			Z_LVAL_P(result) = lval1 > lval2 ? 1 : (lval1 < lval2 ? -1 : 0);
 			Z_TYPE_P(result) = IS_LONG;
 		}
 	} else {
+string_cmp:
 		Z_LVAL_P(result) = zend_u_binary_zval_strcmp(s1, s2);
 		Z_LVAL_P(result) = ZEND_NORMALIZE_BOOL(Z_LVAL_P(result));
 		Z_TYPE_P(result) = IS_LONG;
Index: zend_operators.h
===================================================================
RCS file: /repository/ZendEngine2/zend_operators.h,v
retrieving revision 1.115
diff -u -r1.115 zend_operators.h
--- zend_operators.h	16 Dec 2006 00:21:41 -0000	1.115
+++ zend_operators.h	19 Dec 2006 11:34:00 -0000
@@ -36,7 +36,16 @@
 #include "ext/bcmath/libbcmath/src/bcmath.h"
 #endif
 
+#if SIZEOF_LONG == 4
+#define MAX_LENGTH_OF_LONG 11
+static const char long_min_digits[] = "2147483648";
+#elif SIZEOF_LONG == 8
 #define MAX_LENGTH_OF_LONG 20
+static const char long_min_digits[] = "9223372036854775808";
+#else
+#error "Unknown SIZEOF_LONG"
+#endif
+
 #define MAX_LENGTH_OF_DOUBLE 32
 
 BEGIN_EXTERN_C()
@@ -68,138 +77,268 @@
 ZEND_API double zend_u_strtod(const UChar *nptr, UChar **endptr);
 END_EXTERN_C()
 
-static inline zend_uchar is_numeric_string(char *str, int length, long *lval, double *dval, int allow_errors)
+#define ZEND_IS_DIGIT(c)   ((c) >= '0'  && (c) <= '9')
+#define ZEND_U_IS_DIGIT(c) ((c) >= 0x30 && (c) <= 0x39)
+#define ZEND_IS_XDIGIT(c)   (((c) >= 'A'  && (c) <= 'F')  || ((c) >= 'a'  && (c) <= 'f'))
+#define ZEND_U_IS_XDIGIT(c) (((c) >= 0x41 && (c) <= 0x46) || ((c) >= 0x61 && (c) <= 0x66))
+
+/**
+ * Checks whether the string "str" with length "length" is numeric. The value
+ * of allow_errors determines whether it's required to be entirely numeric, or
+ * just its prefix. Leading whitespace is allowed.
+ *
+ * The function returns 0 if the string did not contain a valid number; IS_LONG
+ * if it contained a number that fits within the range of a long; or IS_DOUBLE
+ * if the number was out of long range or contained a decimal point/exponent.
+ * The number's value is returned into the respective pointer, *lval or *dval,
+ * if that pointer is not NULL.
+ */
+
+static inline zend_uchar is_numeric_string(const char *str, int length, long *lval, double *dval, int allow_errors)
 {
-	long local_lval;
+	const char *ptr;
+	int base = 10, digits = 0, dp_or_e = 0;
 	double local_dval;
-	char *end_ptr_long, *end_ptr_double;
-	int conv_base=10;
+	zend_uchar type;
 
 	if (!length) {
 		return 0;
 	}
 
-	/* handle hex numbers */
-	if (length>=2 && str[0]=='0' && (str[1]=='x' || str[1]=='X')) {
-		conv_base=16;
-	}
-	errno=0;
-	local_lval = strtol(str, &end_ptr_long, conv_base);
-	if (errno!=ERANGE) {
-		if (end_ptr_long == str+length) { /* integer string */
-			if (lval) {
-				*lval = local_lval;
-			}
-			return IS_LONG;
-		} else if (end_ptr_long == str && *end_ptr_long != '\0' && *str != '.' && *str != '-') { /* ignore partial string matches */
-			return 0;
+	/* Skip any whitespace
+	 * This is much faster than the isspace() function */
+	while (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r' || *str == '\v' || *str == '\f') {
+		str++;
+		length--;
+	}
+	ptr = str;
+
+	if (*ptr == '-' || *ptr == '+') {
+		ptr++;
+	}
+
+	if (ZEND_IS_DIGIT(*ptr)) {
+		/* Handle hex numbers
+		 * str is used instead of ptr to disallow signs and keep old behavior */
+		if (length > 2 && *str == '0' && (str[1] == 'x' || str[1] == 'X')) {
+			base = 16;
+			ptr += 2;
 		}
-	} else {
-		end_ptr_long=NULL;
-	}
 
-	if (conv_base==16) { /* hex string, under UNIX strtod() messes it up */
-		return 0;
-	}
+		/* Skip any leading 0s */
+		while (*ptr == '0') {
+			ptr++;
+		}
 
-	errno=0;
-	local_dval = zend_strtod(str, &end_ptr_double);
-	if (errno != ERANGE) {
-		if (end_ptr_double == str+length) { /* floating point string */
-			if (!zend_finite(local_dval)) {
-				/* "inf","nan" and maybe other weird ones */
-				return 0;
+		/* Count the number of digits. If a decimal point/exponent is found,
+		 * it's a double. Otherwise, if there's a dval or no need to check for
+		 * a full match, stop when there are too many digits for a long */
+		for (type = IS_LONG; !(digits >= MAX_LENGTH_OF_LONG && (dval || allow_errors == 1)); digits++, ptr++) {
+check_digits:
+			if (ZEND_IS_DIGIT(*ptr) || (base == 16 && ZEND_IS_XDIGIT(*ptr))) {
+				continue;
+			} else if (base == 10) {
+				if (*ptr == '.' && dp_or_e < 1) {
+					goto process_double;
+				} else if ((*ptr == 'e' || *ptr == 'E') && dp_or_e < 2) {
+					const char *e = ptr + 1;
+
+					if (*e == '-' || *e == '+') {
+						ptr = e++;
+					}
+					if (ZEND_IS_DIGIT(*e)) {
+						goto process_double;
+					}
+				}
 			}
 
+			break;
+		}
+
+		if (base == 10) {
+			if (digits >= MAX_LENGTH_OF_LONG) {
+				dp_or_e = -1;
+				goto process_double;
+			}
+		} else if (!(digits < SIZEOF_LONG * 2 || (digits == SIZEOF_LONG * 2 && ptr[-digits] <= '7'))) {
 			if (dval) {
-				*dval = local_dval;
+				local_dval = zend_hex_strtod(str, (char **)&ptr);
 			}
-			return IS_DOUBLE;
+			type = IS_DOUBLE;
+		}
+	} else if (*ptr == '.' && ZEND_IS_DIGIT(ptr[1])) {
+process_double:
+		type = IS_DOUBLE;
+
+		/* If there's a dval, do the conversion; else continue checking
+		 * the digits if we need to check for a full match */
+		if (dval) {
+			local_dval = zend_strtod(str, (char **)&ptr);
+		} else if (allow_errors != 1 && dp_or_e != -1) {
+			dp_or_e = (*ptr++ == '.') ? 1 : 2;
+			goto check_digits;
 		}
 	} else {
-		end_ptr_double=NULL;
-	}
-
-	if (!allow_errors) {
 		return 0;
 	}
-	if (allow_errors == -1) {
-		zend_error(E_NOTICE, "A non well formed numeric value encountered");
+
+	if (ptr != str + length) {
+		if (!allow_errors) {
+			return 0;
+		}
+		if (allow_errors == -1) {
+			zend_error(E_NOTICE, "A non well formed numeric value encountered");
+		}
 	}
 
-	if (end_ptr_double>end_ptr_long && dval) {
-		*dval = local_dval;
-		return IS_DOUBLE;
-	} else if (end_ptr_long && lval) {
-		*lval = local_lval;
+	if (type == IS_LONG) {
+		if (digits == MAX_LENGTH_OF_LONG - 1) {
+			int cmp = strcmp(&ptr[-digits], long_min_digits);
+
+			if (!(cmp < 0 || (cmp == 0 && *str == '-'))) {
+				if (dval) {
+					*dval = zend_strtod(str, NULL);
+				}
+
+				return IS_DOUBLE;
+			}
+		}
+
+		if (lval) {
+			*lval = strtol(str, NULL, base);
+		}
+
 		return IS_LONG;
+	} else {
+		if (dval) {
+			*dval = local_dval;
+		}
+
+		return IS_DOUBLE;
 	}
-	return 0;
 }
 
-static inline zend_uchar is_numeric_unicode(UChar *str, int length, long *lval, double *dval, zend_bool allow_errors)
+static inline zend_uchar is_numeric_unicode(const UChar *str, int length, long *lval, double *dval, int allow_errors)
 {
-	long local_lval;
+	const UChar *ptr;
+	int base = 10, digits = 0, dp_or_e = 0;
 	double local_dval;
-	UChar *end_ptr_long, *end_ptr_double;
-	int conv_base=10;
+	zend_uchar type;
 
 	if (!length) {
 		return 0;
 	}
 
-	/* handle hex numbers */
-	if (length>=2 && str[0]=='0' && (str[1]=='x' || str[1]=='X')) {
-		conv_base=16;
-	}
+	/* Skip any whitespace */
+	while (u_isspace(*str)) {
+		str++;
+		length--;
+	}
+	ptr = str;
+
+	if (*ptr == 0x2D /*'-'*/ || *ptr == 0x2B /*'+'*/) {
+		ptr++;
+	}
+
+	if (ZEND_U_IS_DIGIT(*ptr)) {
+		/* Handle hex numbers
+		 * str is used instead of ptr to disallow signs and keep old behavior */
+		if (length > 2 && *str == 0x30 /*'0'*/ && (str[1] == 0x78 /*'x'*/ || str[1] == 0x58 /*'X'*/)) {
+			base = 16;
+			ptr += 2;
+		}
+
+		/* Skip any leading 0s */
+		while (*ptr == 0x30 /*'0'*/) {
+			ptr++;
+		}
 
-	errno=0;
-	local_lval = zend_u_strtol(str, &end_ptr_long, conv_base);
-	if (errno != ERANGE) {
-		if (end_ptr_long == str+length) { /* integer string */
-			if (lval) {
-				*lval = local_lval;
+		/* Count the number of digits. If a decimal point/exponent is found,
+		 * it's a double. Otherwise, if there's a dval or no need to check for
+		 * a full match, stop when there are too many digits for a long */
+		for (type = IS_LONG; !(digits >= MAX_LENGTH_OF_LONG && (dval || allow_errors == 1)); digits++, ptr++) {
+check_digits:
+			if (ZEND_U_IS_DIGIT(*ptr) || (base == 16 && ZEND_U_IS_XDIGIT(*ptr))) {
+				continue;
+			} else if (base == 10) {
+				if (*ptr == 0x2E /*'.'*/ && dp_or_e < 1) {
+					goto process_double;
+				} else if ((*ptr == 0x65 /*'e'*/ || *ptr == 0x45 /*'E'*/) && dp_or_e < 2) {
+					const UChar *e = ptr + 1;
+
+					if (*e == 0x2D /*'-'*/ || *e == 0x2B /*'+'*/) {
+						ptr = e++;
+					}
+					if (ZEND_U_IS_DIGIT(*e)) {
+						goto process_double;
+					}
+				}
 			}
-			return IS_LONG;
-		} else if (end_ptr_long == str && *end_ptr_long != '\0' && *str != '.' && *str != '-') { /* ignore partial string matches */
-			return 0;
+
+			break;
+		}
+
+		if (base == 10) {
+			if (digits >= MAX_LENGTH_OF_LONG) {
+				dp_or_e = -1;
+				goto process_double;
+			}
+		} else if (!(digits < SIZEOF_LONG * 2 || (digits == SIZEOF_LONG * 2 && ptr[-digits] <= 0x37 /*'7'*/))) {
+			if (dval) {
+				local_dval = zend_u_hex_strtod(str, (UChar **)&ptr);
+			}
+			type = IS_DOUBLE;
+		}
+	} else if (*ptr == 0x2E /*'.'*/ && ZEND_U_IS_DIGIT(ptr[1])) {
+process_double:
+		type = IS_DOUBLE;
+
+		/* If there's a dval, do the conversion; else continue checking
+		 * the digits if we need to check for a full match */
+		if (dval) {
+			local_dval = zend_u_strtod(str, (UChar **)&ptr);
+		} else if (allow_errors != 1 && dp_or_e != -1) {
+			dp_or_e = (*ptr++ == 0x2E /*'.'*/) ? 1 : 2;
+			goto check_digits;
 		}
 	} else {
-		end_ptr_long = NULL;
+		return 0;
 	}
 
-	if (conv_base == 16) { /* hex string, under UNIX strtod() messes it up */
-		/* UTODO: keep compatibility with is_numeric_string() here? */
-		return 0;
+	if (ptr != str + length) {
+		if (!allow_errors) {
+			return 0;
+		}
+		if (allow_errors == -1) {
+			zend_error(E_NOTICE, "A non well formed numeric value encountered");
+		}
 	}
 
-	local_dval = zend_u_strtod(str, &end_ptr_double);
-	if (local_dval == 0 && end_ptr_double == str) {
-		end_ptr_double = NULL;
-	} else {
-		if (end_ptr_double == str+length) { /* floating point string */
-			if (!zend_finite(local_dval)) {
-				/* "inf","nan" and maybe other weird ones */
-				return 0;
-			}
+	if (type == IS_LONG) {
+		if (digits == MAX_LENGTH_OF_LONG - 1) {
+			int cmp = zend_cmp_unicode_and_literal(&ptr[-digits], digits, long_min_digits, digits);
+
+			if (!(cmp < 0 || (cmp == 0 && *str == 0x2D /*'-'*/))) {
+				if (dval) {
+					*dval = zend_u_strtod(str, NULL);
+				}
 
-			if (dval) {
-				*dval = local_dval;
+				return IS_DOUBLE;
 			}
-			return IS_DOUBLE;
 		}
-	}
 
-	if (allow_errors) {
-		if (end_ptr_double > end_ptr_long && dval) {
+		if (lval) {
+			*lval = zend_u_strtol(str, NULL, base);
+		}
+
+		return IS_LONG;
+	} else {
+		if (dval) {
 			*dval = local_dval;
-			return IS_DOUBLE;
-		} else if (end_ptr_long && lval) {
-			*lval = local_lval;
-			return IS_LONG;
 		}
+
+		return IS_DOUBLE;
 	}
-	return 0;
 }
 
 static inline UChar*
Index: zend_strtod.c
===================================================================
RCS file: /repository/ZendEngine2/zend_strtod.c,v
retrieving revision 1.27
diff -u -r1.27 zend_strtod.c
--- zend_strtod.c	7 Dec 2006 20:45:21 -0000	1.27
+++ zend_strtod.c	12 Dec 2006 11:40:00 -0000
@@ -2645,6 +2645,72 @@
 	return 0;
 }
 
+ZEND_API double zend_hex_strtod(const char *str, char **endptr)
+{
+	const char *s = str;
+	char c;
+	int any = 0;
+	double value = 0;
+
+	if (*s == '0' && (s[1] == 'x' || s[1] == 'X')) {
+		s += 2;
+	}
+
+	while ((c = *s++)) {
+		if (c >= '0' && c <= '9') {
+			c -= '0';
+		} else if (c >= 'A' && c <= 'F') {
+			c -= 'A' - 10;
+		} else if (c >= 'a' && c <= 'f') {
+			c -= 'a' - 10;
+		} else {
+			break;
+		}
+
+		any = 1;
+		value = value * 16 + c;
+	}
+
+	if (endptr != NULL) {
+		*endptr = (char *)(any ? s - 1 : str);
+	}
+
+	return value;
+}
+
+ZEND_API double zend_u_hex_strtod(const UChar *str, UChar **endptr)
+{
+	const UChar *s = str;
+	UChar c;
+	int any = 0;
+	double value = 0;
+
+	if (*s == 0x30 /*'0'*/ && (s[1] == 0x78 /*'x'*/ || s[1] == 0x58 /*'X'*/)) {
+		s += 2;
+	}
+
+	while ((c = *s++)) {
+		if (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) {
+			c -= 0x30 /*'0'*/;
+		} else if (c >= 0x41 /*'A'*/ && c <= 0x46 /*'F'*/) {
+			c -= 0x41 /*'A'*/ - 10;
+		} else if (c >= 0x61 /*'a'*/ && c <= 0x66 /*'f'*/) {
+			c -= 0x61 /*'a'*/ - 10;
+		} else {
+			break;
+		}
+
+		any = 1;
+		value = value * 16 + c;
+	}
+
+	if (endptr != NULL) {
+		*endptr = (UChar *)(any ? s - 1 : str);
+	}
+
+	return value;
+}
+
 /*
  * Local variables:
  * tab-width: 4
Index: zend_strtod.h
===================================================================
RCS file: /repository/ZendEngine2/zend_strtod.h,v
retrieving revision 1.6
diff -u -r1.6 zend_strtod.h
--- zend_strtod.h	6 Dec 2006 09:50:27 -0000	1.6
+++ zend_strtod.h	12 Dec 2006 11:38:44 -0000
@@ -29,6 +29,8 @@
 ZEND_API char * zend_dtoa(double _d, int mode, int ndigits, int *decpt, int *sign, char **rve);
 ZEND_API double zend_strtod(const char *s00, char **se);
 ZEND_API double zend_u_strtod(const UChar *nptr, UChar **endptr);
+ZEND_API double zend_hex_strtod(const char *str, char **endptr);
+ZEND_API double zend_u_hex_strtod(const UChar *str, UChar **endptr);
 ZEND_API int zend_startup_strtod(void);
 ZEND_API int zend_shutdown_strtod(void);
 END_EXTERN_C()
Index: ext/pcre/php_pcre.c
===================================================================
RCS file: /repository/php-src/ext/pcre/php_pcre.c,v
retrieving revision 1.209
diff -u -r1.209 php_pcre.c
--- ext/pcre/php_pcre.c	10 Oct 2006 12:43:34 -0000	1.209
+++ ext/pcre/php_pcre.c	12 Dec 2006 11:47:49 -0000
@@ -633,8 +633,7 @@
 		}
 		if (name_cnt > 0) {
 			int rc1, rc2;
-			long dummy_l;
-			double dummy_d;
+
 			rc1 = pcre_fullinfo(pce->re, extra, PCRE_INFO_NAMETABLE, &name_table);
 			rc2 = pcre_fullinfo(pce->re, extra, PCRE_INFO_NAMEENTRYSIZE, &name_size);
 			rc = rc2 ? rc2 : rc1;
@@ -648,7 +647,7 @@
 			while (ni++ < name_cnt) {
 				name_idx = 0xff * name_table[0] + name_table[1];
 				subpat_names[name_idx] = name_table + 2;
-				if (is_numeric_string(subpat_names[name_idx], strlen(subpat_names[name_idx]), &dummy_l, &dummy_d, 0) > 0) {
+				if (is_numeric_string(subpat_names[name_idx], strlen(subpat_names[name_idx]), NULL, NULL, 0) > 0) {
 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Numeric named subpatterns are not allowed");
 					efree(offsets);
 					efree(subpat_names);
Index: ext/standard/type.c
===================================================================
RCS file: /repository/php-src/ext/standard/type.c,v
retrieving revision 1.48
diff -u -r1.48 type.c
--- ext/standard/type.c	9 Oct 2006 02:55:38 -0000	1.48
+++ ext/standard/type.c	12 Dec 2006 11:46:15 -0000
@@ -391,7 +391,6 @@
 PHP_FUNCTION(is_numeric)
 {
 	zval **arg;
-	int result;
 
 	if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {
 		WRONG_PARAM_COUNT;
@@ -404,8 +403,7 @@
 			break;
 
 		case IS_STRING:
-			result = is_numeric_string(Z_STRVAL_PP(arg), Z_STRLEN_PP(arg), NULL, NULL, 0);
-			if (result == IS_LONG || result == IS_DOUBLE) {
+			if (is_numeric_string(Z_STRVAL_PP(arg), Z_STRLEN_PP(arg), NULL, NULL, 0)) {
 				RETURN_TRUE;
 			} else {
 				RETURN_FALSE;
@@ -413,8 +411,7 @@
 			break;
 
 		case IS_UNICODE:
-			result = is_numeric_unicode(Z_USTRVAL_PP(arg), Z_USTRLEN_PP(arg), NULL, NULL, 0);
-			if (result == IS_LONG || result == IS_DOUBLE) {
+			if (is_numeric_unicode(Z_USTRVAL_PP(arg), Z_USTRLEN_PP(arg), NULL, NULL, 0)) {
 				RETURN_TRUE;
 			} else {
 				RETURN_FALSE;
