diff --git a/Zend/zend_strtod.c b/Zend/zend_strtod.c index 4b44a144292..3e7f90378ef 100644 --- a/Zend/zend_strtod.c +++ b/Zend/zend_strtod.c @@ -4507,6 +4507,105 @@ ZEND_API double zend_bin_strtod(const char *str, const char **endptr) return value; } +ZEND_API char *zend_gcvt(double value, int ndigit, char dec_point, char exponent, char *buf) +{ + char *digits, *dst, *src; + int i, decpt; + bool sign; + int mode = ndigit >= 0 ? 2 : 0; + + if (mode == 0) { + ndigit = 17; + } + digits = zend_dtoa(value, mode, ndigit, &decpt, &sign, NULL); + if (decpt == 9999) { + /* + * Infinity or NaN, convert to inf or nan with sign. + * We assume the buffer is at least ndigit long. + */ + snprintf(buf, ndigit + 1, "%s%s", (sign && *digits == 'I') ? "-" : "", *digits == 'I' ? "INF" : "NAN"); + zend_freedtoa(digits); + return (buf); + } + + dst = buf; + if (sign) { + *dst++ = '-'; + } + + if ((decpt >= 0 && decpt > ndigit) || decpt < -3) { /* use E-style */ + /* exponential format (e.g. 1.2345e+13) */ + if (--decpt < 0) { + sign = 1; + decpt = -decpt; + } else { + sign = 0; + } + src = digits; + *dst++ = *src++; + *dst++ = dec_point; + if (*src == '\0') { + *dst++ = '0'; + } else { + do { + *dst++ = *src++; + } while (*src != '\0'); + } + *dst++ = exponent; + if (sign) { + *dst++ = '-'; + } else { + *dst++ = '+'; + } + if (decpt < 10) { + *dst++ = '0' + decpt; + *dst = '\0'; + } else { + /* XXX - optimize */ + int n; + for (n = decpt, i = 0; (n /= 10) != 0; i++); + dst[i + 1] = '\0'; + while (decpt != 0) { + dst[i--] = '0' + decpt % 10; + decpt /= 10; + } + } + } else if (decpt < 0) { + /* standard format 0. */ + *dst++ = '0'; /* zero before decimal point */ + *dst++ = dec_point; + do { + *dst++ = '0'; + } while (++decpt < 0); + src = digits; + while (*src != '\0') { + *dst++ = *src++; + } + *dst = '\0'; + } else { + /* standard format */ + for (i = 0, src = digits; i < decpt; i++) { + if (*src != '\0') { + *dst++ = *src++; + } else { + *dst++ = '0'; + } + } + if (*src != '\0') { + if (src == digits) { + *dst++ = '0'; /* zero before decimal point */ + } + *dst++ = dec_point; + for (i = decpt; digits[i] != '\0'; i++) { + *dst++ = digits[i]; + } + } + *dst = '\0'; + } + zend_freedtoa(digits); + return (buf); +} + static void destroy_freelist(void) { int i; diff --git a/Zend/zend_strtod.h b/Zend/zend_strtod.h index 232a4d71e42..b049e56e370 100644 --- a/Zend/zend_strtod.h +++ b/Zend/zend_strtod.h @@ -24,7 +24,8 @@ BEGIN_EXTERN_C() ZEND_API void zend_freedtoa(char *s); -ZEND_API char * zend_dtoa(double _d, int mode, int ndigits, int *decpt, bool *sign, char **rve); +ZEND_API char *zend_dtoa(double _d, int mode, int ndigits, int *decpt, bool *sign, char **rve); +ZEND_API char *zend_gcvt(double value, int ndigit, char dec_point, char exponent, char *buf); ZEND_API double zend_strtod(const char *s00, const char **se); ZEND_API double zend_hex_strtod(const char *str, const char **endptr); ZEND_API double zend_oct_strtod(const char *str, const char **endptr); @@ -33,4 +34,12 @@ ZEND_API int zend_startup_strtod(void); ZEND_API int zend_shutdown_strtod(void); END_EXTERN_C() +/* double limits */ +#include +#if defined(DBL_MANT_DIG) && defined(DBL_MIN_EXP) +#define ZEND_DOUBLE_MAX_LENGTH (3 + DBL_MANT_DIG - DBL_MIN_EXP) +#else +#define ZEND_DOUBLE_MAX_LENGTH 1080 +#endif + #endif diff --git a/ext/json/json_encoder.c b/ext/json/json_encoder.c index 25f41d57f52..59f62b610d4 100644 --- a/ext/json/json_encoder.c +++ b/ext/json/json_encoder.c @@ -80,11 +80,11 @@ static inline int php_json_is_valid_double(double d) /* {{{ */ static inline void php_json_encode_double(smart_str *buf, double d, int options) /* {{{ */ { size_t len; - char num[PHP_DOUBLE_MAX_LENGTH]; + char num[ZEND_DOUBLE_MAX_LENGTH]; - php_gcvt(d, (int)PG(serialize_precision), '.', 'e', num); + zend_gcvt(d, (int)PG(serialize_precision), '.', 'e', num); len = strlen(num); - if (options & PHP_JSON_PRESERVE_ZERO_FRACTION && strchr(num, '.') == NULL && len < PHP_DOUBLE_MAX_LENGTH - 2) { + if (options & PHP_JSON_PRESERVE_ZERO_FRACTION && strchr(num, '.') == NULL && len < ZEND_DOUBLE_MAX_LENGTH - 2) { num[len++] = '.'; num[len++] = '0'; num[len] = '\0'; diff --git a/ext/mysqlnd/mysql_float_to_double.h b/ext/mysqlnd/mysql_float_to_double.h index 0a704b904a7..a15458b52de 100644 --- a/ext/mysqlnd/mysql_float_to_double.h +++ b/ext/mysqlnd/mysql_float_to_double.h @@ -38,7 +38,7 @@ static inline double mysql_float_to_double(float fp4, int decimals) { char num_buf[MAX_CHAR_BUF_LEN]; /* Over allocated */ if (decimals < 0) { - php_gcvt(fp4, FLT_DIG, '.', 'e', num_buf); + zend_gcvt(fp4, FLT_DIG, '.', 'e', num_buf); } else { snprintf(num_buf, MAX_CHAR_BUF_LEN, "%.*F", decimals, fp4); } diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index feb8e583c13..5890ef92897 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -1083,7 +1083,7 @@ static xmlNodePtr to_xml_double(encodeTypePtr type, zval *data, int style, xmlNo ZVAL_DOUBLE(&tmp, zval_get_double(data)); str = (char *) safe_emalloc(EG(precision) >= 0 ? EG(precision) : 17, 1, MAX_LENGTH_OF_DOUBLE + 1); - php_gcvt(Z_DVAL(tmp), EG(precision), '.', 'E', str); + zend_gcvt(Z_DVAL(tmp), EG(precision), '.', 'E', str); xmlNodeSetContentLen(ret, BAD_CAST(str), strlen(str)); efree(str); diff --git a/ext/standard/formatted_print.c b/ext/standard/formatted_print.c index 71c1bbcbd93..c1032ee3f6b 100644 --- a/ext/standard/formatted_print.c +++ b/ext/standard/formatted_print.c @@ -299,7 +299,7 @@ php_sprintf_appenddouble(zend_string **buffer, size_t *pos, char exp_char = fmt == 'G' || fmt == 'H' ? 'E' : 'e'; /* We use &num_buf[ 1 ], so that we have room for the sign. */ - s = php_gcvt(number, precision, decimal_point, exp_char, &num_buf[1]); + s = zend_gcvt(number, precision, decimal_point, exp_char, &num_buf[1]); is_negative = 0; if (*s == '-') { is_negative = 1; diff --git a/ext/standard/var.c b/ext/standard/var.c index 4d00ec8060e..24eac781a06 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -480,7 +480,7 @@ static void php_object_element_export(zval *zv, zend_ulong index, zend_string *k PHPAPI void php_var_export_ex(zval *struc, int level, smart_str *buf) /* {{{ */ { HashTable *myht; - char tmp_str[PHP_DOUBLE_MAX_LENGTH]; + char tmp_str[ZEND_DOUBLE_MAX_LENGTH]; zend_string *ztmp, *ztmp2; zend_ulong index; zend_string *key; @@ -508,7 +508,7 @@ again: smart_str_append_long(buf, Z_LVAL_P(struc)); break; case IS_DOUBLE: - php_gcvt(Z_DVAL_P(struc), (int)PG(serialize_precision), '.', 'E', tmp_str); + zend_gcvt(Z_DVAL_P(struc), (int)PG(serialize_precision), '.', 'E', tmp_str); smart_str_appends(buf, tmp_str); /* Without a decimal point, PHP treats a number literal as an int. * This check even works for scientific notation, because the @@ -1038,8 +1038,8 @@ again: return; case IS_DOUBLE: { - char tmp_str[PHP_DOUBLE_MAX_LENGTH]; - php_gcvt(Z_DVAL_P(struc), (int)PG(serialize_precision), '.', 'E', tmp_str); + char tmp_str[ZEND_DOUBLE_MAX_LENGTH]; + zend_gcvt(Z_DVAL_P(struc), (int)PG(serialize_precision), '.', 'E', tmp_str); size_t len = strlen(tmp_str); char *res = smart_str_extend(buf, 2 + len + 1); diff --git a/main/php.h b/main/php.h index ee95cdfe629..3b80a589c29 100644 --- a/main/php.h +++ b/main/php.h @@ -245,13 +245,7 @@ typedef unsigned int socklen_t; #define INT_MIN (- INT_MAX - 1) #endif -/* double limits */ -#include -#if defined(DBL_MANT_DIG) && defined(DBL_MIN_EXP) -#define PHP_DOUBLE_MAX_LENGTH (3 + DBL_MANT_DIG - DBL_MIN_EXP) -#else -#define PHP_DOUBLE_MAX_LENGTH 1080 -#endif +#define PHP_DOUBLE_MAX_LENGTH ZEND_DOUBLE_MAX_LENGTH #define PHP_GCC_VERSION ZEND_GCC_VERSION #define PHP_ATTRIBUTE_MALLOC ZEND_ATTRIBUTE_MALLOC diff --git a/main/snprintf.c b/main/snprintf.c index d86bffd44ef..62b73587c0e 100644 --- a/main/snprintf.c +++ b/main/snprintf.c @@ -128,106 +128,6 @@ static inline char *php_fcvt(double value, int ndigit, int *decpt, bool *sign) / } /* }}} */ -PHPAPI char *php_gcvt(double value, int ndigit, char dec_point, char exponent, char *buf) /* {{{ */ -{ - char *digits, *dst, *src; - int i, decpt; - bool sign; - int mode = ndigit >= 0 ? 2 : 0; - - if (mode == 0) { - ndigit = 17; - } - digits = zend_dtoa(value, mode, ndigit, &decpt, &sign, NULL); - if (decpt == 9999) { - /* - * Infinity or NaN, convert to inf or nan with sign. - * We assume the buffer is at least ndigit long. - */ - snprintf(buf, ndigit + 1, "%s%s", (sign && *digits == 'I') ? "-" : "", *digits == 'I' ? "INF" : "NAN"); - zend_freedtoa(digits); - return (buf); - } - - dst = buf; - if (sign) { - *dst++ = '-'; - } - - if ((decpt >= 0 && decpt > ndigit) || decpt < -3) { /* use E-style */ - /* exponential format (e.g. 1.2345e+13) */ - if (--decpt < 0) { - sign = 1; - decpt = -decpt; - } else { - sign = 0; - } - src = digits; - *dst++ = *src++; - *dst++ = dec_point; - if (*src == '\0') { - *dst++ = '0'; - } else { - do { - *dst++ = *src++; - } while (*src != '\0'); - } - *dst++ = exponent; - if (sign) { - *dst++ = '-'; - } else { - *dst++ = '+'; - } - if (decpt < 10) { - *dst++ = '0' + decpt; - *dst = '\0'; - } else { - /* XXX - optimize */ - int n; - for (n = decpt, i = 0; (n /= 10) != 0; i++); - dst[i + 1] = '\0'; - while (decpt != 0) { - dst[i--] = '0' + decpt % 10; - decpt /= 10; - } - } - } else if (decpt < 0) { - /* standard format 0. */ - *dst++ = '0'; /* zero before decimal point */ - *dst++ = dec_point; - do { - *dst++ = '0'; - } while (++decpt < 0); - src = digits; - while (*src != '\0') { - *dst++ = *src++; - } - *dst = '\0'; - } else { - /* standard format */ - for (i = 0, src = digits; i < decpt; i++) { - if (*src != '\0') { - *dst++ = *src++; - } else { - *dst++ = '0'; - } - } - if (*src != '\0') { - if (src == digits) { - *dst++ = '0'; /* zero before decimal point */ - } - *dst++ = dec_point; - for (i = decpt; digits[i] != '\0'; i++) { - *dst++ = digits[i]; - } - } - *dst = '\0'; - } - zend_freedtoa(digits); - return (buf); -} -/* }}} */ - /* {{{ Apache license */ /* ==================================================================== * Copyright (c) 1995-1998 The Apache Group. All rights reserved. @@ -1043,7 +943,7 @@ static size_t format_converter(buffy * odp, const char *fmt, va_list ap) /* {{{ lconv = localeconv(); } #endif - s = php_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]); + s = zend_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]); if (*s == '-') { prefix_char = *s++; } else if (print_sign) { diff --git a/main/snprintf.h b/main/snprintf.h index 1cb1a4e4e45..d4140c8cee1 100644 --- a/main/snprintf.h +++ b/main/snprintf.h @@ -75,13 +75,14 @@ PHPAPI int ap_php_snprintf(char *, size_t, const char *, ...) ZEND_ATTRIBUTE_FOR PHPAPI int ap_php_vsnprintf(char *, size_t, const char *, va_list ap); PHPAPI int ap_php_vasprintf(char **buf, const char *format, va_list ap); PHPAPI int ap_php_asprintf(char **buf, const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3); -PHPAPI char * php_gcvt(double value, int ndigit, char dec_point, char exponent, char *buf); PHPAPI char * php_0cvt(double value, int ndigit, char dec_point, char exponent, char *buf); PHPAPI char * php_conv_fp(char format, double num, bool add_dp, int precision, char dec_point, bool * is_negative, char *buf, size_t *len); END_EXTERN_C() +#define php_gcvt zend_gcvt + #ifdef slprintf #undef slprintf #endif @@ -138,7 +139,7 @@ PHPAPI char * ap_php_conv_p2(uint64_t num, int nbits, * another level (see NDIG in php_conv_fp()). * Applies to the formatting functions of both spprintf.c and snprintf.c, which * use equally sized buffers of MAX_BUF_SIZE = 512 to hold the result of the - * call to php_gcvt(). + * call to zend_gcvt(). * This should be reasonably smaller than MAX_BUF_SIZE (I think MAX_BUF_SIZE - 9 * should be enough, but let's give some more space) */ #define FORMAT_CONV_MAX_PRECISION 500 diff --git a/main/spprintf.c b/main/spprintf.c index 26d44a1b27b..deb57e93f3d 100644 --- a/main/spprintf.c +++ b/main/spprintf.c @@ -141,7 +141,7 @@ * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions * which can be at most max length of double */ -#define NUM_BUF_SIZE PHP_DOUBLE_MAX_LENGTH +#define NUM_BUF_SIZE ZEND_DOUBLE_MAX_LENGTH #define NUM(c) (c - '0') @@ -651,7 +651,7 @@ static void xbuf_format_converter(void *xbuf, bool is_char, const char *fmt, va_ lconv = localeconv(); } #endif - s = php_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]); + s = zend_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]); if (*s == '-') prefix_char = *s++; else if (print_sign)