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