]> git.saurik.com Git - apple/libc.git/blob - gen.subproj/ppc.subproj/ecvt.c
Libc-186.tar.gz
[apple/libc.git] / gen.subproj / ppc.subproj / ecvt.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /* Copyright (c) 1992 NeXT Computer, Inc. All rights reserved.
23 *
24 * File: libc/m98k/gen/ecvt.c
25 *
26 * char *ecvt(double x, int ndigits, int *decimal, int *sign);
27 * char *fcvt(double x, int ndigits, int *decimal, int *sign);
28 *
29 * The function `ecvt' converts the double `x' to a null-terminated
30 * string of `ndigits' ASCII digits and returns a pointer to the string.
31 * The position of the decimal point relative to the beginning of the
32 * string is stored in the int pointed to by `decimal'. A negative
33 * value means that the decimal point appears to the left of the returned
34 * digits. If the sign of the result is negative, a non-zero value is
35 * stored in the int pointed to by `sign'; otherwise, a zero value is stored.
36 * The low-order digit of the returned value is rounded.
37 *
38 * The function `fcvt' is identical to `ecvt', except that the correct digit
39 * has been rounded for Fortran F-format output of the number of digits
40 * specified by `ndigits'.
41 *
42 * HISTORY
43 * 10-Nov-92 Derek B Clegg (dclegg@next.com)
44 * Ported to m98k.
45 * 8-Jan-92 Peter King (king@next.com)
46 * Created from M68K sources which was created from VAX sources.
47 */
48 #import <math.h>
49
50 static double ecvt_rint(double x);
51 static double ecvt_copysign(double x, double y);
52 static char *cvt(double arg, int ndigits, int *decptp, int *signp, int eflag);
53
54 #define isNAN(x) ((x) != (x))
55
56 /* big enough to handle %.20f conversion of 1e308 */
57 #define NDIG 350
58
59 char *
60 ecvt(double arg, int ndigits, int *decptp, int *signp)
61 {
62 return (cvt(arg, ndigits, decptp, signp, 1));
63 }
64
65 char *
66 fcvt(double arg, int ndigits, int *decptp, int *signp)
67 {
68 return (cvt(arg, ndigits, decptp, signp, 0));
69 }
70
71 static char *
72 cvt(double arg, int ndigits, int *decptp, int *signp, int eflag)
73 {
74 int decpt;
75 double fi, fj;
76 char *p, *p1;
77 static char buf[NDIG] = { 0 };
78
79 if (ndigits < 0)
80 ndigits = 0;
81 if (ndigits >= NDIG - 1)
82 ndigits = NDIG - 2;
83
84 decpt = 0;
85 *signp = 0;
86 p = &buf[0];
87
88 if (arg == 0) {
89 *decptp = 0;
90 while (p < &buf[ndigits])
91 *p++ = '0';
92 *p = '\0';
93 return (buf);
94 } else if (arg < 0) {
95 *signp = 1;
96 arg = -arg;
97 }
98
99 arg = modf(arg, &fi);
100 p1 = &buf[NDIG];
101
102 /* Do integer part */
103
104 if (fi != 0) {
105 while (fi != 0) {
106 fj = modf(fi/10, &fi);
107 #if 0
108 *--p1 = (int)((fj + 0.03) * 10) + '0';
109 #else
110 *--p1 = (int)ecvt_rint(fj * 10) + '0';
111 #endif
112 decpt++;
113 }
114 while (p1 < &buf[NDIG])
115 *p++ = *p1++;
116 } else if (arg > 0) {
117 while ((fj = arg*10) < 1) {
118 arg = fj;
119 decpt--;
120 }
121 }
122 *decptp = decpt;
123
124 /* Do the fractional part.
125 * p pts to where fraction should be concatenated.
126 * p1 is how far conversion must go to.
127 */
128 p1 = &buf[ndigits];
129 if (eflag == 0) {
130 /* fcvt must provide ndigits after decimal pt */
131 p1 += decpt;
132 /* if decpt was negative, we might be done for fcvt */
133 if (p1 < &buf[0]) {
134 buf[0] = '\0';
135 return (buf);
136 }
137 }
138
139 while (p <= p1 && p < &buf[NDIG]) {
140 arg *= 10;
141 arg = modf(arg, &fj);
142 *p++ = (int)fj + '0';
143 }
144
145 /* If we converted all the way to the end of the buf, don't mess with
146 * rounding since there's nothing significant out here anyway.
147 */
148 if (p1 >= &buf[NDIG]) {
149 buf[NDIG-1] = '\0';
150 return (buf);
151 }
152
153 /* Round by adding 5 to last digit and propagating carries. */
154 p = p1;
155 *p1 += 5;
156 while (*p1 > '9') {
157 *p1 = '0';
158 if (p1 > buf) {
159 ++*--p1;
160 } else {
161 *p1 = '1';
162 (*decptp)++;
163 if (eflag == 0) {
164 if (p > buf)
165 *p = '0';
166 p++;
167 }
168 }
169 }
170 *p = '\0';
171 return (buf);
172 }
173
174 static double L = 4503599627370496.0E0; /* 2**52 */
175
176 static int ecvt_init = 0;
177
178 /*
179 * FIXME: This deserves a comment if you turn this off!
180 * This used to #pragma CC_OPT_OFF.
181 * (Probably this was because the isNAN test was optimized away.)
182 * Why don't we just use the value of L given above?
183 */
184
185 static double
186 ecvt_rint(double x)
187 {
188 double s, t, one;
189
190 one = 1.0;
191
192 if (ecvt_init == 0) {
193 int i;
194 L = 1.0;
195 for (i = 52; i != 0; i--)
196 L *= 2.0;
197 ecvt_init = 1;
198 }
199 if (isNAN(x))
200 return (x);
201 if (ecvt_copysign(x, one) >= L) /* already an integer */
202 return (x);
203 s = ecvt_copysign(L, x);
204 t = x + s; /* x+s rounded to integer */
205 return (t - s);
206 }
207
208 /* Couldn't we use something like the following structure instead of the
209 hacky unsigned short pointer stuff?
210
211 struct double_format {
212 unsigned sign:1;
213 unsigned exponent:11;
214 unsigned hi_fraction:20;
215 unsigned lo_fraction:32;
216 };
217
218 */
219
220 #define msign ((unsigned short)0x7fff)
221 #define mexp ((unsigned short)0x7ff0)
222
223 static double
224 ecvt_copysign(double x, double y)
225 {
226 unsigned short *px, *py;
227
228 px = (unsigned short *)&x;
229 py = (unsigned short *)&y;
230 *px = (*px & msign) | (*py & ~msign);
231 return (x);
232 }
233
234 /*
235 * This used to #pragma CC_OPT_ON
236 */
237