1 /* floatcommon.c: convenience functions, based on floatnum. */
2 /*
3 Copyright (C) 2007 - 2009 Wolf Lammen.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License , or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING. If not, write to:
17
18 The Free Software Foundation, Inc.
19 59 Temple Place, Suite 330
20 Boston, MA 02111-1307 USA.
21
22
23 You may contact the author by:
24 e-mail: ookami1 <at> gmx <dot> de
25 mail: Wolf Lammen
26 Oertzweg 45
27 22307 Hamburg
28 Germany
29
30 *************************************************************************/
31
32 #include "floatcommon.h"
33 #include "floatconst.h"
34 #include "floatlong.h"
35 #include <string.h>
36 #include <math.h>
37
38 #define MSB (1 << (sizeof(unsigned)*8 - 1))
39 #define LOGMSB ((301*(sizeof(unsigned)*8-1))/1000)
40
41 static char
_chckparam1(cfloatnum x,int digits,int limit,int specialval)42 _chckparam1(
43 cfloatnum x,
44 int digits,
45 int limit,
46 int specialval)
47 {
48 if (float_isnan(x))
49 {
50 float_seterror(NoOperand);
51 return 0;
52 }
53 if ((digits <= 0 || digits > limit) && digits != specialval)
54 {
55 float_seterror(InvalidPrecision);
56 return 0;
57 }
58 return 1;
59 }
60
61 static char
_chckparam(floatnum x,int digits,int limit,int specialval)62 _chckparam(
63 floatnum x,
64 int digits,
65 int limit,
66 int specialval)
67 {
68 if (!_chckparam1(x, digits, limit, specialval))
69 return _setnan(x);
70 return 1;
71 }
72
73 char
chckmathparam(floatnum x,int digits)74 chckmathparam(
75 floatnum x,
76 int digits)
77 {
78 return _chckparam(x, digits, MATHPRECISION, 1);
79 }
80
81 int
logexp(cfloatnum x)82 logexp(
83 cfloatnum x)
84 {
85 int expx, result;
86
87 expx = float_getexponent(x);
88 if (expx < 0)
89 expx = -expx;
90 result = -1;
91 while (expx != 0)
92 {
93 expx <<= 1;
94 ++result;
95 }
96 return result;
97 }
98
99 void
float_setasciiz(floatnum x,const char * asciiz)100 float_setasciiz(
101 floatnum x,
102 const char* asciiz)
103 {
104 float_setscientific(x, asciiz, NULLTERMINATED);
105 }
106
107 char
float_divi(floatnum quotient,cfloatnum dividend,int divisor,int digits)108 float_divi(
109 floatnum quotient,
110 cfloatnum dividend,
111 int divisor,
112 int digits)
113 {
114 floatstruct tmp;
115 int result, expx;
116
117 if (!_chckparam1(dividend, digits, maxdigits, INTQUOT))
118 return _setnan(quotient);
119 if (digits != INTQUOT && (divisor == 1 || divisor == -1))
120 return float_muli(quotient, dividend, divisor, digits);
121 if (divisor == 10 || divisor == -10)
122 {
123 expx = float_getexponent(dividend)-1;
124 if (expx < -float_getrange() - 1)
125 return _seterror(quotient, Underflow);
126 }
127 float_create(&tmp);
128 float_setinteger(&tmp, divisor);
129 result = float_div(quotient, dividend, &tmp, digits);
130 float_free(&tmp);
131 return result;
132 }
133
134 char
float_addi(floatnum sum,cfloatnum summand1,int summand2,int digits)135 float_addi(
136 floatnum sum,
137 cfloatnum summand1,
138 int summand2,
139 int digits)
140 {
141 floatstruct tmp;
142 int result;
143
144 if (!_chckparam1(summand1, digits, maxdigits, EXACT))
145 return _setnan(sum);
146 if (summand2 == 0)
147 return float_copy(sum, summand1, digits);
148 float_create(&tmp);
149 float_setinteger(&tmp, summand2);
150 result = float_add(sum, summand1, &tmp, digits);
151 float_free(&tmp);
152 return result;
153 }
154
155 char
float_muli(floatnum product,cfloatnum factor1,int factor2,int digits)156 float_muli(
157 floatnum product,
158 cfloatnum factor1,
159 int factor2,
160 int digits)
161 {
162 floatstruct tmp;
163 int result;
164 int expx;
165
166 if (!_chckparam1(factor1, digits, maxdigits, EXACT))
167 return _setnan(product);
168 switch(factor2)
169 {
170 case 0:
171 return _setzero(product);
172 case 1:
173 case -1:
174 case 10:
175 case -10:
176 expx = float_getexponent(factor1);
177 if (factor2 != 1 && factor2 != -1
178 && ++expx > float_getrange())
179 return _seterror(product, Overflow);
180 result = float_copy(product, factor1, digits);
181 if (factor2 < 0)
182 float_neg(product);
183 float_setexponent(product, expx);
184 return result;
185 case 2:
186 case -2:
187 result = float_add(product, factor1, factor1, digits);
188 if (factor2 < 0)
189 float_neg(product);
190 return result;
191 }
192 float_create(&tmp);
193 float_setinteger(&tmp, factor2);
194 result = float_mul(product, factor1, &tmp, digits);
195 float_free(&tmp);
196 return result;
197 }
198
199 int
leadingdigits(cfloatnum x,int digits)200 leadingdigits(
201 cfloatnum x,
202 int digits)
203 {
204 int i;
205 unsigned tmp, ovfl;
206 char buf[LOGMSB+1];
207 const unsigned msb = MSB;
208
209 if (digits <= 0 || digits > (int)LOGMSB+1 || float_isnan(x) || float_iszero(x))
210 return 0;
211 memset(buf, '0', digits);
212 float_getsignificand(buf, digits, x);
213 tmp = 0;
214 for(i = 0; i < digits; ++i)
215 {
216 ovfl = 10;
217 if (_longmul(&tmp, &ovfl))
218 {
219 ovfl = buf[i] - '0';
220 _longadd(&tmp, &ovfl);
221 }
222 if (ovfl != 0)
223 return 0;
224 }
225 if (float_getsign(x) < 0)
226 {
227 if (tmp > msb)
228 return 0;
229 if (tmp == msb)
230 return (int)tmp;
231 return -(int)tmp;
232 }
233 if (tmp >= msb)
234 return 0;
235 return (int)tmp;
236 }
237
238 int
float_abscmp(floatnum x,floatnum y)239 float_abscmp(
240 floatnum x,
241 floatnum y)
242 {
243 signed char sx, sy;
244 int result;
245
246 sx = float_getsign(x);
247 sy = float_getsign(y);
248 float_abs(x);
249 float_abs(y);
250 result = float_cmp(x, y);
251 float_setsign(x, sx);
252 float_setsign(y, sy);
253 return result;
254 }
255
256 int
float_relcmp(floatnum x,floatnum y,int digits)257 float_relcmp(
258 floatnum x,
259 floatnum y,
260 int digits)
261 {
262 /* do not simply use float_sub, because of overflow/underflow */
263 floatstruct tmp;
264 int result;
265 int expx, expy, expdiff;
266
267 result = float_cmp(x, y);
268 if (result == 0 || float_getlength(x) == 0
269 || float_getlength(y) == 0
270 || float_getsign(x) != float_getsign(y))
271 return result;
272 expx = float_getexponent(x);
273 expy = float_getexponent(y);
274 expdiff = expx - expy;
275 if (expdiff >= 2 || expdiff < -2)
276 return result;
277 float_create(&tmp);
278 if (result > 0)
279 float_setexponent(x, 0);
280 float_setexponent(y, expy - expx);
281 float_sub(&tmp, x, y, 2);
282 if ((result * float_getsign(x)) > 0)
283 float_div(&tmp, &tmp, x, 2);
284 else
285 float_div(&tmp, &tmp, y, 2);
286 if (float_getexponent(&tmp) < -digits)
287 result = 0;
288 float_setexponent(x, expx);
289 float_setexponent(y, expy);
290 float_free(&tmp);
291 return result;
292 }
293
294 char
float_reciprocal(floatnum x,int digits)295 float_reciprocal(
296 floatnum x,
297 int digits)
298 {
299 return float_div(x, &c1, x, digits);
300 }
301
302 char
float_isinteger(cfloatnum x)303 float_isinteger(
304 cfloatnum x)
305 {
306 return !float_isnan(x)
307 && float_getlength(x) <= float_getexponent(x) + 1;
308 }
309
310 int
float_asinteger(cfloatnum x)311 float_asinteger(
312 cfloatnum x)
313 {
314 return leadingdigits(x, float_getexponent(x)+1);
315 }
316
317 void
float_checkedround(floatnum x,int digits)318 float_checkedround(
319 floatnum x,
320 int digits)
321 {
322 floatstruct tmp;
323 int saveerr;
324
325 saveerr = float_geterror();
326 float_create(&tmp);
327 if (float_round(&tmp, x, digits, TONEAREST))
328 float_move(x, &tmp);
329 float_free(&tmp);
330 float_geterror();
331 float_seterror(saveerr);
332 }
333
334 void
float_addexp(floatnum x,int smd)335 float_addexp(
336 floatnum x,
337 int smd)
338 {
339 float_setexponent(x, float_getexponent(x) + smd);
340 }
341
342 char
float_isodd(floatnum x)343 float_isodd(
344 floatnum x)
345 {
346 return (float_getdigit(x, float_getexponent(x)) & 1) != 0;
347 }
348
349 char
float_roundtoint(floatnum x,roundmode mode)350 float_roundtoint(
351 floatnum x,
352 roundmode mode)
353 {
354 signed char value = 0;
355 signed char sign;
356 char digit;
357
358 if (float_isnan(x))
359 return float_int(x); /* sets float_error */
360 if (float_getexponent(x) >= 0)
361 return float_round(x, x, float_getexponent(x) + 1, mode);
362 sign = float_getsign(x);
363 switch (mode)
364 {
365 case TONEAREST:
366 digit = float_getdigit(x, 0);
367 if (digit < 5 || (digit == 5 && float_getlength(x) == 1))
368 value = 0;
369 else
370 value = sign;
371 break;
372 case TOINFINITY:
373 value = sign;
374 break;
375 case TOPLUSINFINITY:
376 value = sign > 0? 1 : 0;
377 break;
378 case TOMINUSINFINITY:
379 value = sign > 0? 0 : -1;
380 break;
381 case TOZERO:
382 value = 0;
383 break;
384 }
385 switch (value)
386 {
387 case 0:
388 float_setzero(x);
389 break;
390 case 1:
391 float_copy(x, &c1, EXACT);
392 break;
393 case -1:
394 float_copy(x, &cMinus1, EXACT);
395 break;
396 }
397 return 1;
398 }
399
_ipwr(float x,int exp)400 static float _ipwr(float x, int exp){
401 int e = exp < 0? -exp : exp;
402 double pwr = x;
403 if ((e & 1) == 0)
404 x = 1;
405 while (e >>= 1){
406 pwr *= pwr;
407 if ((exp & 1) != 0)
408 x *= pwr;
409 }
410 return exp < 0? 1/x : x;
411 }
412
413 /* returns x as a float. Only the first 6 digits
414 contribute to the result. The exponent has to
415 be in the valid range of a float */
416
float_asfloat(cfloatnum x)417 float float_asfloat(cfloatnum x){
418 return leadingdigits(x, 6)/100000.0 * _ipwr(10, float_getexponent(x));
419 }
420
float_setfloat(floatnum dest,float x)421 void float_setfloat(floatnum dest, float x){
422 int exp = aprxlog10(x);
423 // use two assignments to avoid overflow
424 x *= _ipwr(10, -exp);
425 x *= 100000000;
426
427 float_setinteger(dest, (int)x);
428 float_addexp(dest, exp - 8);
429 }
430
431 /* Somehow math.h cannot always be included with the full set of
432 ISO C99 math functions enabled. So use the approximations below.
433 These functions are used to get first guess start values for
434 iterative algorithms, or to estimate round off errors, or to find
435 the approximative size of a summand. They need not be
436 accurate to more than, say, 0.1% */
437
aprxsqrt(float x)438 float aprxsqrt(float x){
439 int exp, i;
440 float x2 = 2 * frexp(x, &exp) - 1;
441 float result = (0.5 - 0.125 * x2) * x2 + 1;
442 x2 += 1;
443 for (i = 0; ++i <= 2;)
444 result = 0.5 * (result + x2 / result);
445 if ((exp & 1) == 0)
446 result *= M_SQRT2;
447 return result * _ipwr(2, (exp - 1) >> 1);
448 }
449
aprxln(float x)450 float aprxln(float x){
451 /* The evaluation of approxlog(x) is based
452 on an approximation suggested by Abramowitz, Stegun,
453 Handbook of mathematical functions.
454 The returned logarithm is valid to
455 5 (decimal) digits after the decimal point. */
456 int exp;
457
458 x = 2 * frexpf(fabs(x), &exp) - 1;
459 return ((((0.03215845 * x
460 - 0.13606275) * x
461 + 0.28947478) * x
462 - 0.49190896) * x
463 + 0.99949556) * x
464 + (exp - 1) * M_LN2;
465 }
466
aprxlog2(float x)467 float aprxlog2(float x){
468 return aprxln(x) * M_LOG2E;
469 }
470
aprxlog10(float x)471 float aprxlog10(float x){
472 return aprxln(x) * M_LOG10E;
473 }
474
aprxlog10fn(cfloatnum x)475 float aprxlog10fn(cfloatnum x){
476 return float_getexponent(x)
477 + aprxlog10(leadingdigits(x, 5)) - 4;
478 }
479
aprxlngamma(float x)480 float aprxlngamma(float x){
481 return (x-0.5) * aprxln(x) - x + 0.9189385332f;
482 }
483