]>
Commit | Line | Data |
---|---|---|
e9ce8d39 A |
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 |