|
|
1.1 root 1: /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
2:
3: /*
4: $Header: b1nuA.c,v 1.4 85/08/22 16:50:25 timo Exp $
5: */
6:
7: /* Approximate arithmetic */
8:
9: #include "b.h"
10: #include "b0con.h"
11: #include "b1obj.h"
12: #include "b1num.h"
13: #include "b3err.h" /* For still_ok */
14:
15: /*
16: For various reasons, on some machines (notably the VAX), the range
17: of the exponent is too small (ca. 1.7E38), and we cope with this by
18: adding a second word which holds the exponent.
19: However, on other machines (notably the IBM PC), the range is sufficient
20: (ca. 1E300), and here we try to save as much code as possible by not
21: doing our own exponent handling. (To be fair, we also don't check
22: certain error conditions, to save more code.)
23: The difference is made by #defining EXT_RANGE (in b1num.h), meaning we
24: have to EXTend the RANGE of the exponent.
25: */
26:
27: #ifdef EXT_RANGE
28: Hidden struct real app_0_buf = {Num, 1, -1, 0.0, -(double)Maxint};
29: /* Exponent must be less than any realistic exponent! */
30: #else !EXT_RANGE
31: Hidden struct real app_0_buf = {Num, 1, -1, 0.0};
32: #endif !EXT_RANGE
33:
34: Visible real app_0 = &app_0_buf;
35:
36: /*
37: * Build an approximate number.
38: */
39:
40: Visible real mk_approx(frac, expo) double frac, expo; {
41: real u;
42: #ifdef EXT_RANGE
43: expint e;
44: if (frac != 0) frac = frexp(frac, &e), expo += e;
45: if (frac == 0.5) frac = 1, --expo; /* Assert 0.5 < frac <= 1 */
46: if (frac == 0 || expo < -BIG) return (real) Copy(app_0);
47: if (expo > BIG) {
48: error(MESS(700, "approximate number too large"));
49: expo = BIG;
50: }
51: #else !EXT_RANGE
52: if (frac == 0.0) return (real) Copy(app_0);
53: frac= ldexp(frac, (int)expo);
54: #endif EXT_RANGE
55: u = (real) grab_num(-1);
56: Frac(u) = frac;
57: #ifdef EXT_RANGE
58: Expo(u) = expo;
59: #endif EXT_RANGE
60: return u;
61: }
62:
63: /*
64: * Approximate arithmetic.
65: */
66:
67: Visible real app_sum(u, v) real u, v; {
68: #ifdef EXT_RANGE
69: real w;
70: if (Expo(u) < Expo(v)) w = u, u = v, v = w;
71: if (Expo(v) - Expo(u) < Minexpo) return (real) Copy(u);
72: return mk_approx(Frac(u) + ldexp(Frac(v), (int)(Expo(v) - Expo(u))),
73: Expo(u));
74: #else !EXT_RANGE
75: return mk_approx(Frac(u) + Frac(v), 0.0);
76: #endif !EXT_RANGE
77: }
78:
79: Visible real app_diff(u, v) real u, v; {
80: #ifdef EXT_RANGE
81: real w;
82: int sign = 1;
83: if (Expo(u) < Expo(v)) w = u, u = v, v = w, sign = -1;
84: if (Expo(v) - Expo(u) < Minexpo)
85: return sign < 0 ? app_neg(u) : (real) Copy(u);
86: return mk_approx(
87: sign * (Frac(u) - ldexp(Frac(v), (int)(Expo(v) - Expo(u)))),
88: Expo(u));
89: #else !EXT_RANGE
90: return mk_approx(Frac(u) - Frac(v), 0.0);
91: #endif !EXT_RANGE
92: }
93:
94: Visible real app_neg(u) real u; {
95: return mk_approx(-Frac(u), Expo(u));
96: }
97:
98: Visible real app_prod(u, v) real u, v; {
99: return mk_approx(Frac(u) * Frac(v), Expo(u) + Expo(v));
100: }
101:
102: Visible real app_quot(u, v) real u, v; {
103: if (Frac(v) == 0.0) {
104: error(MESS(701, "in u/v, v is zero"));
105: return (real) Copy(u);
106: }
107: return mk_approx(Frac(u) / Frac(v), Expo(u) - Expo(v));
108: }
109:
110: /*
111: YIELD log"(frac, expo):
112: CHECK frac > 0
113: RETURN normalize"(expo*logtwo + log(frac), 0)
114: */
115:
116: Visible real app_log(v) real v; {
117: double frac = Frac(v), expo = Expo(v);
118: if (frac <= 0) {
119: error(MESS(702, "in log x, x is <= 0"));
120: return (real) Copy(app_0);
121: }
122: return mk_approx(expo*logtwo + log(frac), 0.0);
123: }
124:
125: /*
126: YIELD exp"(frac, expo):
127: IF expo < minexpo: RETURN zero"
128: WHILE expo < 0: PUT frac/2, expo+1 IN frac, expo
129: PUT exp frac IN f
130: PUT normalize"(f, 0) IN f, e
131: WHILE expo > 0:
132: PUT (f, e) prod" (f, e) IN f, e
133: PUT expo-1 IN expo
134: RETURN f, e
135: */
136:
137: Visible real app_exp(v) real v; {
138: #ifdef EXT_RANGE
139: expint ei;
140: double frac = Frac(v), expo = Expo(v), new_expo;
141: static double canexp;
142: if (!canexp) canexp = floor(log(log(Maxreal)) / logtwo);
143: if (expo <= canexp) {
144: if (expo < Minexpo) return mk_approx(1.0, 0.0);
145: frac = ldexp(frac, (int)expo);
146: expo = 0;
147: }
148: else if (expo >= Maxexpo) {
149: /* Definitely too big (the real boundary is much smaller
150: but here we are in danger of overflowing new_expo
151: in the loop below) */
152: return mk_approx(1.0, Maxreal); /* Force an error! */
153: }
154: else {
155: frac = ldexp(frac, (int)canexp);
156: expo -= canexp;
157: }
158: frac = exp(frac);
159: new_expo = 0;
160: while (expo > 0 && frac != 0) {
161: frac = frexp(frac, &ei);
162: new_expo += ei;
163: frac *= frac;
164: new_expo += new_expo;
165: --expo;
166: }
167: return mk_approx(frac, new_expo);
168: #else !EXT_RANGE
169: return mk_approx(exp(Frac(v)), 0.0);
170: #endif !EXT_RANGE
171: }
172:
173: /*
174: YIELD (frac, expo) power" v":
175: \ (f*2**e)**v =
176: \ = f**v * 2**(e*v) =
177: \ = f**v * 2**((e*v) mod 1) * 2**floor(e*v) .
178: PUT exp"(v" prod" normalize"(log frac, 0)) IN temp1" \ = f**v
179: PUT expo*numval(v") IN ev \ = e*v
180: PUT exp(logtwo * (ev - floor ev))) IN temp2 \ = 2**(ev mod 1)
181: PUT temp1" IN f, e
182: RETURN normalize"(f*temp2, e + floor ev)
183: */
184:
185: Visible real app_power(u, v) real u, v; {
186: double frac = Frac(u);
187: #ifdef EXT_RANGE
188: real logfrac, vlogfrac, result;
189: double expo = Expo(u), rest;
190: #endif !EXT_RANGE
191: if (frac <= 0) {
192: if (frac < 0) error(MESS(703, "in 0**v, v is negative"));
193: if (v == app_0) return mk_approx(1.0, 0.0); /* 0**0 = 1 */
194: return (real) Copy(app_0); /* 0**x = 0 */
195: }
196: #ifdef EXT_RANGE
197: frac *= 2, expo -= 1; /* Renormalize to 1 < frac <= 2, so log frac > 0 */
198: logfrac = mk_approx(log(frac), 0.0);
199: vlogfrac = app_prod(v, logfrac);
200: result = app_exp(vlogfrac);
201: /* But what if result overflows but expo is very negative??? */
202: if (still_ok) {
203: expo *= numval((value)v);
204: rest = expo - floor(expo);
205: frac = Frac(result) * exp(logtwo*rest);
206: expo = Expo(result) + floor(expo);
207: }
208: release((value)logfrac), release((value)vlogfrac), release((value)result);
209: return mk_approx(frac, expo);
210: #else !EXT_RANGE
211: return mk_approx(exp(log(frac) * Frac(v)), 0.0);
212: #endif !EXT_RANGE
213: }
214:
215: Visible int app_comp(u, v) real u, v; {
216: double xu, xv;
217: #ifdef EXT_RANGE
218: double eu, ev;
219: #endif EXT_RANGE
220: if (u == v) return 0;
221: xu = Frac(u), xv = Frac(v);
222: #ifdef EXT_RANGE
223: if (xu*xv > 0) {
224: eu = Expo(u), ev = Expo(v);
225: if (eu < ev) return xu < 0 ? 1 : -1;
226: if (eu > ev) return xu < 0 ? -1 : 1;
227: }
228: #endif EXT_RANGE
229: if (xu < xv) return -1;
230: if (xu > xv) return 1;
231: return 0;
232: }
233:
234: Visible value app_floor(u) real u; {
235: #ifdef EXT_RANGE
236: integer v, w;
237: value twotow, result;
238: if (Expo(u) <= Dblbits)
239: return (value)
240: mk_int(floor(ldexp(Frac(u),
241: (int)(Expo(u) < 0 ? -1 : Expo(u)))));
242: v = mk_int(ldexp(Frac(u), Dblbits));
243: w = mk_int(Expo(u) - Dblbits);
244: twotow = power((value)int_2, (value)w);
245: result = prod((value)v, twotow);
246: release((value) v), release((value) w), release(twotow);
247: if (!Integral(result)) syserr(MESS(704, "app_floor: result not integral"));
248: return result;
249: #else !EXT_RANGE
250: return (value) mk_int(floor(Frac(u)));
251: #endif !EXT_RANGE
252: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.