File indexing completed on 2024-05-12 05:55:11

0001 /* floatcommon.c: convenience functions, based on floatnum. */
0002 /*
0003     Copyright (C) 2007 - 2009 Wolf Lammen.
0004 
0005     This program is free software; you can redistribute it and/or modify
0006     it under the terms of the GNU General Public License as published by
0007     the Free Software Foundation; either version 2 of the License , or
0008     (at your option) any later version.
0009 
0010     This program is distributed in the hope that it will be useful,
0011     but WITHOUT ANY WARRANTY; without even the implied warranty of
0012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013     GNU General Public License for more details.
0014 
0015     You should have received a copy of the GNU General Public License
0016     along with this program; see the file COPYING.  If not, write to:
0017 
0018       The Free Software Foundation, Inc.
0019       59 Temple Place, Suite 330
0020       Boston, MA 02111-1307 USA.
0021 
0022 
0023     You may contact the author by:
0024        e-mail:  ookami1 <at> gmx <dot> de
0025        mail:  Wolf Lammen
0026               Oertzweg 45
0027               22307 Hamburg
0028               Germany
0029 
0030 *************************************************************************/
0031 
0032 #include "floatcommon.h"
0033 #include "floatconst.h"
0034 #include "floatlong.h"
0035 #include <string.h>
0036 #include <math.h>
0037 
0038 #define MSB (1 << (sizeof(unsigned)*8 - 1))
0039 #define LOGMSB ((301*(sizeof(unsigned)*8-1))/1000)
0040 
0041 static char
0042 _chckparam1(
0043   cfloatnum x,
0044   int digits,
0045   int limit,
0046   int specialval)
0047 {
0048   if (float_isnan(x))
0049   {
0050     float_seterror(NoOperand);
0051     return 0;
0052   }
0053   if ((digits <= 0 || digits > limit) && digits != specialval)
0054   {
0055     float_seterror(InvalidPrecision);
0056     return 0;
0057   }
0058   return 1;
0059 }
0060 
0061 static char
0062 _chckparam(
0063   floatnum x,
0064   int digits,
0065   int limit,
0066   int specialval)
0067 {
0068   if (!_chckparam1(x, digits, limit, specialval))
0069     return _setnan(x);
0070   return 1;
0071 }
0072 
0073 char
0074 chckmathparam(
0075   floatnum x,
0076   int digits)
0077 {
0078   return _chckparam(x, digits, MATHPRECISION, 1);
0079 }
0080 
0081 int
0082 logexp(
0083   cfloatnum x)
0084 {
0085   int expx, result;
0086 
0087   expx = float_getexponent(x);
0088   if (expx < 0)
0089     expx = -expx;
0090   result = -1;
0091   while (expx != 0)
0092   {
0093     expx <<= 1;
0094     ++result;
0095   }
0096   return result;
0097 }
0098 
0099 void
0100 float_setasciiz(
0101   floatnum x,
0102   const char* asciiz)
0103 {
0104   float_setscientific(x, asciiz, NULLTERMINATED);
0105 }
0106 
0107 char
0108 float_divi(
0109   floatnum quotient,
0110   cfloatnum dividend,
0111   int divisor,
0112   int digits)
0113 {
0114   floatstruct tmp;
0115   int result, expx;
0116 
0117   if (!_chckparam1(dividend, digits, maxdigits, INTQUOT))
0118     return _setnan(quotient);
0119   if (digits != INTQUOT && (divisor == 1 || divisor == -1))
0120     return float_muli(quotient, dividend, divisor, digits);
0121   if (divisor == 10 || divisor == -10)
0122   {
0123     expx = float_getexponent(dividend)-1;
0124     if (expx < -float_getrange() - 1)
0125       return _seterror(quotient, Underflow);
0126   }
0127   float_create(&tmp);
0128   float_setinteger(&tmp, divisor);
0129   result = float_div(quotient, dividend, &tmp, digits);
0130   float_free(&tmp);
0131   return result;
0132 }
0133 
0134 char
0135 float_addi(
0136   floatnum sum,
0137   cfloatnum summand1,
0138   int summand2,
0139   int digits)
0140 {
0141   floatstruct tmp;
0142   int result;
0143 
0144   if (!_chckparam1(summand1, digits, maxdigits, EXACT))
0145     return _setnan(sum);
0146   if (summand2 == 0)
0147     return float_copy(sum, summand1, digits);
0148   float_create(&tmp);
0149   float_setinteger(&tmp, summand2);
0150   result = float_add(sum, summand1, &tmp, digits);
0151   float_free(&tmp);
0152   return result;
0153 }
0154 
0155 char
0156 float_muli(
0157   floatnum product,
0158   cfloatnum factor1,
0159   int factor2,
0160   int digits)
0161 {
0162   floatstruct tmp;
0163   int result;
0164   int expx;
0165 
0166   if (!_chckparam1(factor1, digits, maxdigits, EXACT))
0167     return _setnan(product);
0168   switch(factor2)
0169   {
0170   case 0:
0171     return _setzero(product);
0172   case 1:
0173   case -1:
0174   case 10:
0175   case -10:
0176     expx = float_getexponent(factor1);
0177     if (factor2 != 1 && factor2 != -1
0178         && ++expx > float_getrange())
0179       return _seterror(product, Overflow);
0180     result = float_copy(product, factor1, digits);
0181     if (factor2 < 0)
0182       float_neg(product);
0183     float_setexponent(product, expx);
0184     return result;
0185   case 2:
0186   case -2:
0187     result = float_add(product, factor1, factor1, digits);
0188     if (factor2 < 0)
0189       float_neg(product);
0190     return result;
0191   }
0192   float_create(&tmp);
0193   float_setinteger(&tmp, factor2);
0194   result = float_mul(product, factor1, &tmp, digits);
0195   float_free(&tmp);
0196   return result;
0197 }
0198 
0199 int
0200 leadingdigits(
0201   cfloatnum x,
0202   int digits)
0203 {
0204   int i;
0205   unsigned tmp, ovfl;
0206   char buf[LOGMSB+1];
0207   const unsigned msb = MSB;
0208 
0209   if (digits <= 0 || digits > (int)LOGMSB+1 || float_isnan(x) || float_iszero(x))
0210     return 0;
0211   memset(buf, '0', digits);
0212   float_getsignificand(buf, digits, x);
0213   tmp = 0;
0214   for(i = 0; i < digits; ++i)
0215   {
0216     ovfl = 10;
0217     if (_longmul(&tmp, &ovfl))
0218     {
0219       ovfl = buf[i] - '0';
0220       _longadd(&tmp, &ovfl);
0221     }
0222     if (ovfl != 0)
0223       return 0;
0224   }
0225   if (float_getsign(x) < 0)
0226   {
0227     if (tmp > msb)
0228       return 0;
0229     if (tmp == msb)
0230       return (int)tmp;
0231     return -(int)tmp;
0232   }
0233   if (tmp >= msb)
0234     return 0;
0235   return (int)tmp;
0236 }
0237 
0238 int
0239 float_abscmp(
0240   floatnum x,
0241   floatnum y)
0242 {
0243   signed char sx, sy;
0244   int result;
0245 
0246   sx = float_getsign(x);
0247   sy = float_getsign(y);
0248   float_abs(x);
0249   float_abs(y);
0250   result = float_cmp(x, y);
0251   float_setsign(x, sx);
0252   float_setsign(y, sy);
0253   return result;
0254 }
0255 
0256 int
0257 float_relcmp(
0258   floatnum x,
0259   floatnum y,
0260   int digits)
0261 {
0262   /* do not simply use float_sub, because of overflow/underflow */
0263   floatstruct tmp;
0264   int result;
0265   int expx, expy, expdiff;
0266 
0267   result = float_cmp(x, y);
0268   if (result == 0 || float_getlength(x) == 0
0269      || float_getlength(y) == 0
0270      || float_getsign(x) != float_getsign(y))
0271     return result;
0272   expx = float_getexponent(x);
0273   expy = float_getexponent(y);
0274   expdiff = expx - expy;
0275   if (expdiff >= 2 || expdiff < -2)
0276     return result;
0277   float_create(&tmp);
0278   if (result > 0)
0279   float_setexponent(x, 0);
0280   float_setexponent(y, expy - expx);
0281   float_sub(&tmp, x, y, 2);
0282   if ((result * float_getsign(x)) > 0)
0283     float_div(&tmp, &tmp, x, 2);
0284   else
0285     float_div(&tmp, &tmp, y, 2);
0286   if (float_getexponent(&tmp) < -digits)
0287     result = 0;
0288   float_setexponent(x, expx);
0289   float_setexponent(y, expy);
0290   float_free(&tmp);
0291   return result;
0292 }
0293 
0294 char
0295 float_reciprocal(
0296   floatnum x,
0297   int digits)
0298 {
0299   return float_div(x, &c1, x, digits);
0300 }
0301 
0302 char
0303 float_isinteger(
0304   cfloatnum x)
0305 {
0306   return !float_isnan(x)
0307          && float_getlength(x) <= float_getexponent(x) + 1;
0308 }
0309 
0310 int
0311 float_asinteger(
0312   cfloatnum x)
0313 {
0314   return leadingdigits(x, float_getexponent(x)+1);
0315 }
0316 
0317 void
0318 float_checkedround(
0319   floatnum x,
0320   int digits)
0321 {
0322   floatstruct tmp;
0323   int saveerr;
0324 
0325   saveerr = float_geterror();
0326   float_create(&tmp);
0327   if (float_round(&tmp, x, digits, TONEAREST))
0328     float_move(x, &tmp);
0329   float_free(&tmp);
0330   float_geterror();
0331   float_seterror(saveerr);
0332 }
0333 
0334 void
0335 float_addexp(
0336   floatnum x,
0337   int smd)
0338 {
0339   float_setexponent(x, float_getexponent(x) + smd);
0340 }
0341 
0342 char
0343 float_isodd(
0344   floatnum x)
0345 {
0346   return (float_getdigit(x, float_getexponent(x)) & 1) != 0;
0347 }
0348 
0349 char
0350 float_roundtoint(
0351   floatnum x,
0352   roundmode mode)
0353 {
0354   signed char value = 0;
0355   signed char sign;
0356   char digit;
0357 
0358   if (float_isnan(x))
0359     return float_int(x); /* sets float_error */
0360   if (float_getexponent(x) >= 0)
0361     return float_round(x, x, float_getexponent(x) + 1, mode);
0362   sign = float_getsign(x);
0363   switch (mode)
0364   {
0365   case TONEAREST:
0366     digit = float_getdigit(x, 0);
0367     if (digit < 5 || (digit == 5 && float_getlength(x) == 1))
0368       value = 0;
0369     else
0370       value = sign;
0371     break;
0372   case TOINFINITY:
0373     value = sign;
0374     break;
0375   case TOPLUSINFINITY:
0376     value = sign > 0? 1 : 0;
0377     break;
0378   case TOMINUSINFINITY:
0379     value = sign > 0? 0 : -1;
0380     break;
0381   case TOZERO:
0382     value = 0;
0383     break;
0384   }
0385   switch (value)
0386   {
0387   case 0:
0388     float_setzero(x);
0389     break;
0390   case 1:
0391     float_copy(x, &c1, EXACT);
0392     break;
0393   case -1:
0394     float_copy(x, &cMinus1, EXACT);
0395     break;
0396   }
0397   return 1;
0398 }
0399 
0400 static float _ipwr(float x, int exp){
0401   int e = exp < 0? -exp : exp;
0402   double pwr = x;
0403   if ((e & 1) == 0)
0404     x = 1;
0405   while (e >>= 1){
0406     pwr *= pwr;
0407     if ((exp & 1) != 0)
0408       x *= pwr;
0409   }
0410   return exp < 0? 1/x : x;
0411 }
0412 
0413 /* returns x as a float. Only the first 6 digits
0414    contribute to the result. The exponent has to
0415    be in the valid range of a float */
0416 
0417 float float_asfloat(cfloatnum x){
0418   return leadingdigits(x, 6)/100000.0 * _ipwr(10, float_getexponent(x));
0419 }
0420 
0421 void float_setfloat(floatnum dest, float x){
0422   int exp = aprxlog10(x);
0423   // use two assignments to avoid overflow
0424   x *= _ipwr(10, -exp);
0425   x *= 100000000;
0426 
0427   float_setinteger(dest, (int)x);
0428   float_addexp(dest, exp - 8);
0429 }
0430 
0431 /* Somehow math.h cannot always be included with the full set of
0432    ISO C99 math functions enabled. So use the approximations below.
0433    These functions are used to get first guess start values for
0434    iterative algorithms, or to estimate round off errors, or to find
0435    the approximative size of a summand. They need not be
0436    accurate to more than, say, 0.1% */
0437 
0438 float aprxsqrt(float x){
0439   int exp, i;
0440   float x2 = 2 * frexp(x, &exp) - 1;
0441   float result = (0.5 - 0.125 * x2) * x2 + 1;
0442   x2 += 1;
0443   for (i = 0; ++i <= 2;)
0444     result = 0.5 * (result + x2 / result);
0445   if ((exp & 1) == 0)
0446     result *= M_SQRT2;
0447   return result * _ipwr(2, (exp - 1) >> 1);
0448 }
0449 
0450 float aprxln(float x){
0451   /* The evaluation of approxlog(x) is based
0452   on an approximation suggested by Abramowitz, Stegun,
0453   Handbook of mathematical functions.
0454   The returned logarithm is valid to
0455   5 (decimal) digits after the decimal point. */
0456   int exp;
0457 
0458   x = 2 * frexpf(fabs(x), &exp) - 1;
0459   return ((((0.03215845 * x
0460          - 0.13606275) * x
0461          + 0.28947478) * x
0462          - 0.49190896) * x
0463          + 0.99949556) * x
0464          + (exp - 1) * M_LN2;
0465 }
0466 
0467 float aprxlog2(float x){
0468   return aprxln(x) * M_LOG2E;
0469 }
0470 
0471 float aprxlog10(float x){
0472   return aprxln(x) * M_LOG10E;
0473 }
0474 
0475 float aprxlog10fn(cfloatnum x){
0476  return float_getexponent(x)
0477         + aprxlog10(leadingdigits(x, 5)) - 4;
0478 }
0479 
0480 float aprxlngamma(float x){
0481   return (x-0.5) * aprxln(x) - x + 0.9189385332f;
0482 }