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