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