]> git.saurik.com Git - apple/libc.git/blobdiff - stdio/hexfloat.c
Libc-320.tar.gz
[apple/libc.git] / stdio / hexfloat.c
diff --git a/stdio/hexfloat.c b/stdio/hexfloat.c
new file mode 100644 (file)
index 0000000..2e59d9e
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifdef HEXFLOAT
+
+/*
+ * hexfloat.c provides routines that vfprintf and vfwprintf can call to convert
+ * floating point numbers to hexidecimal, as used by the %a and %A conversions.
+ * This is necessarily dependent not only on the floating point data format,
+ * but also byte order and existence of long double type.
+ *
+ * The union hexdouble represents the IEEE-754 double precision format, while
+ * union hexlongdouble represents the IEEE-754 extended double precision
+ * format.
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include <limits.h>
+
+#define        EXPBIAS         1023
+#define        EXPMIN          -1022
+#define        EXPSPECIAL      2047
+#define        FRACTHEX        13
+
+union hexdouble {
+       double d;
+       struct {
+#if defined(__ppc__)
+               unsigned int sign:1;
+               unsigned int exp:11;
+               unsigned long long fract:52;
+#elif defined(__i386__)
+               unsigned long long fract:52;
+               unsigned int exp:11;
+               unsigned int sign:1;
+#else
+#error Unsupported architecture
+#endif
+       } s;
+};
+
+#if !__TYPE_LONGDOUBLE_IS_DOUBLE
+#ifdef __i386__
+
+#define        LEXPBIAS        16383
+#define        LEXPMIN         -16382
+#define        LEXPSPECIAL     32767
+#define        LFRACTHEX       16
+
+union hexlongdouble {
+       long double d;
+       struct {
+               unsigned long long fract:63;
+               unsigned int i:1;
+               unsigned int exp:15;
+               unsigned int sign:1;
+       } s;
+};
+#endif /* __i386__ */
+#endif /* !__TYPE_LONGDOUBLE_IS_DOUBLE */
+
+int
+__hdtoa(double d, const char *xdigs, int prec, char *cp,
+    int *expt, int *signflag, char **dtoaend)
+{
+       union hexdouble u;
+       char buf[FRACTHEX];
+       char *hp;
+       int i;
+       long long fract;
+       //char *start = cp; //DEBUG
+
+       u.d = d;
+       //printf("\nsign=%d exp=%x fract=%llx\n", u.s.sign, u.s.exp, u.s.fract); //DEBUG
+       *signflag = u.s.sign;
+       fract = u.s.fract;
+       switch (u.s.exp) {
+       case EXPSPECIAL:        /* NaN or Inf */
+               *expt = INT_MAX;
+               *cp = (fract ? 'N' : 'I');
+               return 0;
+       case 0:                 /* Zero or denormalized */
+               *cp++ = '0';
+               *expt = (fract ? EXPMIN : 0);
+               break;
+       default:                /* Normal numbers */
+               *cp++ = '1';
+               *expt = u.s.exp - EXPBIAS;
+               break;
+       }
+       if (prec < 0)
+               prec = FRACTHEX;
+       //printf("prec=%d expt=%d\n", prec, *expt); //DEBUG
+       if (prec > 0) {
+               int dig = (prec > FRACTHEX ? FRACTHEX : prec);
+               int zero = prec - dig;
+               int shift = FRACTHEX - dig;
+               if (shift > 0)
+                   fract >>= (shift << 2);
+               for (hp = buf + dig, i = dig; i > 0; i--) {
+                       *--hp = xdigs[fract & 0xf];
+                       fract >>= 4;
+               }
+               strncpy(cp, hp, dig);
+               cp += dig;
+               while(zero-- > 0)
+                       *cp++ = '0';
+       }
+       *dtoaend = cp;
+       //while (start < cp) putchar(*start++); //DEBUG
+       //putchar('\n'); //DEBUG
+       return prec;
+}
+
+#if !__TYPE_LONGDOUBLE_IS_DOUBLE
+#ifdef __i386__
+int
+__hldtoa(long double d, const char *xdigs, int prec, char *cp,
+    int *expt, int *signflag, char **dtoaend)
+{
+       union hexlongdouble u;
+       char buf[LFRACTHEX];
+       char *hp;
+       int i;
+       unsigned long long fract;
+       //char *start = cp; //DEBUG
+
+       u.d = d;
+       //printf("d=%Lg u.d=%Lg\n", d, u.d); //DEBUG
+       //printf("\nsign=%d exp=%x fract=%llx\n", u.s.sign, u.s.exp, u.s.fract); //DEBUG
+       *signflag = u.s.sign;
+       fract = (u.s.fract << 1);
+       switch (u.s.exp) {
+       case LEXPSPECIAL:       /* NaN or Inf */
+               *expt = INT_MAX;
+               *cp = (fract ? 'N' : 'I');
+               return 0;
+       default:                /* Normal or denormalized */
+               *cp++ = u.s.i ? '1' : '0';
+               *expt = u.s.exp - LEXPBIAS;
+               break;
+       }
+       if (prec < 0)
+               prec = LFRACTHEX;
+       //printf("prec=%d expt=%d\n", prec, *expt); //DEBUG
+       if (prec > 0) {
+               int dig = (prec > LFRACTHEX ? LFRACTHEX : prec);
+               int zero = prec - dig;
+               int shift = LFRACTHEX - dig;
+               if (shift > 0)
+                   fract >>= (shift << 2);
+               for (hp = buf + dig, i = dig; i > 0; i--) {
+                       *--hp = xdigs[fract & 0xf];
+                       fract >>= 4;
+               }
+               strncpy(cp, hp, dig);
+               cp += dig;
+               while(zero-- > 0)
+                       *cp++ = '0';
+       }
+       *dtoaend = cp;
+       //while (start < cp) putchar(*start++); //DEBUG
+       //putchar('\n'); //DEBUG
+       return prec;
+}
+#endif /* __i386__ */
+#endif /* !__TYPE_LONGDOUBLE_IS_DOUBLE */
+
+#endif /* HEXFLOAT */