getval.c

Go to the documentation of this file.
00001   /*     PURPOSE */
00002   /*        get a number : on output s must be a double float */
00003   /*                       which must be very close from the decimal */
00004   /*                       number represented by the pattern of char */
00005 
00006   /*        this subroutine is called by getsym when this last one */
00007   /*        have detected the beginning of a lexical token which corresponds */
00008   /*        to a positive number (an integer or a float). There are two */
00009   /*        cases (whom this routine is informed by the logical */
00010   /*        dotdet (as "dot detected")) : */
00011 
00012   /*          1/ the token begins with a digit d (in [0-9]) : in this case */
00013   /*             dotdet = .false. */
00014 
00015   /*          2/ the token begins with a point following by a digit .d : in */
00016   /*             this case  dotdet = .true. */
00017 
00018   /*        On entry, the global var char1 contains the first digit of the number */
00019   /*        Getting the next "char" is done by a call to getch (which put the */
00020   /*        next char is the global var char1). */
00021 
00022   /*     MOTIVATION */
00023   /*        Written by Bruno Pincon <Bruno.Pincon@iecn.u-nancy.fr> so as */
00024   /*        to replace the old string -> number method used by Scilab */
00025   /*        which was not accurate enough (even in some easy cases we can got */
00026   /*        the near float s (and the float gotten may be 3 or 4 floats after */
00027   /*        or before the optimal one) : in fact contrarily to the old method */
00028   /*        this subroutine computes actually the float s only when the string */
00029   /*        pattern is such that only one (hoped correctly) rounded float */
00030   /*        operation will be done in computing s, all others operations being */
00031   /*        exact (and so we are sure to get the nearer floating point number). */
00032   /*        In the others cases we call an "intrinsic" function of Fortran */
00033   /*        (as strtod in C) to do the job (what fortran called an internal file). */
00034   /*        The overhead comes from the fact that at this level the "scilab characters" */
00035   /*        are actually integers (a first convertion string -> integer is already */
00036   /*        done) so a "reconversion" to a string is necessary. */
00037 
00038   /*     A BRIEF EXPLANATION */
00039   /*        On an example, suppose that the "string" pattern is 1234.56789012345e+23  : */
00040 
00041   /*        1/ the mantissa is red and the digits are recorded inside the array */
00042   /*           digit = [1 2 3 4 5 6 7 8 9 0 1 2 3 4 5]  (only the first ndgmax */
00043   /*           digits of the mantissa are red) */
00044   /*           -> the dot is detected and a correction of -11 will be */
00045   /*              bring in the exponent */
00046   /*        2/ the exponent is computed directly in integer arithmetic, then the */
00047   /*           correction is brought to got the final exponent : 23-11 = 12 */
00048 
00049   /*        All that to say that the string number to convert is equal to */
00050   /*           123456789012345 * 10^12 and in general  x = integer 10^expo */
00051 
00052   /*        So if integer <= 2^53 (all integer n such that |n| <= 2^53 belong */
00053   /*        in the ieee754 double float number) then we must compute this integer */
00054   /*        (from the digit array) exactly in double precision (if all intermediary */
00055   /*        computed quantities are integers <= 2^53 which is the case). */
00056   /*        A simple way to impose this condition is the following : */
00057   /*            2^53 = 9007199254740992 > 8 10^15 > 10^15 */
00058   /*        So that if our integer has 15 digits (or 16 digits with d1 <= 8) then */
00059   /*        it is OK. */
00060 
00061   /*        For the exponent : 10^0, 10^1, 10^2, ...., 10^22 are all exactly representable */
00062   /*        in double ieee 754 (10^22 = 5^22 * 2^22  and 5^22=2384185791015625 < 2^53 */
00063   /*        but 5^23 > 2^53 so that 10^23 is not a float point number) */
00064 
00065   /*        Conclusion : */
00066   /*          (i) if our integer have less than 15 digits and if |expo|<= 22 */
00067   /*              then only one non exact operation (a multiplication or a division */
00068   /*              depending the sign of expo) will be done (eventually) and we */
00069   /*              got the near float ; */
00070   /*         (ii) one other trivial case are also considered (see explanation at */
00071   /*              the end of this file). */
00072   /*        (iii) If not we form a string as  123456789012345.d+12 and we call */
00073   /*              a fortran intrinsic routine to do the job (via internal file) */
00074 
00075   /*     A LAST REMARK : this routine doesn't change the syntax of tokens considered */
00076   /*        as numbers in Scilab, in particular 1.d- or 1.d+ are still valid (the exponent */
00077   /*        is taken as 0) */
00078   /*     PARAMETER */
00079   /*     LOCAL VAR */
00080   /*     detdot : a var to put the value of the argument dotdet */
00081   /*              (this is because at the call, dotdet is a constant */
00082   /*              (.true. or .false.) and in this subroutine detdot */
00083   /*              may change of value */
00084 
00085   /*     ndgmax : maximun number of recorded digits (=> when */
00086   /*              the mantissa have more than ndgmax digit, */
00087   /*              it may result a relative error of 10^(1-ndgmax) */
00088   /*              between the initial number and the number that */
00089   /*              this routine converts as a machine number */
00090   /*              (this last one may suffer of a relative error */
00091   /*              of epsm = (approx) 1.11 10^(-16))) */
00092   /*     digit  : array of length ndgmax to record the mantissa 's digits */
00093   /*     ndgrec : number of recorded digits (<= ndgmax) */
00094   /*     ndg    : to count the number of digits of the integer part of */
00095   /*              the mantissa (which may be superior to ndgmax => in */
00096   /*              this case a correction must be bring in the exponent) */
00097   /*     sgnexp : sign of the exponent part (see SYMBOL AFTER) */
00098   /*     expcor : correction to bring in the exponent (because all */
00099   /*              the mantissa begin an integer, p.e. 123.456 => 123456 */
00100   /*              in this case the correction is -3) */
00101   /*     ndgexp : number of digits of the exponent (to control spurious */
00102   /*              integer overflow if the exponent is something like */
00103   /*              e+2147483648  (=2^31  (= -2^31 with the usual 32 bits */
00104   /*              integer arithmetic ))) */
00105   /*     expo   : the exponent (directly computed with integer arithmetic */
00106   /*              but ndgexp may control integer "overflow") */
00107   /*     code0  : integer code of the character "0" */
00108   /*     string : string to hold the "number" to be converted in double */
00109   /*              when the "number" is such that a direct straitforward */
00110   /*              conversion will be not enough accurate */
00111   /*     toto   : a var to got an inf with 1/(toto-toto) */
00112   /*     CONSTANTS  (to adapt eventualy ...) */
00113   /*     EXPMAX may be such that 10^EXPMAX > max positive float num */
00114   /*     EXPMIN may be such that 10^EXPMIN < min positive float num */
00115   /*     NDEMAX may be such that 10^5 <= 10^NDEMAX < MAX_INTEGER : */
00116   /*            when we compute (with integer arithmetic) the exponent */
00117   /*            the number of digits of the exponent is recorded in */
00118   /*            ndgexp and the test  ndgexp <= NDEMAX validate this */
00119   /*            calculus. */
00120   /*     DGLIM  all integers with a number of digits <= DGLIM must be */
00121   /*            exactly representable as double float (DGLIM = 15 for */
00122   /*            ieee 754) */
00123   /*     EXPLIM all power of 10 up to EXPLIM (included) must be exactly */
00124   /*            representable as double float (EXPLIM = 22 for ieee 754) */
00125   /*     SOME CHAR SYMBOLS (scilab char are integers) */
00126 
00127 #include <string.h>
00128 #include <stdio.h>
00129 #include <math.h>
00130 #include <stdlib.h>
00131 #include "stack-c.h"
00132 #include "getval.h"
00133 
00134 /* Table of constant values */
00135 #define EXPMAX 309
00136 #define EXPMIN -324
00137 #define NDEMAX 7
00138 #define DGLIM 15
00139 #define EXPLIM 22
00140 
00141 /* Scilab character encoding*/
00142 #define dot   51
00143 #define plus  45
00144 #define minus 46
00145 #define D 13
00146 #define E 14
00147 
00148 extern int C2F(fortrangetch)();
00149 
00150 
00151 int C2F(getval)(double *s, int *dotdet)
00152 {
00153   /* Initialized constants */
00154   static double toto = 0.;
00155   static double c10 = 10.;
00156 
00157   /* Local variables */
00158   static int expo;
00159   static int code0;
00160   static int i, k;
00161   static int digit[25], ndgrec;
00162   static int detdot;
00163   static int ndgexp, expcor, sgnexp;
00164   static char string[31];
00165   static int ndg;
00166 
00167   /* System generated locals */
00168   static double d1;
00169   int i1;
00170 
00171   C2F(com).fin = 0;
00172   /*     beginning of the code */
00173   detdot = *dotdet;
00174   ndg = 0;
00175   ndgrec = 0;
00176   if (! detdot) {
00177     /*  1) got the integer part of the mantissa of the pattern
00178       1-a) may be there is some 0 at the beginning */
00179     while(C2F(com).char1 == 0) {
00180       C2F(fortrangetch)();
00181     }
00182     /* 1-b) now record the digits (inside the digit array) 
00183        (but we record a maximum of ndgmax digits)*/
00184     while(abs(C2F(com).char1) <= 9) {
00185       ++ndg;
00186       if (ndgrec < 25) {
00187         ++ndgrec;
00188         digit[ndgrec - 1] = C2F(com).char1;
00189       }
00190       C2F(fortrangetch)();
00191     }
00192     /*1-c) at this point we have detected something which is not a digit 
00193            may be a point, may be a d,D,e,E, or something else 
00194            here we only test for the dot and let the others cases
00195            to be treated after ... */
00196     if (abs(C2F(com).char1) == dot) {
00197       detdot = TRUE_;
00198       C2F(fortrangetch)();
00199     }
00200   }
00201   /*first correction for the (future) exponent : if the first part 
00202     of the string have more then ndgmax digits we have to add 
00203     ndg - ndgrec (else we have expcor=0) */
00204   expcor = ndg - ndgrec;
00205   if (detdot) {
00206     /*2) got the "fractionnal" part of the "mantissa" */
00207     if (ndgrec == 0) {
00208       /*we have not passed throw the part 1) or only zeros have been met
00209       and may be the number start with .000xxx : so clean up those 0 */
00210       while(C2F(com).char1 == 0) {
00211         --expcor;
00212         C2F(fortrangetch)();
00213       }
00214     }
00215     /*now we begin to record the digits */
00216     while(abs(C2F(com).char1) <= 9) {
00217       if (ndgrec < 25) {
00218         ++ndgrec;
00219         --expcor;
00220         digit[ndgrec - 1] = C2F(com).char1;
00221       }
00222       C2F(fortrangetch)();
00223     }
00224   }
00225   /*3) at this point the "mantissa" of the string decimal number
00226     must be recorded, now detect the exponent */
00227   expo = 0;
00228   ndgexp = 0;
00229   sgnexp = plus;
00230   if (abs(C2F(com).char1) == D || abs(C2F(com).char1) == E) {
00231     /*the string have an exponent part (which, in Scilab, may be empty or 
00232       may had only a sign ! => expo = 0) */
00233     C2F(fortrangetch)();
00234     if (C2F(com).char1 == minus || C2F(com).char1 == plus) {
00235       sgnexp = C2F(com).char1;
00236       C2F(fortrangetch)();
00237     } else {
00238       sgnexp = plus;
00239     }
00240     /*may be the exponent start by some 0 */
00241     while(C2F(com).char1 == 0) {
00242       C2F(fortrangetch)();
00243     }
00244     /*now form the exponent : the var ndgexp is here
00245       to treat spurious integer overflow ... */
00246     while(abs(C2F(com).char1) <= 9) {
00247       expo = expo * 10 + C2F(com).char1;
00248       ++ndgexp;
00249       C2F(fortrangetch)();
00250     }
00251   }
00252   /*4) Now we can form the double float number s
00253     4-1/ only zeros in the mantissa */
00254   if (ndgrec == 0) {
00255     /*no digits have been recorded : this is the case
00256       when the mantissa part is of the form [000][.][000] 
00257       the number is 0 */
00258     *s = 0.;
00259     return 0;
00260   }
00261   /*4-2/ ndgexp is to large => the exponent expo is perhaps badly 
00262     computed (integer "overflow") or in all cases the 
00263     exponent is too large (positive or negative) such that it result 
00264     (for s) in a overflow or underflow depending the exponent sign */
00265   if (ndgexp >= NDEMAX) {
00266     if (sgnexp == minus) {/*underflow */
00267       *s = 0.;
00268     } else {/*overflow : got an inf ... */
00269       *s = 1. / (toto - toto);
00270     }
00271     return 0;
00272   }
00273   /*4-3/ now build the final exponent */
00274   if (sgnexp == plus) {
00275     expo += expcor;
00276   } else {
00277     expo = -expo + expcor;
00278   }
00279   /*4-4/ here some tests to avoid unnecessary call to  "strtod"
00280     Now we have a number s of the form  d_1 d_2 ... d_ndgrec 10^expo
00281     which is equal to d_1 . d_2 ... d_ndgrec 10^(expo + ndgrec - 1) 
00282     with d_1 .ne. 0 
00283     so it comes :  s >= 10^(expo + ndgrec - 1)
00284     s <= 10^(expo + ndgrec) 
00285 
00286     Suppose given EXPMAX such that  10^EXPMAX > max positive float number 
00287     and EXPMIN such that  10^EXPMIN < min positive float number 
00288 
00289     then if  expo + ndgrec - 1 >= EXPMAX then overflow occurs necessarily 
00290     and  if  expo + ndgrec <= EXPMIN then underflow occurs 
00291 
00292     On IEEE 754 we have : max positive float num = (approx) 1.8E+308 
00293     min positive float num = (approx) 4.9EEXPMIN 
00294     (if denormalised number are used) 
00295 
00296     So that EXPMAX = 309 
00297     and  EXPMIN = -324  are OK (but larger limits are possible to take 
00298     into account others f.p. arithmetics) 
00299     Note that after the test (with these values) the exponent have a 
00300     maximum of 3 (decimals) digits */
00301   if (expo + ndgrec - 1 >= EXPMAX) {/*overflow : got an inf ... */
00302     *s = 1. / (toto - toto);
00303     return 0;
00304   }
00305   if (expo + ndgrec <= EXPMIN) { /*underflow : got an 0 */
00306     *s = 0.;
00307     return 0;
00308   }
00309   /*4-5/ Now the usual case where we can get the near floating point
00310     without any problem */
00311   if (ndgrec <= DGLIM && abs(expo) <= EXPLIM) {
00312     *s = 0.;
00313     i1 = ndgrec;
00314     for (i = 1; i <= i1; ++i) {
00315       *s = *s * 10. + digit[i - 1];
00316     }
00317     if (expo < 0) {
00318       d1 = -expo;
00319       *s /= pow(c10, d1);
00320     } else {
00321        d1 = expo;
00322       *s *= pow(c10, d1);
00323     }
00324     return 0;
00325   }
00326   /*4-6/ The other easy case where we can compute s : 
00327     if expo = EXPLIM + k  but [integer part]*10^k < max_int_coded_in_double 
00328     then it is OK (retrieve k in the exponent and multiply the integer 
00329     part by 10^k and do the same job as previus) */
00330   if (expo > EXPLIM && expo - EXPLIM + ndgrec <= DGLIM) {
00331     *s = 0.;
00332     i1 = ndgrec;
00333     for (i = 1; i <= i1; ++i) {
00334       *s = *s * 10. + digit[i - 1];
00335     }
00336     /*peut etre dangereux avec des options d'optimisation ? 
00337       (le compilo peut etre tente d'ecrire directement s = s*10**expo 
00338       ce qui detruit le truc ...) */
00339     /*         s = s*10.d0**(expo-EXPLIM)
00340                s = s*10.d0**EXPLIM*/
00341 
00342     d1 = (double)(expo - EXPLIM);
00343     *s *= pow(c10,d1);
00344     d1 = (double) EXPLIM;
00345     *s *= pow(c10, d1);
00346 
00347     return 0;
00348   }
00349   /*4-7/ else use langage routines to do the job
00350     the overhead is a retranslation into a string... */
00351   code0 = '0';
00352   i1 = ndgrec;
00353   for (i = 1; i <= i1; ++i) {
00354     *(unsigned char *)&string[i - 1] = (char) (digit[i - 1] + code0);
00355   }
00356   i1 = ndgrec;
00357   if (expo < 0) {
00358     sprintf(string+i1,".e-%d",abs(expo));
00359   } else {
00360     sprintf(string+i1,".e+%d",abs(expo));
00361   }
00362   k = ndgrec + 4;
00363   *s=strtod(string,NULL);
00364   return 0;
00365 }
00366 

Generated on Sun Mar 4 15:03:46 2007 for Scilab [trunk] by  doxygen 1.5.1