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: }