]> git.saurik.com Git - apple/libc.git/blobdiff - gdtoa/FreeBSD/gdtoa-gethex.c
Libc-583.tar.gz
[apple/libc.git] / gdtoa / FreeBSD / gdtoa-gethex.c
index 610f74735e98c314823dde21438f06c063166b38..13908e5be79696b4dfa2d7e3add0c759a247d226 100644 (file)
@@ -45,17 +45,30 @@ gethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
 {
        Bigint *b;
        CONST unsigned char *decpt, *s0, *s, *s1;
-       int esign, havedig, irv, k, n, nbits, up, zret;
+       int big, esign, havedig, irv, j, k, n, n0, nbits, up, zret;
        ULong L, lostbits, *x;
        Long e, e1;
 #ifdef USE_LOCALE
-       unsigned char decimalpoint = *localeconv()->decimal_point;
+       int i;
+#ifdef NO_LOCALE_CACHE
+       const unsigned char *decimalpoint = (unsigned char*)localeconv()->decimal_point;
 #else
-#define decimalpoint '.'
+       const unsigned char *decimalpoint;
+       static unsigned char *decimalpoint_cache;
+       if (!(s0 = decimalpoint_cache)) {
+               s0 = (unsigned char*)localeconv()->decimal_point;
+               if ((decimalpoint_cache = (char*)malloc(strlen(s0) + 1))) {
+                       strcpy(decimalpoint_cache, s0);
+                       s0 = decimalpoint_cache;
+                       }
+               }
+       decimalpoint = s0;
+#endif
 #endif
 
        if (!hexdig['0'])
                hexdig_init_D2A();
+       *bp = 0;
        havedig = 0;
        s0 = *(CONST unsigned char **)sp + 2;
        while(s0[havedig] == '0')
@@ -65,11 +78,21 @@ gethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
        decpt = 0;
        zret = 0;
        e = 0;
-       if (!hexdig[*s]) {
+       if (hexdig[*s])
+               havedig++;
+       else {
                zret = 1;
-               if (*s != decimalpoint)
+#ifdef USE_LOCALE
+               for(i = 0; decimalpoint[i]; ++i) {
+                       if (s[i] != decimalpoint[i])
+                               goto pcheck;
+                       }
+               decpt = s += i;
+#else
+               if (*s != '.')
                        goto pcheck;
                decpt = ++s;
+#endif
                if (!hexdig[*s])
                        goto pcheck;
                while(*s == '0')
@@ -81,19 +104,28 @@ gethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
                }
        while(hexdig[*s])
                s++;
-       if (*s == decimalpoint && !decpt) {
+#ifdef USE_LOCALE
+       if (*s == *decimalpoint && !decpt) {
+               for(i = 1; decimalpoint[i]; ++i) {
+                       if (s[i] != decimalpoint[i])
+                               goto pcheck;
+                       }
+               decpt = s += i;
+#else
+       if (*s == '.' && !decpt) {
                decpt = ++s;
+#endif
                while(hexdig[*s])
                        s++;
-               }
+               }/*}*/
        if (decpt)
                e = -(((Long)(s-decpt)) << 2);
  pcheck:
        s1 = s;
+       big = esign = 0;
        switch(*s) {
          case 'p':
          case 'P':
-               esign = 0;
                switch(*++s) {
                  case '-':
                        esign = 1;
@@ -106,15 +138,66 @@ gethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
                        break;
                        }
                e1 = n - 0x10;
-               while((n = hexdig[*++s]) !=0 && n <= 0x19)
+               while((n = hexdig[*++s]) !=0 && n <= 0x19) {
+                       if (e1 & 0xf8000000)
+                               big = 1;
                        e1 = 10*e1 + n - 0x10;
+                       }
                if (esign)
                        e1 = -e1;
                e += e1;
          }
        *sp = (char*)s;
+       if (!havedig)
+               *sp = (char*)s0 - 1;
        if (zret)
-               return havedig ? STRTOG_Zero : STRTOG_NoNumber;
+               return STRTOG_Zero;
+       if (big) {
+               if (esign) {
+                       switch(fpi->rounding) {
+                         case FPI_Round_up:
+                               if (sign)
+                                       break;
+                               goto ret_tiny;
+                         case FPI_Round_down:
+                               if (!sign)
+                                       break;
+                               goto ret_tiny;
+                         }
+                       goto retz;
+ ret_tiny:
+                       b = Balloc(0);
+                       b->wds = 1;
+                       b->x[0] = 1;
+                       goto dret;
+                       }
+               switch(fpi->rounding) {
+                 case FPI_Round_near:
+                       goto ovfl1;
+                 case FPI_Round_up:
+                       if (!sign)
+                               goto ovfl1;
+                       goto ret_big;
+                 case FPI_Round_down:
+                       if (sign)
+                               goto ovfl1;
+                       goto ret_big;
+                 }
+ ret_big:
+               nbits = fpi->nbits;
+               n0 = n = nbits >> kshift;
+               if (nbits & kmask)
+                       ++n;
+               for(j = n, k = 0; j >>= 1; ++k);
+               *bp = b = Balloc(k);
+               b->wds = n;
+               for(j = 0; j < n0; ++j)
+                       b->x[j] = ALL_ON;
+               if (n > n0)
+                       b->x[j] = ULbits >> (ULbits - (nbits & kmask));
+               *exp = fpi->emin;
+               return STRTOG_Normal | STRTOG_Inexlo;
+               }
        n = s1 - s0 - 1;
        for(k = 0; n > 7; n >>= 1)
                k++;
@@ -122,9 +205,19 @@ gethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
        x = b->x;
        n = 0;
        L = 0;
+#ifdef USE_LOCALE
+       for(i = 0; decimalpoint[i+1]; ++i);
+#endif
        while(s1 > s0) {
-               if (*--s1 == decimalpoint)
+#ifdef USE_LOCALE
+               if (*--s1 == decimalpoint[i]) {
+                       s1 -= i;
                        continue;
+                       }
+#else
+               if (*--s1 == '.')
+                       continue;
+#endif
                if (n == 32) {
                        *x++ = L;
                        L = 0;
@@ -146,7 +239,7 @@ gethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
                        k = n - 1;
                        if (x[k>>kshift] & 1 << (k & kmask)) {
                                lostbits = 2;
-                               if (k > 1 && any_on(b,k-1))
+                               if (k > 0 && any_on(b,k))
                                        lostbits = 3;
                                }
                        }
@@ -162,7 +255,10 @@ gethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
        if (e > fpi->emax) {
  ovfl:
                Bfree(b);
-               *bp = 0;
+ ovfl1:
+#ifndef NO_ERRNO
+               errno = ERANGE;
+#endif
                return STRTOG_Infinite | STRTOG_Overflow | STRTOG_Inexhi;
                }
        irv = STRTOG_Normal;
@@ -182,15 +278,22 @@ gethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
                          case FPI_Round_down:
                                if (sign) {
  one_bit:
-                                       *exp = fpi->emin;
                                        x[0] = b->wds = 1;
+ dret:
                                        *bp = b;
+                                       *exp = fpi->emin;
+#ifndef NO_ERRNO
+                                       errno = ERANGE;
+#endif
                                        return STRTOG_Denormal | STRTOG_Inexhi
                                                | STRTOG_Underflow;
                                        }
                          }
                        Bfree(b);
-                       *bp = 0;
+ retz:
+#ifndef NO_ERRNO
+                       errno = ERANGE;
+#endif
                        return STRTOG_Zero | STRTOG_Inexlo | STRTOG_Underflow;
                        }
                k = n - 1;