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
1.5.1