From 4789badd3a87e9fc243572f019a148322b912cb3 Mon Sep 17 00:00:00 2001 From: Antony Dovgal Date: Wed, 6 Dec 2006 09:50:28 +0000 Subject: [PATCH] use BSD licensed implementation of double-to-string utilities instead of LGPL one this patch also fixes thread safety issues in zend_strtod() --- Zend/zend.c | 2 + Zend/zend_strtod.c | 1533 ++++++++++++++++----- Zend/zend_strtod.h | 7 +- ext/standard/formatted_print.c | 240 +--- ext/standard/tests/serialize/003.phpt | 4 +- ext/standard/tests/strings/sprintf_f.phpt | 2 +- main/snprintf.c | 439 +++--- main/snprintf.h | 8 +- main/spprintf.c | 3 +- 9 files changed, 1443 insertions(+), 795 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index 23ac1e13af..3e919d38d0 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -994,6 +994,7 @@ int zend_startup(zend_utility_functions *utility_functions, char **extensions, i fpsetmask(0); #endif + zend_startup_strtod(); zend_startup_extensions_mechanism(); /* Set up utility functions and values */ @@ -1177,6 +1178,7 @@ void zend_shutdown(TSRMLS_D) zend_hash_destroy(GLOBAL_CONSTANTS_TABLE); free(GLOBAL_CONSTANTS_TABLE); + zend_shutdown_strtod(); #ifdef ZTS GLOBAL_FUNCTION_TABLE = NULL; diff --git a/Zend/zend_strtod.c b/Zend/zend_strtod.c index 6c2c3ba560..e7414da34c 100644 --- a/Zend/zend_strtod.c +++ b/Zend/zend_strtod.c @@ -18,13 +18,13 @@ ***************************************************************/ /* Please send bug reports to - David M. Gay - AT&T Bell Laboratories, Room 2C-463 - 600 Mountain Avenue - Murray Hill, NJ 07974-2070 - U.S.A. - dmg@research.att.com or research!dmg - */ + David M. Gay + AT&T Bell Laboratories, Room 2C-463 + 600 Mountain Avenue + Murray Hill, NJ 07974-2070 + U.S.A. + dmg@research.att.com or research!dmg + */ /* strtod for IEEE-, VAX-, and IBM-arithmetic machines. * @@ -92,6 +92,23 @@ #include #include #include +#include + +#ifdef ZTS +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LOCALE_H +#include +#endif #ifdef HAVE_SYS_TYPES_H #include @@ -127,10 +144,10 @@ typedef unsigned long int uint32_t; #if defined(__arm__) && !defined(__VFP_FP__) /* - * Although the CPU is little endian the FP has different - * byte and word endianness. The byte order is still little endian - * but the word order is big endian. - */ + * * Although the CPU is little endian the FP has different + * * byte and word endianness. The byte order is still little endian + * * but the word order is big endian. + * */ #define IEEE_BIG_ENDIAN #undef IEEE_LITTLE_ENDIAN #endif @@ -145,8 +162,8 @@ typedef unsigned long int uint32_t; #define IEEE_LITTLE_ENDIAN #endif -#define Long int32_t -#define ULong uint32_t +#define Long int32_t +#define ULong uint32_t #ifdef __cplusplus #include "malloc.h" @@ -210,6 +227,7 @@ extern void *MALLOC(size_t); #define DBL_MAX 1.7014118346046923e+38 #endif + #ifndef LONG_MAX #define LONG_MAX 2147483647 #endif @@ -220,9 +238,7 @@ extern void *MALLOC(size_t); #include "math.h" #endif -#ifdef __cplusplus -extern "C" { -#endif +BEGIN_EXTERN_C() #ifndef CONST #ifdef KR_headers @@ -239,15 +255,15 @@ extern "C" { #endif #if defined(IEEE_LITTLE_ENDIAN) + defined(IEEE_BIG_ENDIAN) + defined(VAX) + \ - defined(IBM) != 1 -Exactly one of IEEE_LITTLE_ENDIAN IEEE_BIG_ENDIAN, VAX, or -IBM should be defined. + defined(IBM) != 1 + Exactly one of IEEE_LITTLE_ENDIAN IEEE_BIG_ENDIAN, VAX, or + IBM should be defined. #endif -typedef union { - double d; - ULong ul[2]; -} _double; + typedef union { + double d; + ULong ul[2]; + } _double; #define value(x) ((x).d) #ifdef IEEE_LITTLE_ENDIAN #define word0(x) ((x).ul[1]) @@ -258,15 +274,15 @@ typedef union { #endif /* The following definition of Storeinc is appropriate for MIPS processors. - * An alternative that might be better on some machines is - * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff) - */ + * * An alternative that might be better on some machines is + * * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff) + * */ #if defined(IEEE_LITTLE_ENDIAN) + defined(VAX) + defined(__arm__) #define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \ -((unsigned short *)a)[0] = (unsigned short)c, a++) + ((unsigned short *)a)[0] = (unsigned short)c, a++) #else #define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \ -((unsigned short *)a)[1] = (unsigned short)c, a++) + ((unsigned short *)a)[1] = (unsigned short)c, a++) #endif /* #define P DBL_MANT_DIG */ @@ -315,7 +331,7 @@ typedef union { #define Bias 65 #define Exp_1 0x41000000 #define Exp_11 0x41000000 -#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ +#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ #define Frac_mask 0xffffff #define Frac_mask1 0xffffff #define Bletch 4 @@ -378,10 +394,10 @@ extern double rnd_prod(double, double), rnd_quot(double, double); #ifndef Just_16 /* When Pack_32 is not defined, we store 16 bits per 32-bit Long. - * This makes some inner loops simpler and sometimes saves work - * during multiplications, but it often seems to make things slightly - * slower. Hence the default is now to store 32 bits per Long. - */ + * * This makes some inner loops simpler and sometimes saves work + * * during multiplications, but it often seems to make things slightly + * * slower. Hence the default is now to store 32 bits per Long. + * */ #ifndef Pack_32 #define Pack_32 #endif @@ -389,65 +405,120 @@ extern double rnd_prod(double, double), rnd_quot(double, double); #define Kmax 15 - struct -Bigint { +struct Bigint { struct Bigint *next; int k, maxwds, sign, wds; ULong x[1]; - }; +}; - typedef struct Bigint Bigint; +typedef struct Bigint Bigint; - static Bigint *freelist[Kmax+1]; +static Bigint *freelist[Kmax+1]; +static void destroy_freelist(void); - static Bigint * -Balloc -#ifdef KR_headers - (k) int k; -#else - (int k) +#ifdef ZTS + +static MUTEX_T dtoa_mutex; +static MUTEX_T pow5mult_mutex; + +#define _THREAD_PRIVATE_MUTEX_LOCK(x) tsrm_mutex_lock(x); +#define _THREAD_PRIVATE_MUTEX_UNLOCK(x) tsrm_mutex_unlock(x); + +#else + +#define _THREAD_PRIVATE_MUTEX_LOCK(x) +#define _THREAD_PRIVATE_MUTEX_UNLOCK(x) + +#endif /* ZTS */ + +ZEND_API int zend_startup_strtod(void) /* {{{ */ +{ +#ifdef ZTS + dtoa_mutex = tsrm_mutex_alloc(); + pow5mult_mutex = tsrm_mutex_alloc(); +#endif + return 1; +} +/* }}} */ +ZEND_API int zend_shutdown_strtod(void) /* {{{ */ +{ +#ifdef ZTS + tsrm_mutex_free(dtoa_mutex); + dtoa_mutex = NULL; + + tsrm_mutex_free(pow5mult_mutex); + pow5mult_mutex = NULL; #endif + destroy_freelist(); + return 1; +} +/* }}} */ + +static Bigint * Balloc(int k) { int x; Bigint *rv; + _THREAD_PRIVATE_MUTEX_LOCK(dtoa_mutex); if ((rv = freelist[k])) { freelist[k] = rv->next; - } - else { + } else { x = 1 << k; rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(Long)); rv->k = k; rv->maxwds = x; - } + } + _THREAD_PRIVATE_MUTEX_UNLOCK(dtoa_mutex); rv->sign = rv->wds = 0; return rv; - } +} - static void -Bfree -#ifdef KR_headers - (v) Bigint *v; -#else - (Bigint *v) -#endif +static void Bfree(Bigint *v) { if (v) { + _THREAD_PRIVATE_MUTEX_LOCK(dtoa_mutex); v->next = freelist[v->k]; freelist[v->k] = v; - } + _THREAD_PRIVATE_MUTEX_UNLOCK(dtoa_mutex); } +} #define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ -y->wds*sizeof(Long) + 2*sizeof(int)) + y->wds*sizeof(Long) + 2*sizeof(int)) - static Bigint * -multadd -#ifdef KR_headers - (b, m, a) Bigint *b; int m, a; -#else - (Bigint *b, int m, int a) /* multiply by m and add a */ -#endif +/* return value is only used as a simple string, so mis-aligned parts + * inside the Bigint are not at risk on strict align architectures + */ +static char * rv_alloc(int i) { + int j, k, *r; + + j = sizeof(ULong); + for(k = 0; + sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= i; + j <<= 1) { + k++; + } + r = (int*)Balloc(k); + *r = k; + return (char *)(r+1); +} + + +static char * nrv_alloc(char *s, char **rve, int n) +{ + char *rv, *t; + + t = rv = rv_alloc(n); + while((*t = *s++) !=0) { + t++; + } + if (rve) { + *rve = t; + } + return rv; +} + +static Bigint * multadd(Bigint *b, int m, int a) /* multiply by m and add a */ { int i, wds; ULong *x, y; @@ -471,148 +542,95 @@ multadd a = (int)(y >> 16); *x++ = y & 0xffff; #endif - } - while(++i < wds); + } + while(++i < wds); if (a) { if (wds >= b->maxwds) { b1 = Balloc(b->k+1); Bcopy(b1, b); Bfree(b); b = b1; - } + } b->x[wds++] = a; b->wds = wds; - } - return b; } - - static Bigint * -s2b -#ifdef KR_headers - (s, nd0, nd, y9) CONST char *s; int nd0, nd; ULong y9; -#else - (CONST char *s, int nd0, int nd, ULong y9) -#endif -{ - Bigint *b; - int i, k; - Long x, y; - - x = (nd + 8) / 9; - for(k = 0, y = 1; x > y; y <<= 1, k++) ; -#ifdef Pack_32 - b = Balloc(k); - b->x[0] = y9; - b->wds = 1; -#else - b = Balloc(k+1); - b->x[0] = y9 & 0xffff; - b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; -#endif - - i = 9; - if (9 < nd0) { - s += 9; - do b = multadd(b, 10, *s++ - '0'); - while(++i < nd0); - s++; - } - else - s += 10; - for(; i < nd; i++) - b = multadd(b, 10, *s++ - '0'); return b; - } +} - static int -hi0bits -#ifdef KR_headers - (x) register ULong x; -#else - (register ULong x) -#endif +static int hi0bits(ULong x) { - register int k = 0; + int k = 0; if (!(x & 0xffff0000)) { k = 16; x <<= 16; - } + } if (!(x & 0xff000000)) { k += 8; x <<= 8; - } + } if (!(x & 0xf0000000)) { k += 4; x <<= 4; - } + } if (!(x & 0xc0000000)) { k += 2; x <<= 2; - } + } if (!(x & 0x80000000)) { k++; - if (!(x & 0x40000000)) + if (!(x & 0x40000000)) { return 32; } - return k; } + return k; +} - static int -lo0bits -#ifdef KR_headers - (y) ULong *y; -#else - (ULong *y) -#endif +static int lo0bits(ULong *y) { - register int k; - register ULong x = *y; + int k; + ULong x = *y; if (x & 7) { - if (x & 1) + if (x & 1) { return 0; + } if (x & 2) { *y = x >> 1; return 1; - } + } *y = x >> 2; return 2; - } + } k = 0; if (!(x & 0xffff)) { k = 16; x >>= 16; - } + } if (!(x & 0xff)) { k += 8; x >>= 8; - } + } if (!(x & 0xf)) { k += 4; x >>= 4; - } + } if (!(x & 0x3)) { k += 2; x >>= 2; - } + } if (!(x & 1)) { k++; x >>= 1; - if (!x & 1) + if (!(x & 1)) { return 32; } + } *y = x; return k; - } +} - static Bigint * -i2b -#ifdef KR_headers - (i) int i; -#else - (int i) -#endif +static Bigint * i2b(int i) { Bigint *b; @@ -620,15 +638,9 @@ i2b b->x[0] = i; b->wds = 1; return b; - } +} - static Bigint * -mult -#ifdef KR_headers - (a, b) Bigint *a, *b; -#else - (Bigint *a, Bigint *b) -#endif +static Bigint * mult(Bigint *a, Bigint *b) { Bigint *c; int k, wa, wb, wc; @@ -642,16 +654,18 @@ mult c = a; a = b; b = c; - } + } k = a->k; wa = a->wds; wb = b->wds; wc = wa + wb; - if (wc > a->maxwds) + if (wc > a->maxwds) { k++; + } c = Balloc(k); - for(x = c->x, xa = x + wc; x < xa; x++) + for(x = c->x, xa = x + wc; x < xa; x++) { *x = 0; + } xa = a->x; xae = xa + wa; xb = b->x; @@ -669,10 +683,10 @@ mult z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; carry = z2 >> 16; Storeinc(xc, z2, z); - } - while(x < xae); - *xc = carry; } + while(x < xae); + *xc = carry; + } if ((y = *xb >> 16)) { x = xa; xc = xc0; @@ -684,11 +698,11 @@ mult Storeinc(xc, z, z2); z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; carry = z2 >> 16; - } - while(x < xae); - *xc = z2; } + while(x < xae); + *xc = z2; } + } #else for(; xb < xbe; xc0++) { if (y = *xb++) { @@ -699,65 +713,97 @@ mult z = *x++ * y + *xc + carry; carry = z >> 16; *xc++ = z & 0xffff; - } - while(x < xae); - *xc = carry; } + while(x < xae); + *xc = carry; } + } #endif for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; c->wds = wc; return c; - } +} - static Bigint *p5s; +static Bigint * s2b (CONST char *s, int nd0, int nd, ULong y9) +{ + Bigint *b; + int i, k; + Long x, y; - static Bigint * -pow5mult -#ifdef KR_headers - (b, k) Bigint *b; int k; + x = (nd + 8) / 9; + for(k = 0, y = 1; x > y; y <<= 1, k++) ; +#ifdef Pack_32 + b = Balloc(k); + b->x[0] = y9; + b->wds = 1; #else - (Bigint *b, int k) + b = Balloc(k+1); + b->x[0] = y9 & 0xffff; + b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; #endif + + i = 9; + if (9 < nd0) { + s += 9; + do b = multadd(b, 10, *s++ - '0'); + while(++i < nd0); + s++; + } else { + s += 10; + } + for(; i < nd; i++) { + b = multadd(b, 10, *s++ - '0'); + } + return b; +} + + +static Bigint *p5s; + +static Bigint * pow5mult(Bigint *b, int k) { Bigint *b1, *p5, *p51; int i; static int p05[3] = { 5, 25, 125 }; - if ((i = k & 3)) + if ((i = k & 3)) { b = multadd(b, p05[i-1], 0); + } - if (!(k >>= 2)) + if (!(k >>= 2)) { return b; + } if (!(p5 = p5s)) { /* first time */ + _THREAD_PRIVATE_MUTEX_LOCK(pow5mult_mutex); p5 = p5s = i2b(625); p5->next = 0; - } + _THREAD_PRIVATE_MUTEX_UNLOCK(pow5mult_mutex); + } for(;;) { if (k & 1) { b1 = mult(b, p5); Bfree(b); b = b1; - } - if (!(k >>= 1)) + } + if (!(k >>= 1)) { break; + } if (!(p51 = p5->next)) { - p51 = p5->next = mult(p5,p5); - p51->next = 0; + _THREAD_PRIVATE_MUTEX_LOCK(pow5mult_mutex); + if (!(p51 = p5->next)) { + p51 = p5->next = mult(p5,p5); + p51->next = 0; } - p5 = p51; + _THREAD_PRIVATE_MUTEX_UNLOCK(pow5mult_mutex); } - return b; + p5 = p51; } + return b; +} - static Bigint * -lshift -#ifdef KR_headers - (b, k) Bigint *b; int k; -#else - (Bigint *b, int k) -#endif + +static Bigint *lshift(Bigint *b, int k) { int i, k1, n, n1; Bigint *b1; @@ -770,12 +816,14 @@ lshift #endif k1 = b->k; n1 = n + b->wds + 1; - for(i = b->maxwds; n1 > i; i <<= 1) + for(i = b->maxwds; n1 > i; i <<= 1) { k1++; + } b1 = Balloc(k1); x1 = b1->x; - for(i = 0; i < n; i++) + for(i = 0; i < n; i++) { *x1++ = 0; + } x = b->x; xe = x + b->wds; #ifdef Pack_32 @@ -785,11 +833,12 @@ lshift do { *x1++ = *x << k | z; z = *x++ >> k1; - } - while(x < xe); - if ((*x1 = z)) + } + while(x < xe); + if ((*x1 = z)) { ++n1; } + } #else if (k &= 0xf) { k1 = 16 - k; @@ -797,27 +846,22 @@ lshift do { *x1++ = *x << k & 0xffff | z; z = *x++ >> k1; - } - while(x < xe); - if (*x1 = z) + } + while(x < xe); + if (*x1 = z) { ++n1; } + } #endif else do *x1++ = *x++; - while(x < xe); + while(x < xe); b1->wds = n1 - 1; Bfree(b); return b1; - } +} - static int -cmp -#ifdef KR_headers - (a, b) Bigint *a, *b; -#else - (Bigint *a, Bigint *b) -#endif +static int cmp(Bigint *a, Bigint *b) { ULong *xa, *xa0, *xb, *xb0; int i, j; @@ -841,21 +885,16 @@ cmp return *xa < *xb ? -1 : 1; if (xa <= xa0) break; - } - return 0; } + return 0; +} - static Bigint * -diff -#ifdef KR_headers - (a, b) Bigint *a, *b; -#else - (Bigint *a, Bigint *b) -#endif + +static Bigint * diff(Bigint *a, Bigint *b) { Bigint *c; int i, wa, wb; - Long borrow, y; /* We need signed shifts here. */ + Long borrow, y; /* We need signed shifts here. */ ULong *xa, *xae, *xb, *xbe, *xc; #ifdef Pack_32 Long z; @@ -867,15 +906,15 @@ diff c->wds = 1; c->x[0] = 0; return c; - } + } if (i < 0) { c = a; a = b; b = c; i = 1; - } - else + } else { i = 0; + } c = Balloc(a->k); c->sign = i; wa = a->wds; @@ -895,8 +934,7 @@ diff borrow = z >> 16; Sign_Extend(borrow, z); Storeinc(xc, z, y); - } - while(xb < xbe); + } while(xb < xbe); while(xa < xae) { y = (*xa & 0xffff) + borrow; borrow = y >> 16; @@ -905,35 +943,29 @@ diff borrow = z >> 16; Sign_Extend(borrow, z); Storeinc(xc, z, y); - } + } #else do { y = *xa++ - *xb++ + borrow; borrow = y >> 16; Sign_Extend(borrow, y); *xc++ = y & 0xffff; - } - while(xb < xbe); + } while(xb < xbe); while(xa < xae) { y = *xa++ + borrow; borrow = y >> 16; Sign_Extend(borrow, y); *xc++ = y & 0xffff; - } + } #endif - while(!*--xc) + while(!*--xc) { wa--; + } c->wds = wa; return c; - } +} - static double -ulp -#ifdef KR_headers - (_x) double _x; -#else - (double _x) -#endif +static double ulp (double _x) { _double x; register Long L; @@ -950,29 +982,29 @@ ulp word0(a) = L; word1(a) = 0; #ifndef Sudden_Underflow - } + } else { L = -L >> Exp_shift; if (L < Exp_shift) { word0(a) = 0x80000 >> L; word1(a) = 0; - } + } else { word0(a) = 0; L -= Exp_shift; word1(a) = L >= 31 ? 1 : 1 << (31 - L); - } } + } #endif return value(a); - } +} - static double +static double b2d #ifdef KR_headers - (a, e) Bigint *a; int *e; +(a, e) Bigint *a; int *e; #else - (Bigint *a, int *e) +(Bigint *a, int *e) #endif { ULong *xa, *xa0, w, y, z; @@ -999,17 +1031,17 @@ b2d w = xa > xa0 ? *--xa : 0; d1 = y << ((32-Ebits) + k) | w >> (Ebits - k); goto ret_d; - } + } z = xa > xa0 ? *--xa : 0; if (k -= Ebits) { d0 = Exp_1 | y << k | z >> (32 - k); y = xa > xa0 ? *--xa : 0; d1 = z << k | y >> (32 - k); - } + } else { d0 = Exp_1 | y; d1 = z; - } + } #else if (k < Ebits + 16) { z = xa > xa0 ? *--xa : 0; @@ -1018,7 +1050,7 @@ b2d y = xa > xa0 ? *--xa : 0; d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k; goto ret_d; - } + } z = xa > xa0 ? *--xa : 0; w = xa > xa0 ? *--xa : 0; k -= Ebits + 16; @@ -1026,7 +1058,7 @@ b2d y = xa > xa0 ? *--xa : 0; d1 = w << k + 16 | y << k; #endif - ret_d: +ret_d: #ifdef VAX word0(d) = d0 >> 16 | d0 << 16; word1(d) = d1 >> 16 | d1 << 16; @@ -1035,15 +1067,10 @@ b2d #undef d1 #endif return value(d); - } +} - static Bigint * -d2b -#ifdef KR_headers - (_d, e, bits) double d; int *e, *bits; -#else - (double _d, int *e, int *bits) -#endif + +static Bigint * d2b(double _d, int *e, int *bits) { Bigint *b; int de, i, k; @@ -1070,7 +1097,7 @@ d2b x = b->x; z = d0 & Frac_mask; - d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ + d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ #ifdef Sudden_Underflow de = (int)(d0 >> Exp_shift); #ifndef IBM @@ -1083,14 +1110,13 @@ d2b #ifdef Pack_32 if ((y = d1)) { if ((k = lo0bits(&y))) { - x[0] = y | z << (32 - k); + x[0] = y | (z << (32 - k)); z >>= k; - } - else + } else { x[0] = y; - i = b->wds = (x[1] = z) ? 2 : 1; } - else { + i = b->wds = (x[1] = z) ? 2 : 1; + } else { #ifdef DEBUG if (!z) Bug("Zero passed to d2b"); @@ -1099,32 +1125,30 @@ d2b x[0] = z; i = b->wds = 1; k += 32; - } + } #else if (y = d1) { - if (k = lo0bits(&y)) + if (k = lo0bits(&y)) { if (k >= 16) { x[0] = y | z << 32 - k & 0xffff; x[1] = z >> k - 16 & 0xffff; x[2] = z >> k; i = 2; - } - else { + } else { x[0] = y & 0xffff; x[1] = y >> 16 | z << 16 - k & 0xffff; x[2] = z >> k & 0xffff; x[3] = z >> k+16; i = 3; - } - else { + } + } else { x[0] = y & 0xffff; x[1] = y >> 16; x[2] = z & 0xffff; x[3] = z >> 16; i = 3; - } } - else { + } else { #ifdef DEBUG if (!z) Bug("Zero passed to d2b"); @@ -1133,14 +1157,13 @@ d2b if (k >= 16) { x[0] = z; i = 0; - } - else { + } else { x[0] = z & 0xffff; x[1] = z >> 16; i = 1; - } - k += 32; } + k += 32; + } while(!x[i]) --i; b->wds = i + 1; @@ -1156,28 +1179,22 @@ d2b *bits = P - k; #endif #ifndef Sudden_Underflow - } - else { + } else { *e = de - Bias - (P-1) + 1 + k; #ifdef Pack_32 *bits = 32*i - hi0bits(x[i-1]); #else *bits = (i+2)*16 - hi0bits(x[i]); #endif - } + } #endif return b; - } +} #undef d0 #undef d1 - static double -ratio -#ifdef KR_headers - (a, b) Bigint *a, *b; -#else - (Bigint *a, Bigint *b) -#endif + +static double ratio (Bigint *a, Bigint *b) { _double da, db; int k, ka, kb; @@ -1192,35 +1209,35 @@ ratio #ifdef IBM if (k > 0) { word0(da) += (k >> 2)*Exp_msk1; - if (k &= 3) + if (k &= 3) { da *= 1 << k; } - else { + } else { k = -k; word0(db) += (k >> 2)*Exp_msk1; if (k &= 3) db *= 1 << k; - } + } #else - if (k > 0) + if (k > 0) { word0(da) += k*Exp_msk1; - else { + } else { k = -k; word0(db) += k*Exp_msk1; - } + } #endif return value(da) / value(db); - } +} static CONST double tens[] = { - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, - 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, - 1e20, 1e21, 1e22 + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22 #ifdef VAX , 1e23, 1e24 #endif - }; +}; #ifdef IEEE_Arith static CONST double bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; @@ -1238,16 +1255,768 @@ static CONST double tinytens[] = { 1e-16, 1e-32 }; #endif #endif -ZEND_API double -zend_strtod -#ifdef KR_headers - (s00, se) CONST char *s00; char **se; + +static int quorem(Bigint *b, Bigint *S) +{ + int n; + Long borrow, y; + ULong carry, q, ys; + ULong *bx, *bxe, *sx, *sxe; +#ifdef Pack_32 + Long z; + ULong si, zs; +#endif + + n = S->wds; +#ifdef DEBUG + /*debug*/ if (b->wds > n) + /*debug*/ Bug("oversize b in quorem"); +#endif + if (b->wds < n) + return 0; + sx = S->x; + sxe = sx + --n; + bx = b->x; + bxe = bx + n; + q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ +#ifdef DEBUG + /*debug*/ if (q > 9) + /*debug*/ Bug("oversized quotient in quorem"); +#endif + if (q) { + borrow = 0; + carry = 0; + do { +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) * q + carry; + zs = (si >> 16) * q + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + z = (*bx >> 16) - (zs & 0xffff) + borrow; + borrow = z >> 16; + Sign_Extend(borrow, z); + Storeinc(bx, z, y); +#else + ys = *sx++ * q + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + *bx++ = y & 0xffff; +#endif + } + while(sx <= sxe); + if (!*bxe) { + bx = b->x; + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + if (cmp(b, S) >= 0) { + q++; + borrow = 0; + carry = 0; + bx = b->x; + sx = S->x; + do { +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) + carry; + zs = (si >> 16) + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + z = (*bx >> 16) - (zs & 0xffff) + borrow; + borrow = z >> 16; + Sign_Extend(borrow, z); + Storeinc(bx, z, y); +#else + ys = *sx++ + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + *bx++ = y & 0xffff; +#endif + } + while(sx <= sxe); + bx = b->x; + bxe = bx + n; + if (!*bxe) { + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + return q; +} + +static void destroy_freelist(void) +{ + int i; + Bigint *tmp; + + _THREAD_PRIVATE_MUTEX_LOCK(dtoa_mutex); + for (i = 0; i <= Kmax; i++) { + Bigint **listp = &freelist[i]; + while ((tmp = *listp) != NULL) { + *listp = tmp->next; + free(tmp); + } + freelist[i] = NULL; + } + _THREAD_PRIVATE_MUTEX_UNLOCK(dtoa_mutex); + +} + + +ZEND_API void zend_freedtoa(char *s) +{ + Bigint *b = (Bigint *)((int *)s - 1); + b->maxwds = 1 << (b->k = *(int*)b); + Bfree(b); +} + +/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the Long + * calculation. + */ + +ZEND_API char * zend_dtoa(double _d, int mode, int ndigits, int *decpt, int *sign, char **rve) +{ + /* Arguments ndigits, decpt, sign are similar to those + of ecvt and fcvt; trailing zeros are suppressed from + the returned string. If not null, *rve is set to point + to the end of the return value. If d is +-Infinity or NaN, + then *decpt is set to 9999. + + mode: + 0 ==> shortest string that yields d when read in + and rounded to nearest. + 1 ==> like 0, but with Steele & White stopping rule; + e.g. with IEEE P754 arithmetic , mode 0 gives + 1e23 whereas mode 1 gives 9.999999999999999e22. + 2 ==> max(1,ndigits) significant digits. This gives a + return value similar to that of ecvt, except + that trailing zeros are suppressed. + 3 ==> through ndigits past the decimal point. This + gives a return value similar to that from fcvt, + except that trailing zeros are suppressed, and + ndigits can be negative. + 4-9 should give the same return values as 2-3, i.e., + 4 <= mode <= 9 ==> same return as mode + 2 + (mode & 1). These modes are mainly for + debugging; often they run slower but sometimes + faster than modes 2-3. + 4,5,8,9 ==> left-to-right digit generation. + 6-9 ==> don't try fast floating-point estimate + (if applicable). + + Values of mode other than 0-9 are treated as mode 0. + + Sufficient space is allocated to the return value + to hold the suppressed trailing zeros. + */ + + int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, + j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, + spec_case, try_quick; + Long L; +#ifndef Sudden_Underflow + int denorm; + ULong x; +#endif + Bigint *b, *b1, *delta, *mlo, *mhi, *S, *tmp; + double ds; + char *s, *s0; + _double d, d2, eps; + + value(d) = _d; + + if (word0(d) & Sign_bit) { + /* set sign for everything, including 0's and NaNs */ + *sign = 1; + word0(d) &= ~Sign_bit; /* clear sign bit */ + } + else + *sign = 0; + +#if defined(IEEE_Arith) + defined(VAX) +#ifdef IEEE_Arith + if ((word0(d) & Exp_mask) == Exp_mask) +#else + if (word0(d) == 0x8000) +#endif + { + /* Infinity or NaN */ + *decpt = 9999; +#ifdef IEEE_Arith + if (!word1(d) && !(word0(d) & 0xfffff)) + return nrv_alloc("Infinity", rve, 8); +#endif + return nrv_alloc("NaN", rve, 3); + } +#endif +#ifdef IBM + value(d) += 0; /* normalize */ +#endif + if (!value(d)) { + *decpt = 1; + return nrv_alloc("0", rve, 1); + } + + b = d2b(value(d), &be, &bbits); +#ifdef Sudden_Underflow + i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); +#else + if ((i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)))) { +#endif + value(d2) = value(d); + word0(d2) &= Frac_mask1; + word0(d2) |= Exp_11; +#ifdef IBM + if (j = 11 - hi0bits(word0(d2) & Frac_mask)) + value(d2) /= 1 << j; +#endif + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) + * + * This suggests computing an approximation k to log10(d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + + i -= Bias; +#ifdef IBM + i <<= 2; + i += j; +#endif +#ifndef Sudden_Underflow + denorm = 0; + } + else { + /* d is denormalized */ + + i = bbits + be + (Bias + (P-1) - 1); + x = i > 32 ? (word0(d) << (64 - i)) | (word1(d) >> (i - 32)) + : (word1(d) << (32 - i)); + value(d2) = x; + word0(d2) -= 31*Exp_msk1; /* adjust exponent */ + i -= (Bias + (P-1) - 1) + 1; + denorm = 1; + } +#endif + ds = (value(d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; + k = (int)ds; + if (ds < 0. && ds != k) + k--; /* want k = floor(ds) */ + k_check = 1; + if (k >= 0 && k <= Ten_pmax) { + if (value(d) < tens[k]) + k--; + k_check = 0; + } + j = bbits - i - 1; + if (j >= 0) { + b2 = 0; + s2 = j; + } + else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } + else { + b2 -= k; + b5 = -k; + s5 = 0; + } + if (mode < 0 || mode > 9) + mode = 0; + try_quick = 1; + if (mode > 5) { + mode -= 4; + try_quick = 0; + } + leftright = 1; + switch(mode) { + case 0: + case 1: + ilim = ilim1 = -1; + i = 18; + ndigits = 0; + break; + case 2: + leftright = 0; + /* no break */ + case 4: + if (ndigits <= 0) + ndigits = 1; + ilim = ilim1 = i = ndigits; + break; + case 3: + leftright = 0; + /* no break */ + case 5: + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) + i = 1; + } + s = s0 = rv_alloc(i); + + if (ilim >= 0 && ilim <= Quick_max && try_quick) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + value(d2) = value(d); + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + value(d) /= bigtens[n_bigtens-1]; + ieps++; + } + for(; j; j >>= 1, i++) + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + value(d) /= ds; + } + else if ((j1 = -k)) { + value(d) *= tens[j1 & 0xf]; + for(j = j1 >> 4; j; j >>= 1, i++) + if (j & 1) { + ieps++; + value(d) *= bigtens[i]; + } + } + if (k_check && value(d) < 1. && ilim > 0) { + if (ilim1 <= 0) + goto fast_failed; + ilim = ilim1; + k--; + value(d) *= 10.; + ieps++; + } + value(eps) = ieps*value(d) + 7.; + word0(eps) -= (P-1)*Exp_msk1; + if (ilim == 0) { + S = mhi = 0; + value(d) -= 5.; + if (value(d) > value(eps)) + goto one_digit; + if (value(d) < -value(eps)) + goto no_digits; + goto fast_failed; + } +#ifndef No_leftright + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + value(eps) = 0.5/tens[ilim-1] - value(eps); + for(i = 0;;) { + L = value(d); + value(d) -= L; + *s++ = '0' + (int)L; + if (value(d) < value(eps)) + goto ret1; + if (1. - value(d) < value(eps)) + goto bump_up; + if (++i >= ilim) + break; + value(eps) *= 10.; + value(d) *= 10.; + } + } + else { +#endif + /* Generate ilim digits, then fix them up. */ + value(eps) *= tens[ilim-1]; + for(i = 1;; i++, value(d) *= 10.) { + L = value(d); + value(d) -= L; + *s++ = '0' + (int)L; + if (i == ilim) { + if (value(d) > 0.5 + value(eps)) + goto bump_up; + else if (value(d) < 0.5 - value(eps)) { + while(*--s == '0'); + s++; + goto ret1; + } + break; + } + } +#ifndef No_leftright + } +#endif +fast_failed: + s = s0; + value(d) = value(d2); + k = k0; + ilim = ilim0; + } + + /* Do we have a "small" integer? */ + + if (be >= 0 && k <= Int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S = mhi = 0; + if (ilim < 0 || value(d) <= 5*ds) + goto no_digits; + goto one_digit; + } + for(i = 1;; i++) { + L = value(d) / ds; + value(d) -= L*ds; +#ifdef Check_FLT_ROUNDS + /* If FLT_ROUNDS == 2, L will usually be high by 1 */ + if (value(d) < 0) { + L--; + value(d) += ds; + } +#endif + *s++ = '0' + (int)L; + if (i == ilim) { + value(d) += value(d); + if (value(d) > ds || (value(d) == ds && (L & 1))) { +bump_up: + while(*--s == '9') + if (s == s0) { + k++; + *s = '0'; + break; + } + ++*s++; + } + break; + } + if (!(value(d) *= 10.)) + break; + } + goto ret1; + } + + m2 = b2; + m5 = b5; + mhi = mlo = 0; + if (leftright) { + if (mode < 2) { + i = +#ifndef Sudden_Underflow + denorm ? be + (Bias + (P-1) - 1 + 1) : +#endif +#ifdef IBM + 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3); +#else + 1 + P - bbits; +#endif + } + else { + j = ilim - 1; + if (m5 >= j) + m5 -= j; + else { + s5 += j -= m5; + b5 += j; + m5 = 0; + } + if ((i = ilim) < 0) { + m2 -= i; + i = 0; + } + } + b2 += i; + s2 += i; + mhi = i2b(1); + } + if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5); + b1 = mult(mhi, b); + Bfree(b); + b = b1; + } + if ((j = b5 - m5)) { + b = pow5mult(b, j); + } + } else { + b = pow5mult(b, b5); + } + } + S = i2b(1); + if (s5 > 0) + S = pow5mult(S, s5); + /* Check for special case that d is a normalized power of 2. */ + + if (mode < 2) { + if (!word1(d) && !(word0(d) & Bndry_mask) +#ifndef Sudden_Underflow + && word0(d) & Exp_mask +#endif + ) { + /* The special case */ + b2 += Log2P; + s2 += Log2P; + spec_case = 1; + } + else + spec_case = 0; + } + + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ +#ifdef Pack_32 + if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f)) + i = 32 - i; #else - (CONST char *s00, char **se) + if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf)) + i = 16 - i; +#endif + if (i > 4) { + i -= 4; + b2 += i; + m2 += i; + s2 += i; + } + else if (i < 4) { + i += 28; + b2 += i; + m2 += i; + s2 += i; + } + if (b2 > 0) + b = lshift(b, b2); + if (s2 > 0) + S = lshift(S, s2); + if (k_check) { + if (cmp(b,S) < 0) { + k--; + b = multadd(b, 10, 0); /* we botched the k estimate */ + if (leftright) + mhi = multadd(mhi, 10, 0); + ilim = ilim1; + } + } + if (ilim <= 0 && mode > 2) { + if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) { + /* no digits, fcvt style */ +no_digits: + k = -1 - ndigits; + goto ret; + } +one_digit: + *s++ = '1'; + k++; + goto ret; + } + if (leftright) { + if (m2 > 0) + mhi = lshift(mhi, m2); + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) { + mhi = Balloc(mhi->k); + Bcopy(mhi, mlo); + mhi = lshift(mhi, Log2P); + } + + for(i = 1;;i++) { + dig = quorem(b,S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + delta = diff(S, mhi); + j1 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta); +#ifndef ROUND_BIASED + if (j1 == 0 && !mode && !(word1(d) & 1)) { + if (dig == '9') + goto round_9_up; + if (j > 0) + dig++; + *s++ = dig; + goto ret; + } #endif + if (j < 0 || (j == 0 && !mode +#ifndef ROUND_BIASED + && !(word1(d) & 1) +#endif + )) { + if (j1 > 0) { + b = lshift(b, 1); + j1 = cmp(b, S); + if ((j1 > 0 || (j1 == 0 && (dig & 1))) + && dig++ == '9') + goto round_9_up; + } + *s++ = dig; + goto ret; + } + if (j1 > 0) { + if (dig == '9') { /* possible if i == 1 */ +round_9_up: + *s++ = '9'; + goto roundoff; + } + *s++ = dig + 1; + goto ret; + } + *s++ = dig; + if (i == ilim) + break; + b = multadd(b, 10, 0); + if (mlo == mhi) + mlo = mhi = multadd(mhi, 10, 0); + else { + mlo = multadd(mlo, 10, 0); + mhi = multadd(mhi, 10, 0); + } + } + } + else + for(i = 1;; i++) { + *s++ = dig = quorem(b,S) + '0'; + if (i >= ilim) + break; + b = multadd(b, 10, 0); + } + + /* Round off last digit */ + + b = lshift(b, 1); + j = cmp(b, S); + if (j > 0 || (j == 0 && (dig & 1))) { +roundoff: + while(*--s == '9') + if (s == s0) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; + } + else { + while(*--s == '0'); + s++; + } +ret: + Bfree(S); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo); + Bfree(mhi); + } +ret1: + + _THREAD_PRIVATE_MUTEX_LOCK(pow5mult_mutex); + while (p5s) { + tmp = p5s; + p5s = p5s->next; + free(tmp); + } + _THREAD_PRIVATE_MUTEX_UNLOCK(pow5mult_mutex); + + Bfree(b); + + if (s == s0) { /* don't return empty string */ + *s++ = '0'; + k = 0; + } + *s = 0; + *decpt = k + 1; + if (rve) + *rve = s; + return s0; +} + +ZEND_API double zend_strtod (CONST char *s00, char **se) { int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign, - e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign; + e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign; CONST char *s, *s0, *s1; double aadj, aadj1, adj; _double rv, rv0; @@ -1282,7 +2051,7 @@ zend_strtod while(*++s == '0') ; if (!*s) goto ret; - } + } s0 = s; y = z = 0; for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) @@ -1301,11 +2070,11 @@ zend_strtod nf += nz; nz = 0; goto have_dig; - } - goto dig_done; } + goto dig_done; + } for(; c >= '0' && c <= '9'; c = *++s) { - have_dig: +have_dig: nz++; if (c -= '0') { nf += nz; @@ -1319,16 +2088,16 @@ zend_strtod else if (nd <= DBL_DIG + 1) z = 10*z + c; nz = 0; - } } } - dig_done: + } +dig_done: e = 0; if (c == 'e' || c == 'E') { if (!nd && !nz && !nz0) { s = s00; goto ret; - } + } s00 = s; esign = 0; switch(c = *++s) { @@ -1336,7 +2105,7 @@ zend_strtod esign = 1; case '+': c = *++s; - } + } if (c >= '0' && c <= '9') { while(c == '0') c = *++s; @@ -1354,18 +2123,18 @@ zend_strtod e = (int)L; if (esign) e = -e; - } + } else e = 0; - } + } else s = s00; - } + } if (!nd) { if (!nz && !nz0) s = s00; goto ret; - } + } e1 = e -= nf; /* Now we have nd0 digits, starting at s0, followed by a @@ -1382,9 +2151,9 @@ zend_strtod bd0 = 0; if (nd <= DBL_DIG #ifndef RND_PRODQUOT - && FLT_ROUNDS == 1 + && FLT_ROUNDS == 1 #endif - ) { + ) { if (!e) goto ret; if (e > 0) { @@ -1393,10 +2162,10 @@ zend_strtod goto vax_ovfl_check; #else /* value(rv) = */ rounded_product(value(rv), - tens[e]); + tens[e]); goto ret; #endif - } + } i = DBL_DIG - nd; if (e <= Ten_pmax + i) { /* A fancier test would sometimes let us do @@ -1408,29 +2177,29 @@ zend_strtod /* VAX exponent range is so narrow we must * worry about overflow here... */ - vax_ovfl_check: +vax_ovfl_check: word0(rv) -= P*Exp_msk1; /* value(rv) = */ rounded_product(value(rv), - tens[e]); + tens[e]); if ((word0(rv) & Exp_mask) - > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) + > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) goto ovfl; word0(rv) += P*Exp_msk1; #else /* value(rv) = */ rounded_product(value(rv), - tens[e]); + tens[e]); #endif goto ret; - } } + } #ifndef Inaccurate_Divide else if (e >= -Ten_pmax) { /* value(rv) = */ rounded_quotient(value(rv), - tens[-e]); + tens[-e]); goto ret; - } -#endif } +#endif + } e1 += nd - k; /* Get starting approximation = rv * 10**e1 */ @@ -1440,7 +2209,7 @@ zend_strtod value(rv) *= tens[i]; if (e1 &= ~15) { if (e1 > DBL_MAX_10_EXP) { - ovfl: +ovfl: errno = ERANGE; #ifndef Bad_float_h value(rv) = HUGE_VAL; @@ -1457,29 +2226,29 @@ zend_strtod if (bd0) goto retfree; goto ret; - } + } if (e1 >>= 4) { for(j = 0; e1 > 1; j++, e1 >>= 1) if (e1 & 1) value(rv) *= bigtens[j]; - /* The last multiplication could overflow. */ + /* The last multiplication could overflow. */ word0(rv) -= P*Exp_msk1; value(rv) *= bigtens[j]; if ((z = word0(rv) & Exp_mask) - > Exp_msk1*(DBL_MAX_EXP+Bias-P)) + > Exp_msk1*(DBL_MAX_EXP+Bias-P)) goto ovfl; if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { /* set to largest number */ /* (Can't trust DBL_MAX) */ word0(rv) = Big0; word1(rv) = Big1; - } + } else word0(rv) += P*Exp_msk1; - } - } + } + } else if (e1 < 0) { e1 = -e1; if ((i = e1 & 15)) @@ -1498,21 +2267,21 @@ zend_strtod value(rv) = 2.*value(rv0); value(rv) *= tinytens[j]; if (!value(rv)) { - undfl: +undfl: value(rv) = 0.; errno = ERANGE; if (bd0) goto retfree; goto ret; - } + } word0(rv) = Tiny0; word1(rv) = Tiny1; /* The refinement below will clean * this approximation up. */ - } } } + } /* Now the hard part -- adjusting rv to the correct value.*/ @@ -1529,11 +2298,11 @@ zend_strtod if (e >= 0) { bb2 = bb5 = 0; bd2 = bd5 = e; - } + } else { bb2 = bb5 = -e; bd2 = bd5 = 0; - } + } if (bbe >= 0) bb2 += bbe; else @@ -1561,13 +2330,13 @@ zend_strtod bb2 -= i; bd2 -= i; bs2 -= i; - } + } if (bb5 > 0) { bs = pow5mult(bs, bb5); bb1 = mult(bs, bb); Bfree(bb); bb = bb1; - } + } if (bb2 > 0) bb = lshift(bb, bb2); if (bd5 > 0) @@ -1590,12 +2359,12 @@ zend_strtod if (cmp(delta, bs) > 0) goto drop_down; break; - } + } if (i == 0) { /* exactly half-way between */ if (dsign) { if ((word0(rv) & Bndry_mask1) == Bndry_mask1 - && word1(rv) == 0xffffffff) { + && word1(rv) == 0xffffffff) { /*boundary case -- increment exponent*/ word0(rv) = (word0(rv) & Exp_mask) + Exp_msk1 @@ -1605,19 +2374,19 @@ zend_strtod ; word1(rv) = 0; break; - } } + } else if (!(word0(rv) & Bndry_mask) && !word1(rv)) { - drop_down: +drop_down: /* boundary case -- decrement exponent */ #ifdef Sudden_Underflow L = word0(rv) & Exp_mask; #ifdef IBM if (L < Exp_msk1) #else - if (L <= Exp_msk1) + if (L <= Exp_msk1) #endif - goto undfl; + goto undfl; L -= Exp_msk1; #else L = (word0(rv) & Exp_mask) - Exp_msk1; @@ -1629,7 +2398,7 @@ zend_strtod #else break; #endif - } + } #ifndef ROUND_BIASED if (!(word1(rv) & LSB)) break; @@ -1643,10 +2412,10 @@ zend_strtod if (!value(rv)) goto undfl; #endif - } + } #endif break; - } + } if ((aadj = ratio(delta, bs)) <= 2.) { if (dsign) aadj = aadj1 = 1.; @@ -1657,7 +2426,7 @@ zend_strtod #endif aadj = 1.; aadj1 = -1.; - } + } else { /* special case -- power of FLT_RADIX to be */ /* rounded down... */ @@ -1667,8 +2436,8 @@ zend_strtod else aadj *= 0.5; aadj1 = -aadj; - } } + } else { aadj *= 0.5; aadj1 = dsign ? aadj : -aadj; @@ -1680,12 +2449,12 @@ zend_strtod case 0: /* towards 0 */ case 3: /* towards -infinity */ aadj1 += 0.5; - } + } #else if (FLT_ROUNDS == 0) aadj1 += 0.5; #endif - } + } y = word0(rv) & Exp_mask; /* Check for overflow */ @@ -1702,10 +2471,10 @@ zend_strtod word0(rv) = Big0; word1(rv) = Big1; goto cont; - } + } else word0(rv) += P*Exp_msk1; - } + } else { #ifdef Sudden_Underflow if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { @@ -1716,23 +2485,23 @@ zend_strtod #ifdef IBM if ((word0(rv) & Exp_mask) < P*Exp_msk1) #else - if ((word0(rv) & Exp_mask) <= P*Exp_msk1) + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) #endif { - if (word0(rv0) == Tiny0 - && word1(rv0) == Tiny1) - goto undfl; - word0(rv) = Tiny0; - word1(rv) = Tiny1; - goto cont; + if (word0(rv0) == Tiny0 + && word1(rv0) == Tiny1) + goto undfl; + word0(rv) = Tiny0; + word1(rv) = Tiny1; + goto cont; } - else - word0(rv) -= P*Exp_msk1; - } + else + word0(rv) -= P*Exp_msk1; + } else { adj = aadj1 * ulp(value(rv)); value(rv) += adj; - } + } #else /* Compute adj so that the IEEE rounding rules will * correctly round rv + adj in some half-way cases. @@ -1745,11 +2514,11 @@ zend_strtod aadj1 = (double)(int)(aadj + 0.5); if (!dsign) aadj1 = -aadj1; - } + } adj = aadj1 * ulp(value(rv)); value(rv) += adj; #endif - } + } z = word0(rv) & Exp_mask; if (y == z) { /* Can we stop now? */ @@ -1759,44 +2528,37 @@ zend_strtod if (dsign || word1(rv) || word0(rv) & Bndry_mask) { if (aadj < .4999999 || aadj > .5000001) break; - } + } else if (aadj < .4999999/FLT_RADIX) break; - } - cont: + } +cont: Bfree(bb); Bfree(bd); Bfree(bs); Bfree(delta); - } - retfree: + } +retfree: Bfree(bb); Bfree(bd); Bfree(bs); Bfree(bd0); Bfree(delta); - ret: +ret: if (se) *se = (char *)s; result = sign ? -value(rv) : value(rv); - for (i = 0; i <= Kmax; i++) { - Bigint **listp = &freelist[i]; - while ((tmp = *listp) != NULL) { - *listp = tmp->next; - free(tmp); - } - freelist[i] = NULL; - } - + _THREAD_PRIVATE_MUTEX_LOCK(pow5mult_mutex); while (p5s) { tmp = p5s; p5s = p5s->next; free(tmp); } + _THREAD_PRIVATE_MUTEX_UNLOCK(pow5mult_mutex); return result; - } +} /* UTODO: someone can reimplement this using the code above, if they really want to. */ ZEND_API double zend_u_strtod(const UChar *nptr, UChar **endptr) @@ -1817,3 +2579,12 @@ ZEND_API double zend_u_strtod(const UChar *nptr, UChar **endptr) return 0; } } + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/Zend/zend_strtod.h b/Zend/zend_strtod.h index f740ccf1b7..8d18a09e01 100644 --- a/Zend/zend_strtod.h +++ b/Zend/zend_strtod.h @@ -5,7 +5,7 @@ | Copyright (c) 1998-2006 Zend Technologies Ltd. (http://www.zend.com) | +----------------------------------------------------------------------+ | This source file is subject to version 2.00 of the Zend license, | - | that is bundled with this package in the file LICENSE, and is | + | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.zend.com/license/2_00.txt. | | If you did not receive a copy of the Zend license and are unable to | @@ -25,7 +25,12 @@ #include BEGIN_EXTERN_C() +ZEND_API void zend_freedtoa(char *s); +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 int zend_startup_strtod(void); +ZEND_API int zend_shutdown_strtod(void); END_EXTERN_C() #endif diff --git a/ext/standard/formatted_print.c b/ext/standard/formatted_print.c index 454eacdc06..bd9627720a 100644 --- a/ext/standard/formatted_print.c +++ b/ext/standard/formatted_print.c @@ -51,110 +51,6 @@ static char hexchars[] = "0123456789abcdef"; static char HEXCHARS[] = "0123456789ABCDEF"; -/* - * cvt.c - IEEE floating point formatting routines for FreeBSD - * from GNU libc-4.6.27 - */ - -/* - * php_convert_to_decimal converts to decimal - * the number of digits is specified by ndigit - * decpt is set to the position of the decimal point - * sign is set to 0 for positive, 1 for negative - */ -static char *php_convert_to_decimal(double arg, int ndigits, int *decpt, int *sign, int eflag) -{ - register int r2; - int mvl; - double fi, fj; - register char *p, *p1; - /*THREADX*/ -#ifndef THREAD_SAFE - static char cvt_buf[NDIG]; -#endif - - if (ndigits >= NDIG - 1) - ndigits = NDIG - 2; - r2 = 0; - *sign = 0; - p = &cvt_buf[0]; - if (arg < 0) { - *sign = 1; - arg = -arg; - } - arg = modf(arg, &fi); - p1 = &cvt_buf[NDIG]; - /* - * Do integer part - */ - if (fi != 0) { - p1 = &cvt_buf[NDIG]; - while (fi != 0) { - fj = modf(fi / 10, &fi); - if (p1 <= &cvt_buf[0]) { - mvl = NDIG - ndigits; - memmove(&cvt_buf[mvl], &cvt_buf[0], NDIG-mvl-1); - p1 += mvl; - } - *--p1 = (int) ((fj + .03) * 10) + '0'; - r2++; - } - while (p1 < &cvt_buf[NDIG]) - *p++ = *p1++; - } else if (arg > 0) { - while ((fj = arg * 10) < 1) { - if (!eflag && (r2 * -1) < ndigits) { - break; - } - arg = fj; - r2--; - } - } - p1 = &cvt_buf[ndigits]; - if (eflag == 0) - p1 += r2; - *decpt = r2; - if (p1 < &cvt_buf[0]) { - cvt_buf[0] = '\0'; - return (cvt_buf); - } - if (p <= p1 && p < &cvt_buf[NDIG]) { - arg = modf(arg * 10, &fj); - if ((int)fj==10) { - *p++ = '1'; - fj = 0; - *decpt = ++r2; - } - while (p <= p1 && p < &cvt_buf[NDIG]) { - *p++ = (int) fj + '0'; - arg = modf(arg * 10, &fj); - } - } - if (p1 >= &cvt_buf[NDIG]) { - cvt_buf[NDIG - 1] = '\0'; - return (cvt_buf); - } - p = p1; - *p1 += 5; - while (*p1 > '9') { - *p1 = '0'; - if (p1 > cvt_buf) - ++ * --p1; - else { - *p1 = '1'; - (*decpt)++; - if (eflag == 0) { - if (p > cvt_buf) - *p = '0'; - p++; - } - } - } - *p = '\0'; - return (cvt_buf); -} - - inline static void php_sprintf_appendchar(char **buffer, int *pos, int *size, char add TSRMLS_DC) { @@ -299,19 +195,10 @@ php_sprintf_appenddouble(char **buffer, int *pos, int always_sign TSRMLS_DC) { - char numbuf[NUM_BUF_SIZE]; - char *cvt; - register int i = 0, j = 0; - int sign, decpt, cvt_len; - char decimal_point = '.'; -#ifdef HAVE_LOCALE_H - struct lconv lc; - char locale_decimal_point; - localeconv_r(&lc); - locale_decimal_point = (lc.decimal_point)[0]; -#else - char locale_decimal_point = '.'; -#endif + char num_buf[NUM_BUF_SIZE]; + char *s, *q; + int s_len; + int is_negative; PRINTF_DEBUG(("sprintf: appenddouble(%x, %x, %x, %f, %d, '%c', %d, %c)\n", *buffer, pos, size, number, width, padding, alignment, fmt)); @@ -322,92 +209,66 @@ php_sprintf_appenddouble(char **buffer, int *pos, } if (zend_isnan(number)) { - sign = (number<0); + is_negative = (number<0); php_sprintf_appendstring(buffer, pos, size, "NaN", 3, 0, padding, - alignment, precision, sign, 0, always_sign); + alignment, precision, is_negative, 0, always_sign); return; } if (zend_isinf(number)) { - sign = (number<0); + is_negative = (number<0); php_sprintf_appendstring(buffer, pos, size, "INF", 3, 0, padding, - alignment, precision, sign, 0, always_sign); + alignment, precision, is_negative, 0, always_sign); return; } - cvt = php_convert_to_decimal(number, precision, &decpt, &sign, (fmt == 'e')); - cvt_len = strlen(cvt); - - if (sign) { - numbuf[i++] = '-'; - } else if (always_sign) { - numbuf[i++] = '+'; - } - - if (fmt == 'f' || fmt == 'F') { - if (decpt <= 0) { - numbuf[i++] = '0'; - if (precision > 0) { - int k = precision; - numbuf[i++] = fmt == 'F' ? decimal_point : locale_decimal_point; - while ((decpt++ < 0) && k--) { - numbuf[i++] = '0'; - } - } - } else { - while (decpt-- > 0) { - numbuf[i++] = j < cvt_len ? cvt[j++] : '0'; + switch (fmt) { + case 'e': + if (precision) { + precision--; } - if (precision > 0) { - numbuf[i++] = fmt == 'F' ? decimal_point : locale_decimal_point; - while (precision-- > 0) { - numbuf[i++] = j < cvt_len ? cvt[j++] : '0'; - } - } - } - } else if (fmt == 'e' || fmt == 'E') { - char *exp_p; - int dec2; - - decpt--; - - numbuf[i++] = cvt[j++]; - numbuf[i++] = decimal_point; - - if (precision > 0) { - int k = precision; - - while (k-- && cvt[j]) { - numbuf[i++] = cvt[j++]; + case 'E': + case 'f': + s = ap_php_conv_fp(fmt, number, 0, precision, + &is_negative, &num_buf[1], &s_len); + if (is_negative) { + num_buf[0] = '-'; + s = num_buf; + s_len++; + } else if (always_sign) { + num_buf[0] = '+'; + s = num_buf; + s_len++; } - } else { - numbuf[i++] = '0'; - } - - numbuf[i++] = fmt; - exp_p = php_convert_to_decimal(decpt, 0, &dec2, &sign, 0); - numbuf[i++] = sign ? '-' : '+'; - if (*exp_p) { - while (*exp_p) { - numbuf[i++] = *(exp_p++); + break; + + case 'g': + case 'G': + if (precision == 0) + precision = 1; + /* + * * We use &num_buf[ 1 ], so that we have room for the sign + */ + s = bsd_gcvt(number, precision, &num_buf[1]); + is_negative = 0; + if (*s == '-') { + is_negative = 1; + s = &num_buf[1]; + } else if (always_sign) { + num_buf[0] = '+'; + s = num_buf; } - } else { - numbuf[i++] = '0'; - } - } else { - numbuf[i++] = cvt[j++]; - if (precision > 0) - numbuf[i++] = decimal_point; - } - while (cvt[j]) { - numbuf[i++] = cvt[j++]; - } + s_len = strlen(s); - numbuf[i] = '\0'; + if (fmt == 'G' && (q = strchr(s, 'e')) != NULL) { + *q = 'E'; + } + break; + } - php_sprintf_appendstring(buffer, pos, size, numbuf, width, 0, padding, - alignment, i, sign, 0, always_sign); + php_sprintf_appendstring(buffer, pos, size, s, width, 0, padding, + alignment, s_len, is_negative, 0, always_sign); } @@ -690,7 +551,10 @@ php_formatted_print(int ht, int *len, int use_array, int format_offset TSRMLS_DC width, padding, alignment); break; + case 'g': + case 'G': case 'e': + case 'E': case 'f': case 'F': /* XXX not done */ diff --git a/ext/standard/tests/serialize/003.phpt b/ext/standard/tests/serialize/003.phpt index 173f57b72c..edbcb1f0f0 100644 --- a/ext/standard/tests/serialize/003.phpt +++ b/ext/standard/tests/serialize/003.phpt @@ -15,11 +15,11 @@ foreach(array(1e2, 5.2e25, 85.29e-23, 9e-9) AS $value) { d:100; float\(100\) -d:5\.2E\+25; +d:5[0-9]*; float\(5\.2E\+25\) d:8\.52[89][0-9]+E-22; float\(8\.529E-22\) -d:9\.[0-9]*E-9; +d:8\.[9]*[0-9]*E-9; float\(9\.0E-9\) diff --git a/ext/standard/tests/strings/sprintf_f.phpt b/ext/standard/tests/strings/sprintf_f.phpt index 6135270d23..6cba6d10b4 100755 --- a/ext/standard/tests/strings/sprintf_f.phpt +++ b/ext/standard/tests/strings/sprintf_f.phpt @@ -37,4 +37,4 @@ string(7) "-5.60 " string(7) "-005.60" string(7) "-5.6000" -string(105) "12345678%d00000000000000000000000000000000000000000000000000.0000" \ No newline at end of file +string(105) "1234567%d.0000" diff --git a/main/snprintf.c b/main/snprintf.c index caaf3b78c0..3dd2ebf93f 100644 --- a/main/snprintf.c +++ b/main/snprintf.c @@ -18,6 +18,210 @@ /* $Id$ */ + +#include "php.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_INTTYPES_H +#include +#endif + +#ifdef HAVE_LOCALE_H +#include +#endif + +/* + * Copyright (c) 2002, 2006 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ + +static char * __cvt(double value, int ndigit, int *decpt, int *sign, int fmode, int pad) +{ + register char *s = NULL; + char *p, *rve, c; + size_t siz; + + if (ndigit < 0) { + siz = -ndigit + 1; + } else { + siz = ndigit + 1; + } + + /* __dtoa() doesn't allocate space for 0 so we do it by hand */ + if (value == 0.0) { + *decpt = 1 - fmode; /* 1 for 'e', 0 for 'f' */ + *sign = 0; + if ((rve = s = (char *)malloc(ndigit?siz:2)) == NULL) + return(NULL); + *rve++ = '0'; + *rve = '\0'; + if (!ndigit) { + return(s); + } + } else { + p = zend_dtoa(value, fmode + 2, ndigit, decpt, sign, &rve); + if (*decpt == 9999) { + /* Infinity or Nan, convert to inf or nan like printf */ + *decpt = 0; + c = *p; + zend_freedtoa(p); + return(c == 'I' ? "inf" : "nan"); + } + /* Make a local copy and adjust rve to be in terms of s */ + if (pad && fmode) + siz += *decpt; + if ((s = (char *)malloc(siz+1)) == NULL) { + zend_freedtoa(p); + return(NULL); + } + (void) strlcpy(s, p, siz); + rve = s + (rve - p); + zend_freedtoa(p); + } + + /* Add trailing zeros */ + if (pad) { + siz -= rve - s; + while (--siz) + *rve++ = '0'; + *rve = '\0'; + } + + return(s); +} + +char *bsd_ecvt(double value, int ndigit, int *decpt, int *sign) +{ + return(__cvt(value, ndigit, decpt, sign, 0, 1)); +} + +char *bsd_fcvt(double value, int ndigit, int *decpt, int *sign) +{ + return(__cvt(value, ndigit, decpt, sign, 1, 1)); +} + +char *bsd_gcvt(double value, int ndigit, char *buf) +{ + char *digits, *dst, *src; + int i, decpt, sign; + struct lconv *lconv; + + lconv = localeconv(); + + digits = zend_dtoa(value, 2, 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' ? "inf" : "nan"); + zend_freedtoa(digits); + return (buf); + } + + dst = buf; + if (sign) + *dst++ = '-'; + + for (i = 0; i < ndigit && digits[i] != '\0'; i++); + + if ((decpt >= 0 && decpt - i > 4) + || (decpt < 0 && 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++ = *lconv->decimal_point; + if (*src == '\0') { + *dst++ = '0'; + } else { + do { + *dst++ = *src++; + } while (*src != '\0'); + } + *dst++ = 'e'; + if (sign) + *dst++ = '-'; + else + *dst++ = '+'; + if (decpt < 10) { + *dst++ = '0' + decpt; + *dst = '\0'; + } else { + /* XXX - optimize */ + for (sign = decpt, i = 0; (sign /= 10) != 0; i++) + continue; + 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++ = *lconv->decimal_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++ = *lconv->decimal_point; + for (i = decpt; digits[i] != '\0'; i++) { + *dst++ = digits[i]; + } + } + *dst = '\0'; + } + zend_freedtoa(digits); + return (buf); +} + + /* ==================================================================== * Copyright (c) 1995-1998 The Apache Group. All rights reserved. * @@ -73,20 +277,6 @@ * for xinetd. */ -#include "php.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_INTTYPES_H -#include -#endif - #define FALSE 0 #define TRUE 1 #define NUL '\0' @@ -171,14 +361,17 @@ char * ap_php_conv_fp(register char format, register double num, boolean_e add_dp, int precision, bool_int * is_negative, char *buf, int *len) { register char *s = buf; - register char *p; + register char *p, *p_orig; int decimal_point; - char buf1[NDIG]; + + if (precision >= NDIG - 1) { + precision = NDIG - 2; + } if (format == 'f') - p = ap_php_fcvt(num, precision, &decimal_point, is_negative, buf1); + p_orig = p = bsd_fcvt(num, precision, &decimal_point, is_negative); else /* either e or E format */ - p = ap_php_ecvt(num, precision + 1, &decimal_point, is_negative, buf1); + p_orig = p = bsd_ecvt(num, precision + 1, &decimal_point, is_negative); /* * Check for Infinity and NaN @@ -187,17 +380,20 @@ char * ap_php_conv_fp(register char format, register double num, *len = strlen(p); memcpy(buf, p, *len + 1); *is_negative = FALSE; + free(p_orig); return (buf); } if (format == 'f') { if (decimal_point <= 0) { - *s++ = '0'; - if (precision > 0) { - *s++ = '.'; - while (decimal_point++ < 0) - *s++ = '0'; - } else if (add_dp) { - *s++ = '.'; + if (num != 0 || precision > 0) { + *s++ = '0'; + if (precision > 0) { + *s++ = '.'; + while (decimal_point++ < 0) + *s++ = '0'; + } else if (add_dp) { + *s++ = '.'; + } } } else { int addz = decimal_point >= NDIG ? decimal_point - NDIG + 1 : 0; @@ -239,21 +435,18 @@ char * ap_php_conv_fp(register char format, register double num, /* * Make sure the exponent has at least 2 digits */ - if (t_len == 1) - *s++ = '0'; while (t_len--) *s++ = *p++; } else { *s++ = '+'; *s++ = '0'; - *s++ = '0'; } } *len = s - buf; + free(p_orig); return (buf); } - /* * Convert num to a base X number where X is a power of 2. nbits determines X. * For example, if nbits is 3, we do base 8 conversion @@ -283,189 +476,6 @@ char * ap_php_conv_p2(register u_wide_int num, register int nbits, return (p); } -/* - * cvt.c - IEEE floating point formatting routines for FreeBSD - * from GNU libc-4.6.27 - */ - -/* - * ap_php_ecvt converts to decimal - * the number of digits is specified by ndigit - * decpt is set to the position of the decimal point - * sign is set to 0 for positive, 1 for negative - */ - - -char * ap_php_cvt(double arg, int ndigits, int *decpt, int *sign, int eflag, char *buf) -{ - register int r2; - int mvl; - double fi, fj; - register char *p, *p1; - - if (ndigits >= NDIG - 1) - ndigits = NDIG - 2; - r2 = 0; - *sign = 0; - p = &buf[0]; - if (arg < 0) { - *sign = 1; - arg = -arg; - } - arg = modf(arg, &fi); - p1 = &buf[NDIG]; - /* - * Do integer part - */ - if (fi != 0) { - while (fi != 0) { - fj = modf(fi / 10, &fi); - if (p1 <= &buf[0]) { - mvl = NDIG - ndigits; - if (ndigits > 0) { - memmove(&buf[mvl], &buf[0], NDIG-mvl-1); - } - p1 += mvl; - } - *--p1 = (int) ((fj + .03) * 10) + '0'; - r2++; - } - while (p1 < &buf[NDIG]) { - *p++ = *p1++; - } - } else if (arg > 0) { - while ((fj = arg * 10) < 1) { - if (!eflag && (r2 * -1) < ndigits) { - break; - } - arg = fj; - r2--; - } - } - p1 = &buf[ndigits]; - if (eflag == 0) - p1 += r2; - *decpt = r2; - if (p1 < &buf[0]) { - buf[0] = '\0'; - return (buf); - } - if (p <= p1 && p < &buf[NDIG]) { - arg = modf(arg * 10, &fj); - if ((int)fj==10) { - *p++ = '1'; - fj = 0; - *decpt = ++r2; - } - while (p <= p1 && p < &buf[NDIG]) { - *p++ = (int) fj + '0'; - arg = modf(arg * 10, &fj); - } - } - if (p1 >= &buf[NDIG]) { - buf[NDIG - 1] = '\0'; - return (buf); - } - p = p1; - *p1 += 5; - while (*p1 > '9') { - *p1 = '0'; - if (p1 > buf) - ++ * --p1; - else { - *p1 = '1'; - (*decpt)++; - if (eflag == 0) { - if (p > buf) - *p = '0'; - p++; - } - } - } - *p = '\0'; - return (buf); -} - -char * ap_php_ecvt(double arg, int ndigits, int *decpt, int *sign, char *buf) -{ - return (ap_php_cvt(arg, ndigits, decpt, sign, 1, buf)); -} - -char * ap_php_fcvt(double arg, int ndigits, int *decpt, int *sign, char *buf) -{ - return (ap_php_cvt(arg, ndigits, decpt, sign, 0, buf)); -} - -/* - * ap_php_gcvt - Floating output conversion to - * minimal length string - */ - -char * ap_php_gcvt(double number, int ndigit, char *buf, boolean_e altform) -{ - int sign, decpt; - register char *p1, *p2; - register int i; - char buf1[NDIG]; - - if (ndigit >= NDIG - 1) { - ndigit = NDIG - 2; - } - - p1 = ap_php_ecvt(number, ndigit, &decpt, &sign, buf1); - p2 = buf; - if (sign) - *p2++ = '-'; - for (i = ndigit - 1; i > 0 && p1[i] == '0'; i--) - ndigit--; - if ((decpt >= 0 && decpt - ndigit > 4) - || (decpt < 0 && decpt < -3)) { /* use E-style */ - decpt--; - *p2++ = *p1++; - *p2++ = '.'; - for (i = 1; i < ndigit; i++) - *p2++ = *p1++; - if (*(p2 - 1) == '.') { - *p2++ = '0'; - } - *p2++ = 'e'; - if (decpt < 0) { - decpt = -decpt; - *p2++ = '-'; - } else - *p2++ = '+'; - if (decpt / 100 > 0) - *p2++ = decpt / 100 + '0'; - if (decpt / 10 > 0) - *p2++ = (decpt % 100) / 10 + '0'; - *p2++ = decpt % 10 + '0'; - } else { - if (decpt <= 0) { - if (*p1 != '0') { - *p2++ = '0'; - *p2++ = '.'; - } - while (decpt < 0) { - decpt++; - *p2++ = '0'; - } - } - for (i = 1; i <= ndigit; i++) { - *p2++ = *p1++; - if (i == decpt) - *p2++ = '.'; - } - if (ndigit < decpt) { - while (ndigit++ < decpt) - *p2++ = '0'; - *p2++ = '.'; - } - } - if (p2[-1] == '.' && !altform) - p2--; - *p2 = '\0'; - return (buf); -} /* * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions @@ -909,7 +919,7 @@ static int format_converter(register buffy * odp, const char *fmt, pad_char = ' '; break; - + case 'f': case 'e': case 'E': @@ -979,8 +989,7 @@ static int format_converter(register buffy * odp, const char *fmt, /* * * We use &num_buf[ 1 ], so that we have room for the sign */ - s = ap_php_gcvt(fp_num, precision, &num_buf[1], - alternate_form); + s = bsd_gcvt(fp_num, precision, &num_buf[1]); if (*s == '-') prefix_char = *s++; else if (print_sign) diff --git a/main/snprintf.h b/main/snprintf.h index 38575b2b2f..2512ca79b3 100644 --- a/main/snprintf.h +++ b/main/snprintf.h @@ -106,11 +106,6 @@ typedef enum { LM_LONG_DOUBLE } length_modifier_e; -extern char * ap_php_cvt(double arg, int ndigits, int *decpt, int *sign, int eflag, char *buf); -extern char * ap_php_ecvt(double arg, int ndigits, int *decpt, int *sign, char *buf); -extern char * ap_php_fcvt(double arg, int ndigits, int *decpt, int *sign, char *buf); -extern char * ap_php_gcvt(double number, int ndigit, char *buf, boolean_e altform); - #ifdef PHP_WIN32 # define WIDE_INT __int64 #elif SIZEOF_LONG_LONG_INT @@ -134,6 +129,9 @@ extern char * ap_php_conv_fp(register char format, register double num, extern char * ap_php_conv_p2(register u_wide_int num, register int nbits, char format, char *buf_end, register int *len); +extern char * bsd_ecvt(double value, int ndigit, int *decpt, int *sign); +extern char * bsd_fcvt(double value, int ndigit, int *decpt, int *sign); +extern char * bsd_gcvt(double value, int ndigit, char *buf); #endif /* SNPRINTF_H */ diff --git a/main/spprintf.c b/main/spprintf.c index 477c03100f..c46bd70c61 100644 --- a/main/spprintf.c +++ b/main/spprintf.c @@ -685,8 +685,7 @@ fmt_string: /* * * We use &num_buf[ 1 ], so that we have room for the sign */ - s = ap_php_gcvt(fp_num, precision, &num_buf[1], - alternate_form); + s = bsd_gcvt(fp_num, precision, &num_buf[1]); if (*s == '-') prefix_char = *s++; else if (print_sign) -- 2.40.0