bringing SDL 1.2.14 from vendor into the main branch
[AROS-Contrib.git] / regina / strmath.c
blob8949ea8bc9dbe7d02a87051ebd473cae7c25953e
1 #ifndef lint
2 static char *RCSid = "$Id$";
3 #endif
5 /*
6 * The Regina Rexx Interpreter
7 * Copyright (C) 1992-1994 Anders Christensen <anders@pvv.unit.no>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the Free
21 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "rexx.h"
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <assert.h>
28 #include <string.h>
31 #define log_xor(a,b) (( (a)&&(!(b)) ) || ( (!(a)) && (b) ))
32 #if !defined(MAX)
33 # define MAX(a,b) ((a>b)?(a):(b))
34 #endif
35 #if !defined(MIN)
36 # define MIN(a,b) ((a<b)?(a):(b))
37 #endif
38 #define IS_AT_LEAST(ptr,now,min) \
39 if (now<min) { if (ptr) FreeTSD(ptr); ptr=MallocTSD(now=min) ; } ;
42 /* hmmm perhaps it should be rebuilt, so that 0 gave: size=0 and num
43 and exp undefined, that would be most logical ... */
46 #define MAX_EXPONENT (999999999)
48 typedef struct { /* mat_tsd: static variables of this module (thread-safe) */
49 #ifdef TRACEMEM
50 void * outptr1;
51 void * outptr2;
52 void * outptr3;
53 void * outptr4;
54 void * outptr5;
55 #endif
57 num_descr edescr;
58 num_descr fdescr;
59 num_descr rdescr;
60 num_descr sdescr;
64 int ClassicFormat; /* For testing purpose it is declared here and is
65 * equivalent to NotJ18 .
67 int OldRegina; /* For testing purpose only */
69 int add_outsize; /* This values MAY all become one. CHECK THIS! */
70 char * add_out;
71 int norm_outsize;
72 char * norm_out;
73 int div_outsize;
74 char * div_out;
75 int mul_outsize;
76 char * mul_out;
77 } mat_tsd_t; /* thread-specific but only needed by this module. see
78 * init_math
81 /* init_math initializes the module.
82 * Currently, we set up the thread specific data and check for environment
83 * variables to change debugging behaviour.
84 * The function returns 1 on success, 0 if memory is short.
86 int init_math( tsd_t *TSD )
88 mat_tsd_t *mt;
90 if (TSD->mat_tsd != NULL)
91 return(1);
93 if ((mt = TSD->mat_tsd = MallocTSD(sizeof(mat_tsd_t))) == NULL)
94 return(0);
95 memset(mt,0,sizeof(mat_tsd_t));
97 if (getenv("OLD_REGINA") != NULL)
98 mt->OldRegina = 1;
99 if (getenv("CLASSIC_REGINA") != NULL)
100 mt->ClassicFormat = 1;
101 return(1);
104 #ifdef TRACEMEM
105 void mark_descrs( const tsd_t *TSD )
107 mat_tsd_t *mt;
109 mt = TSD->mat_tsd;
110 if (mt->rdescr.num) markmemory( mt->rdescr.num, TRC_MATH ) ;
111 if (mt->sdescr.num) markmemory( mt->sdescr.num, TRC_MATH ) ;
112 if (mt->fdescr.num) markmemory( mt->fdescr.num, TRC_MATH ) ;
113 if (mt->edescr.num) markmemory( mt->edescr.num, TRC_MATH ) ;
115 if (mt->outptr1) markmemory( mt->outptr1, TRC_MATH ) ;
116 if (mt->outptr2) markmemory( mt->outptr2, TRC_MATH ) ;
117 if (mt->outptr3) markmemory( mt->outptr3, TRC_MATH ) ;
118 if (mt->outptr4) markmemory( mt->outptr4, TRC_MATH ) ;
119 if (mt->outptr5) markmemory( mt->outptr5, TRC_MATH ) ;
121 #endif /* TRACEMEM */
124 int descr_to_int( const num_descr *input )
126 /* number must be integer, and must be small enough */
127 int result=0, i=0 ;
129 if (input->size<input->exp)
130 exiterror( ERR_INVALID_INTEGER, 0 ) ;
131 else if (input->size>input->exp)
133 i = MAX( 0, input->exp ) ;
134 for (; i<input->size; i++)
136 if (input->num[i]!='0')
137 exiterror( ERR_INVALID_INTEGER, 0 ) ;
142 for (i=0; i<input->size; i++)
144 result = result*10 + (input->num[i] - '0') ;
146 if (input->negative)
147 result = -result ;
149 return result ;
152 void str_strip( num_descr *num )
154 int i=0, j=0 ;
156 if (num->size==1)
158 if (num->num[0] == '0')
160 num->negative = 0 ;
161 num->exp = 1 ;
163 return ;
166 for (i=0; i<num->size-1 && num->num[i]=='0'; i++ ) ;
167 if (i)
169 for (j=0; j<(num->size-i); j++)
171 num->num[j] = num->num[j+i] ;
174 num->exp -= i ;
175 num->size -= i ;
176 assert( num->size > 0 ) ;
179 if ((num->size==1) && (num->num[0]=='0'))
181 num->negative = 0 ;
182 num->exp = 1 ;
187 #if 1
188 int getdescr( const tsd_t *TSD, const streng *num, num_descr *descr )
189 /* converts num into a descr and returns 0 if successfully.
190 * returns 1 in case of an error. descr contains nonsense in this case.
191 * The newly generated descr is as short as possible: leading and
192 * trailing zeros (after a period) will be cut, rounding occurs.
193 * We don't use registers and hope the compiler does it better than outselves
194 * in the optimization stage, else try in this order: c, inlen, in, out, exp.
197 const char *in; /* num->value */
198 int inlen; /* chars left in "in" */
199 char *out; /* descr->num */
200 int outpos; /* position where to write */
201 int outmax; /* descr->max */
202 char c, /* tmp var */
203 lastdigit = 0; /* last digit seen for mantissa, init: error */
204 int pointseen, /* point in mantissa seen? */
205 exp, /* exp from mantissa */
206 exp2, /* exp from "1E1" */
207 expminus; /* exp in "1E-1" is negative? */
209 IS_AT_LEAST( descr->num, descr->max, TSD->currlevel->currnumsize+1 ) ;
211 in = num->value;
212 inlen = Str_len(num);
213 /* skip leading spaces */
214 while (inlen && isspace(*in))
216 in++;
217 inlen--;
220 if (!inlen)
221 return 1 ;
223 c = *in;
225 /* check sign */
226 if ((c == '-') || (c == '+'))
228 descr->negative = (c == '-') ;
229 in++; /* c eaten */
230 inlen--;
231 while (inlen && isspace(*in)) /* skip leading spaces */
233 in++;
234 inlen--;
237 if (!inlen)
238 return 1 ;
240 else
241 descr->negative = 0 ;
243 /* cut ending blanks first, a non blank exists (in[0]) at this point */
244 while (isspace(in[inlen-1]))
245 inlen--;
247 while (inlen && (*in == '0')) /* skip leading zeros */
249 in++;
250 inlen--;
251 lastdigit = '0';
253 if (!inlen)
254 { /* Fast breakout in case of a plain "0" or an error */
256 descr->num[0] = lastdigit;
257 descr->exp = 1;
258 descr->size = 1;
259 if (lastdigit == '0')
261 descr->negative = 0;
262 return 0 ;
264 return 1 ;
267 /* Transfer digits and check for points */
268 pointseen = 0; /* never seen */
269 exp = 0;
270 out = descr->num;
271 outmax = TSD->currlevel->currnumsize+1;
272 outpos = 0;
273 while (inlen)
275 if ((c = *in) == '.')
277 if (pointseen)
278 return 1 ;
279 pointseen = 1;
280 in++;
281 inlen--;
282 continue;
284 if (!isdigit(c))
285 break;
286 if (outpos < outmax)
288 lastdigit = c;
289 if ((c=='0') && (outpos==0)) /* skip zeros in "0.0001" */
290 exp--; /* We must be after a point, see zero parsing above */
291 else
293 out[outpos++] = c;
294 if (!pointseen)
295 exp++;
298 else
300 lastdigit = '0';
301 if (!pointseen)
302 exp++;
304 in++;
305 inlen--;
307 /* the mantissa is correct now, check for ugly "0.0000" later */
308 if (inlen)
310 /* c is *in at this point, see above */
311 expminus = 0;
312 if ((c != 'e') && (c != 'E'))
313 return 1 ;
314 if (--inlen == 0) /* at least one digit must follow */
315 return 1 ;
316 in++;
318 c = *in;
319 if ((c == '+') || (c == '-'))
321 if (c == '-')
322 expminus = 1;
323 if (--inlen == 0) /* at least one digit must follow */
324 return 1 ;
325 in++;
327 exp2 = 0;
328 while (inlen--)
330 c = *in++;
331 if (!isdigit(c))
332 return 1 ;
333 exp2 = exp2*10 + (c - '0'); /* Hmm, no overflow checking? */
335 if (expminus)
336 exp -= exp2;
337 else
338 exp += exp2;
340 if (outpos == 0) /* no digit or 0.000 with or without exp */
342 if (!lastdigit)
343 return 1 ;
344 out[outpos++] = '0';
345 exp = 1;
346 descr->negative = 0;
348 descr->exp = exp;
349 descr->size = outpos;
350 assert(descr->size <= TSD->currlevel->currnumsize+1);
351 return(0);
353 #else
354 int getdescr( const tsd_t *TSD, const streng *num, num_descr *descr )
356 register unsigned char *i=NULL, *k=NULL, *top=NULL, *start=NULL ;
357 unsigned char *ktop=NULL, ch=' ' ;
358 register int kextra=0, skipped=0 ;
359 register int skipzeros=0, decipoint=0, found_digits=0 ;
361 IS_AT_LEAST( descr->num, descr->max, TSD->currlevel->currnumsize+1 ) ;
363 /* skip leading whitespace */
364 i = (unsigned char *)num->value ;
365 top = i + Str_len( num ) ;
366 for ( ; (i<top && isspace(ch=*i)); i++ ) ; /* FGC: ordered */
368 /* set the sign, and skip more whitespace */
369 descr->negative = 0 ;
370 if (((ch == '-') || (ch == '+')) && i<top)
372 descr->negative = (*(i++) == '-') ;
373 for ( ; (i<top) && (isspace(*i)); i++ ) ;
376 /* This is slightly confusing ... but the conventions are:
378 * decipoint - number of leading digits in descr->num that are in
379 * front of the decimal point, if any.
380 * skipzeros - number of leading zeros in the output, _after_ the
381 * decimal point, which has been skipped.
382 * examples:
383 * 00.0004 -> decipoint=0 skipzeros=3
384 * 123.456 -> decipoint=3 skipzeros=0
385 * 0004000 -> decipoint=-1 skipzeros=-1
388 /* read all digits in number */
389 start = i ;
390 kextra = 0 ;
391 skipped = 0 ;
392 skipzeros = -1 ;
393 decipoint = -1 ;
394 found_digits = 0 ;
395 k = (unsigned char*)descr->num ;
396 ktop = k + TSD->currlevel->currnumsize + 1 ;
397 for (; i<top; i++ )
399 if ( isdigit( ch=*i ) )
401 if (skipzeros<0)
403 found_digits = 1 ;
404 if (ch=='0')
406 if (decipoint>=0) skipped++ ;
407 continue ;
409 else
411 assert( decipoint <= 0 ) ;
412 skipzeros = skipped ;
416 if (k < ktop)
417 *(k++) = ch ;
418 else
419 kextra++ ;
421 else if (ch=='.')
423 if (decipoint!=(-1))
424 return 1 ;
426 decipoint = (int)( (char*)k - (char*)descr->num ) ;
428 else
429 break ;
432 descr->exp = 0 ;
433 if ((i<top) && ((ch=='e') || (ch=='E')))
435 int sign=0 ;
436 unsigned char *tmp ;
438 if ((*(++i) == '-') || (*i == '+'))
439 sign = (*(i++) == '-') ;
441 for (tmp=i; (i<top) && (isdigit(*i)); i++ )
442 descr->exp = descr->exp * 10 + (*i - '0') ;
444 if (tmp==i) return 1 ;
446 if (sign)
447 descr->exp = - descr->exp ;
450 /* If we didn't find any non-zero digits */
451 descr->size = (int)( (char*)k - (char*)descr->num ) ;
452 if (skipzeros<0)
454 if (!found_digits)
455 return 1 ;
457 descr->exp += 1 - skipped ;
458 *(k++) = '0' ;
459 descr->size++ ;
461 else if (decipoint<0)
462 descr->exp += descr->size + kextra ;
463 else
464 descr->exp += decipoint - skipzeros ;
466 /* check for non-white-space at the end */
467 for (; i<top; i++ )
468 if (!isspace(*i))
469 return 1 ;
471 assert( descr->size <= TSD->currlevel->currnumsize+1 ) ;
472 return 0 ;
474 #endif
476 void str_round( num_descr *descr, int size )
478 int i=0 ;
480 /* we can't round to zero digits */
481 if (size==0)
483 if (descr->num[0]>='5')
485 descr->num[0] = '1' ;
486 descr->exp++ ;
487 descr->size = 1 ;
489 else
491 descr->num[0] = '0' ;
492 descr->size = 1 ;
493 descr->negative = descr->exp = 0 ;
495 return ;
497 else if (size<0)
499 descr->num[0] = '0' ;
500 descr->size = 1 ;
501 descr->exp = descr->negative = 0 ;
502 return ;
505 /* increment size by the number of leading zeros existing */
506 for (i=0; i<descr->size && descr->num[i]=='0'; i++) ;
507 size += i ;
509 /* do we have to round? */
510 if (descr->size<=size)
511 return ;
513 /* set the size to the wanted value */
514 descr->size = size ;
516 /* is it possible just to truncate? */
517 if (descr->num[size] < '5')
518 return ;
520 /* increment next digit, and loop if that is a '9' */
521 for (i=size-1;;)
523 /* can we get away with inc'ing this digit? */
524 if (descr->num[i] != '9')
526 descr->num[i]++ ;
527 return ;
530 /* no, set it to zero, and inc' next digit */
531 descr->num[i--] = '0' ;
533 /* if there are no more digits, move number one magnitude up */
534 if (i==(-1))
536 #ifndef NDEBUG
537 /* Just check a few things ... I don't like surprises */
538 for (i=0; i<size; i++)
539 assert( descr->num[i] == '0' ) ;
540 #endif
542 /* increase order of magnitude, and set first digit */
543 descr->exp++ ;
544 descr->num[0] = '1' ;
545 return ;
552 void descr_copy( const tsd_t *TSD, const num_descr *f, num_descr *s )
555 * Check for the special case that these are identical, then we don't
556 * have to do any copying, so just return.
558 if (f==s)
559 return ;
561 s->negative = f->negative ;
562 s->exp = f->exp ;
563 s->size = f->size ;
565 IS_AT_LEAST( s->num, s->max, f->size ) ;
566 memcpy( s->num, f->num, f->size ) ;
574 * So, why don't we just flush the changes into the result string
575 * directly, without temporarily storing it in the out string? Well,
576 * the answer is that if this function is called like:
578 * string_add( TSD, &descr1, &descr2, &descr1 )
580 * then it should be able to produce the correct answer, which is
581 * impossible to do without a temporary storage. (Hmmm. No, that is
582 * bogos, it just takes a bit of care to not overwrite anything that
583 * we might need. Must be rewritten). Another problem, if the result
584 * string is to small to hold the answer, we must reallocate space
585 * so we might have to live with the out anyway.
587 void string_add( const tsd_t *TSD, const num_descr *f, const num_descr *s, num_descr *r )
589 int count1=0, carry=0, tmp=0, sum=0, neg=0 ;
590 int lsd=0 ; /* least significant digit */
591 int msd=0, loan=0, ccns=0 ;
592 int flog=0, fexp=0, fsize=0, slog=0, ssize=0, sexp=0, sdiff=0, fdiff=0 ;
593 char *fnum=NULL, *snum=NULL ;
594 mat_tsd_t *mt;
596 mt = TSD->mat_tsd;
598 fexp = f->exp ;
599 fsize = f->size ;
600 sexp = s->exp ;
601 ssize = s->size ;
602 flog = f->negative & !s->negative;
603 slog = s->negative & !f->negative;
604 sdiff = sexp - ssize ;
605 fdiff = fexp - fsize ;
606 fnum = f->num ;
607 snum = s->num ;
610 * Make sure that we have enough space for the internal use.
612 ccns = TSD->currlevel->currnumsize ;
613 IS_AT_LEAST( mt->add_out, mt->add_outsize, ccns+2 ) ;
614 #ifdef TRACEMEM
615 mt->outptr5 = mt->add_out ;
616 #endif
619 * If *s is zero compared to *f under NUMERIC DIGITS, set it to zero
620 * This also applies if *s is zero. TRL says that in that case, the
621 * other number is to be returned.
623 if ((ssize==1)&&(snum[0]=='0'))
625 descr_copy( TSD, f, r ) ;
626 return ;
630 * And do the same thing for *f
632 if (( fsize==1)&&(fnum[0]=='0'))
634 descr_copy( TSD, s, r ) ;
635 return ;
638 if (sexp > fexp)
640 if (sexp > fexp + ccns)
642 descr_copy( TSD, s, r ) ;
643 return ;
646 else
648 if (fexp > sexp + ccns)
650 descr_copy( TSD, f, r ) ;
651 return ;
656 * Find the exponent number for the most significant digit and the
657 * least significant digit. 'size' is the size of the result, minus
658 * any extra carry. 'count1' is the loop variable that iterates
659 * through each digit.
661 * These initializations may look a bit complex, so there is a
662 * description of what they really means, consider the following
663 * addition:
665 * xxxxx.xx
666 * yy.yyyy
668 * The 'lsd' is the fourth digit after the decimal point, and is
669 * therefore set to -3. The 'msd' is the fifth digit before the
670 * decimal point, and is therefore set to 5. The size is set to
671 * the difference between them, that is 8.
672 * The 'carry' and 'loan' are initially
673 * cleared.
675 * Special consideration is taken, so that 'lsd' will never be more
676 * so small that the difference between them are bigger than the
677 * current precision.
679 msd = MAX( fexp, sexp ) ;
680 lsd = MAX( msd-(TSD->currlevel->currnumsize+1), MIN( fdiff, sdiff));
681 carry = loan = 0 ;
684 * Loop through the numbers, from the 'lsd' to the 'msd', letting
685 * 'count1' have the value of the current digit.
688 #ifdef CHECK_MEMORY
689 /* The faster (and correct) algorithm uses fnum- and snum-pointers which
690 are initially set to perhaps illegal values. They become valid by
691 an offset. This isn't correctly understood by the bounds checker.
692 We use valid base pointers and a complex index here. See below for
693 the faster code. WARNING: Changes should be done both here and in the
694 '#else' - statement. FGC
696 for (count1=lsd; count1<msd; count1++ )
699 * The variable 'sum' collects the sum for the addition of the
700 * current digit. This is done, in five steps. First, register
701 * any old value stored in 'carry' or 'loan'.
703 sum = carry - loan ;
706 * Then, for each of the two numbers, add its digit to 'sum'.
707 * There are two considerations to be taken. First, are we
708 * within the range of that number. Then what are the sign of
709 * the number. The expression of the if statement checks for
710 * the validity of the range, and the contents of the if
711 * statement adds the digit to 'sum' taking note of the sign.
713 if (count1>=fdiff && fexp>count1)
715 tmp = fnum[fexp - 1 - count1] - '0';
716 if (flog)
717 sum -= tmp ;
718 else
719 sum += tmp ;
721 /* else
722 fdiff = msd ;
725 * Repeat previous step for the second number
727 if (count1>=sdiff && sexp>count1)
729 tmp = snum[sexp - 1 - count1] - '0';
730 if (slog)
731 sum -= tmp ;
732 else
733 sum += tmp ;
735 /* else
736 sdiff = msd ; */
739 * If the sum is more than 9, we have a carry, then set 'carry'
740 * and subtract 10. And similar, if the sum is less than 0,
741 * set 'loan' and add 10.
743 if ((carry = ( sum > 9 )))
744 sum -= 10 ;
746 if ((loan = ( sum < 0 )))
747 sum += 10 ;
750 * Flush the resulting digit to the output string.
752 mt->add_out[ msd - count1 ] = (char) (sum + '0');
754 #else
755 fnum += fexp - 1 ;
756 snum += sexp - 1 ;
757 for (count1=lsd; count1<msd; count1++ )
760 * The variable 'sum' collects the sum for the addition of the
761 * current digit. This is done, in five steps. First, register
762 * any old value stored in 'carry' or 'loan'.
764 sum = carry - loan ;
767 * Then, for each of the two numbers, add its digit to 'sum'.
768 * There are two considerations to be taken. First, are we
769 * within the range of that number. Then what are the sign of
770 * the number. The expression of the if statement checks for
771 * the validity of the range, and the contents of the if
772 * statement adds the digit to 'sum' taking note of the sign.
774 if (count1>=fdiff && fexp>count1)
776 tmp = fnum[-count1] - '0';
777 if (flog)
778 sum -= tmp ;
779 else
780 sum += tmp ;
782 /* else
783 fdiff = msd ;
786 * Repeat previous step for the second number
788 if (count1>=sdiff && sexp>count1)
790 tmp = snum[-count1] - '0';
791 if (slog)
792 sum -= tmp ;
793 else
794 sum += tmp ;
796 /* else
797 sdiff = msd ; */
800 * If the sum is more than 9, we have a carry, then set 'carry'
801 * and subtract 10. And similar, if the sum is less than 0,
802 * set 'loan' and add 10.
804 if ((carry = ( sum > 9 )) != 0)
805 sum -= 10 ;
807 if ((loan = ( sum < 0 )) != 0)
808 sum += 10 ;
811 * Flush the resulting digit to the output string.
813 mt->add_out[ msd - count1 ] = (char) (sum + '0');
815 #endif
817 neg = ( f->negative && s->negative ) ;
818 IS_AT_LEAST( r->num, r->max, /*ccns+2*/ msd-lsd+3 ) ;
820 fnum = r->num ;
821 if ( carry )
823 *(fnum++) = '1' ;
825 else if ( loan )
827 int i ;
828 assert( neg==0 ) ;
829 neg = 1 ;
830 mt->add_out[0] = '0' ;
831 sum = 10 ;
832 for ( i=msd-lsd; i>0; i-- )
834 if ((mt->add_out[i] = (char) (sum - (mt->add_out[i]-'0') + '0')) > '9')
836 mt->add_out[i] = '0' ;
837 sum = 10 ;
839 else
840 sum = 9 ;
842 snum = mt->add_out ;
843 msd-- ;
845 else
847 msd-- ;
850 r->negative = neg ;
851 r->exp = msd + 1 ;
852 r->size = r->exp - lsd ;
854 #if 1
855 memcpy( fnum, mt->add_out+1, r->size - ( (carry) ? 1 : 0 ) ) ;
856 #else
857 memcpy( fnum, mt->add_out+1, r->size ) ;
858 #endif
859 str_strip( r ) ;
860 /* for (; count1<fsize; count1++)
861 fnum[count1] = mt->add_out[count1] ;
866 streng *str_format_orig( const tsd_t *TSD, const streng *input, int before, int after, int expp, int expt )
868 static char *out=NULL ;
869 static int outsize=0 ;
870 int sdigs=0, trigger=0, j=0, size=0, k=0 ;
871 int decim, use_exp=0, use_eng=0 ;
872 char *tmp_ptr=NULL ;
873 streng *result=NULL ;
875 char *in_ptr ; /* ptr to current char in input string */
876 char *in_end ; /* ptr to end+1 in input string */
877 int exponent ; /* the value of the exponent */
878 char *out_ptr ; /* ptr to current char in output string */
879 mat_tsd_t *mt;
881 mt = TSD->mat_tsd;
883 if (getdescr(TSD,input,&mt->fdescr))
884 exiterror( ERR_BAD_ARITHMETIC, 0 ) ;
886 str_round( &mt->fdescr, TSD->currlevel->currnumsize ) ;
889 * Make sure that we have enough space available. If we have memory
890 * tracing enabled, remember to save the info about the new memory
891 * allocated.
893 IS_AT_LEAST( out, outsize, 3*TSD->currlevel->currnumsize+5 ) ;
894 #ifdef TRACEMEM
895 mt->outptr1 = out ;
896 #endif
898 new_round:
900 * Str_ip leading zeros from the descriptor. We could have done this
901 * by calling strip(), but it is faster to do it here. Besides doing
902 * it this way shortcuts the need to shift (i.e copy) the digits
904 str_strip( &mt->fdescr ) ;
905 exponent = mt->fdescr.exp ;
906 in_end = &mt->fdescr.num[mt->fdescr.size] ;
907 in_ptr = mt->fdescr.num ;
910 * Now, let us see if we've got a zero. That one is a special case,
911 * and then just return a zero, no more, no less
914 if (in_ptr==in_end)
915 return Str_creTSD( "0" ) ;
918 * There are certain limits that the .exp must be within, the 32
919 * bit integer can hold slightly larger numbers, so if we are outside
920 * the legal range, report an error.
922 if ((MAX_EXPONENT<exponent-1) || ((-MAX_EXPONENT)>exponent+1))
923 exiterror( ERR_ARITH_OVERFLOW, 0 ) ;
926 * Set 'sdigs' to the number of significant digits we want in the
927 * answer. Whatever the user wants, we first round off 'in' to
928 * the precision before doing anyting.
930 sdigs = MIN( in_end-in_ptr, TSD->currlevel->currnumsize ) ; /* significant digits in answer */
932 /* Are we in for exponential form? If the integer part is bigger
933 * than a trigger value, or the decimal part bigger than twice that
934 * trigger value, use exponential form. The default for trigger is
935 * the precision, but it will be expt if that is defined.
937 * Problem: will the setting of before and after effect the
938 * precision on such a way that it will change the representation
939 * between exponential/simple form. (I don't think so?) The code
940 * that considers 'after' has therefore been commented out. Consider
941 * the problem format('0.00011111',,4,,3). Now, this number needs
942 * eight digits after the decimal point in order to be represented.
943 * On the other hand, it only need four digits, in the output.
944 * Which is the correct??? I don't know, but I think the code should
945 * be commented out.
947 trigger = (expt!=(-1)) ? expt : TSD->currlevel->currnumsize ;
948 decim = MAX( sdigs - exponent, 0 ) ;
950 use_exp = ((decim>2*trigger) || (trigger<exponent)) ;
951 if (after>(-1))
952 decim = after ;
954 /* If expp is zero, then we will never use exponential form
956 if (expp==0)
957 use_exp = 0 ;
959 /* Here comes the big question, are we going to use exponential form
960 * or simple form, 'use_exp' holds the answer
962 if (use_exp)
964 /* We are going to use exponential form, now we have to check
965 * whether to use scientific or engineering form. In exponential
966 * form, there are *always* an integer part, which size is 1 in
967 * sci. form and 1-3 in eng. form. Now, there are a total of three
968 * ways to do this: sci, eng and custom (i.e. before is set too).
970 use_eng = (TSD->currlevel->numform == NUM_FORM_ENG) ;
972 /* If number is 99.995, we might have to reconsider the integer
973 * part (even the length of the integer part) after rounding the
974 * fractional part. So we better round it right away, and do
975 * something sensible if order of magnitude changed.
977 k = mt->fdescr.exp ;
978 if (after>(-1))
980 if (use_eng)
981 str_round( &mt->fdescr, after + 1 + exponent%2 ) ;
982 else
983 str_round( &mt->fdescr, after + 1 ) ;
985 if (k!=mt->fdescr.exp)
986 goto new_round ;
989 /* If 'before' was specified, we need to initialize the first
990 * chars in out to spaces, and set 'k' so we skip over them.
992 if (before!=(-1))
994 out_ptr = out + before - (mt->fdescr.negative!=0) - 1 ;
995 if (use_eng)
996 out_ptr -= (exponent)%3 ;
998 /* Now, check that there is enough space, and set the initial
999 * characters that we are not going to use to spaces.
1001 if (out_ptr<out)
1002 exiterror( ERR_ARITH_OVERFLOW, 0 ) ;
1004 /* Then set the initial characters to space. When this is done,
1005 * we are sure (almost, at least) that there we have set k to
1006 * point to the 'right' char, so that after the sign and the
1007 * integer part of the matissa has been written out, k will be
1008 * at the k'th position of the output string.
1010 for (tmp_ptr=out; tmp_ptr<out_ptr; *(tmp_ptr++)=' ') ;
1012 else
1013 out_ptr = out ;
1016 * Let's start with the sign, that is not effected by any of the
1017 * various formats at which this routine can output 'in'
1019 if (mt->fdescr.negative)
1020 *(out_ptr++) = '-' ;
1022 /* Then continue with the first digit, that shall *always* be
1023 * written out, both in sci and eng form.
1025 assert( in_ptr < in_end ) ;
1026 *(out_ptr++) = *(in_ptr++) ;
1027 exponent-- ;
1029 /* If we use eng. form, we might have to stuff into it as much
1030 * as two additional digits. And we have to watch out so we neigher
1031 * increase the precision nor use more digits than we are allowed
1032 * to do.
1034 if (use_eng)
1036 for (;;)
1038 /* Break out of this when the exponent is a multiple of three.
1040 if ((exponent%3)==0)
1041 break ;
1043 /* First, check to see if there are more digits in the number
1044 * that we want to write out. If that is ok, then just write
1045 * the whole thing out.
1047 if (in_ptr >= in_end)
1048 exiterror( ERR_ARITH_OVERFLOW, 0 ) ;
1049 *(out_ptr++) = *(in_ptr++) ;
1051 exponent-- ;
1055 /* OK, now we have written out the integer part of the matissa. Now
1056 * we must follow with the decimal part. First of all, let us start
1057 * with the decimal point, which should only be present if the
1058 * actually are a decimal part.
1060 if (after==(-1))
1061 after = in_end - in_ptr ;
1063 if (after>0)
1064 *(out_ptr++) = '.' ;
1066 /* At last we have to to fill in the digits in the decimal part of
1067 * the matissa. Just loop through it.
1069 for (; (after) && (in_ptr < in_end); after--)
1070 *(out_ptr++) = *(in_ptr++) ;
1072 /* And then we have to add the required number of tailing zeros in
1073 * the matissa
1075 for (; after; after--)
1076 *(out_ptr++) = '0' ;
1078 /* Then comes the exponent. It should not be written if the
1079 * exponent is one. On the other hand, if a particular size is
1080 * requested for the exponent in expp, then the appropriate number
1081 * of space should be filled in.
1083 if (exponent)
1085 *(out_ptr++) = 'E' ;
1086 *(out_ptr++) = (char) ((exponent>0) ? '+' : '-') ;
1088 if (exponent<0)
1089 exponent = -exponent ;
1091 /* Now, suppose that expp is unspecified, then we would have to
1092 * find the number of characters needed for the exponent. The
1093 * following is a kludge to set expp to the 'right' number if
1094 * it was previously unset.
1096 if (expp==(-1))
1098 for (k=exponent,expp=0; k; k/=10, expp++)
1102 /* We have to fill in numbers from the end of it, and pad with
1103 * zeros to the left. First find the end, and then loop backwards
1104 * for each significant digit in exponent
1106 for (tmp_ptr=out_ptr+expp-1;;)
1108 /* First check for overflow, i.e the space reserved in the
1109 * exponent is not enough to hold it.
1111 if (tmp_ptr<out_ptr)
1112 exiterror( ERR_ARITH_OVERFLOW, 0 ) ;
1114 /* Then extract the decimal digit and put it into the output
1115 * string, while deviding the exponent by ten.
1117 *(tmp_ptr--) = (char) (exponent%10 + '0') ;
1118 exponent /= 10 ;
1120 /* The only possible way out of this is when the exponent is
1121 * zero, i.e. it has been written out. (unless an error is
1122 * trapped in the lines above.)
1124 if (!exponent)
1125 break ;
1128 /* Now there might be some chars left in the exponent that has
1129 * to be set to leading zeros, check it and do it.
1131 for (; tmp_ptr>=out_ptr;)
1132 *(tmp_ptr--) = '0' ;
1134 /* At last, set k, so that we know the real size of the out
1135 * string.
1137 out_ptr += expp ;
1139 else if (expp!=(-1))
1141 for (j=(-2); j<expp; j++)
1142 *(out_ptr++) = ' ' ;
1145 else
1147 /* If number is 99.995, we might have to reconsider the integer
1148 * part (even the length of the integer part) after rounding the
1149 * fractional part. So we better round it right away, and do
1150 * something sensible if order of magnitude changed.
1152 after = decim ;
1153 k = mt->fdescr.exp ;
1154 if (after>(-1))
1156 str_round( &mt->fdescr, after + mt->fdescr.exp ) ;
1157 if (k!=mt->fdescr.exp)
1158 goto new_round ;
1161 out_ptr = out ;
1162 /* We are not going to use an exponent. Our number will consist of
1163 * two parts, the integer part, and the fractional part. Let us
1164 * concentrate on the integer part, which will have a particular
1165 * length if before is set.
1167 if (before>(-1))
1169 /* Since the integer part is going to have a particular length,
1170 * we better find that lenght, and skip the initial part (or
1171 * give an error if the length given is too small for this
1172 * number. Remember that we need at least on digit before the
1173 * decimal point (the leading zero).
1175 size = (mt->fdescr.negative) + MAX(exponent, 1) ;
1177 /* Does the integer part of the output string hold enough chars?
1178 * If not, report an error. If is does, initialize the first
1179 * part of it (the unused part) to spaces.
1181 if (size>before)
1182 exiterror( ERR_ARITH_OVERFLOW, 0 ) ;
1183 else
1185 for (k=0; k<(before-size); k++)
1186 *(out_ptr++) = ' ' ;
1190 /* Write out the sign if it is needed, and then loop trough the
1191 * digits of the integer part of the number. If the integer part
1192 * in empty, write out a "0" instead.
1194 if (mt->fdescr.negative)
1195 *(out_ptr++) = '-' ;
1197 if (exponent>0)
1198 for (; (exponent) && (in_ptr<in_end); exponent--)
1199 *(out_ptr++) = *(in_ptr++) ;
1200 else
1201 *(out_ptr++) = '0' ;
1204 * If the number doesn't have enough digits to fill the integer
1205 * part, help it, and fill it with zeros.
1207 if (exponent>0)
1209 for (; exponent; exponent--)
1210 *(out_ptr++) = '0' ;
1213 /* Now has the time come for the decimal points, which is only
1214 * to be written out if there actually are decimals, and if
1215 * the size of the decimal part is non-zero. First find the
1216 * number of decimals, and stuff it into the 'size' variable
1218 if (after>(-1))
1219 after = after ;
1220 else
1221 after = in_end - in_ptr ;
1223 /* If there are decimals, write the decimal point
1225 if (after>0)
1226 *(out_ptr++) = '.' ;
1228 /* Then write the leading zeros after the decimal point, but
1229 * before the first digit in the number, if there are any
1231 for (j=0; (j<after) && (exponent<0); exponent++, j++)
1232 *(out_ptr++) = '0' ;
1234 /* And then loop through the decimals, and write them out.
1235 * Remember that size might be larger than the actual number
1236 * of decimals available.
1238 for (; (in_ptr<in_end) && (j<after); j++)
1239 *(out_ptr++) = *(in_ptr++) ;
1241 /* Then append the right number of zeros, to pad the number
1242 * to the wanted length. Here k is the length of the number
1244 for (; j<after; j++)
1245 *(out_ptr++) = '0' ;
1248 result = Str_makeTSD( out_ptr - out ) ;
1249 memcpy( result->value, out, result->len=out_ptr-out ) ;
1250 return result ;
1253 streng *str_format(tsd_t *TSD, const streng *input, int Before,
1254 int After, int Expp, int Expt)
1255 /* According to ANSI X3J18-199X, 9.4.2, this function performs the BIF "format"
1256 * with extensions made by Brian.
1257 * I rewrote the complete function to allow comparing of this function code
1258 * to that one made in Rexx originally.
1259 * input is the first arg to "format" and may not be a number.
1260 * Before, After, Expp and Expt are the other args to this function and are
1261 * -1 if they are missing value.
1262 * FGC
1265 #define Enlarge(Num,atleast) if (Num.size + (atleast) > Num.max) { \
1266 char *new = MallocTSD(Num.size + (atleast) + 5); \
1267 Num.max = Num.size + (atleast) + 5; \
1268 memcpy(new,Num.num,Num.size); \
1269 FreeTSD(Num.num); \
1270 Num.num = new; \
1272 char *buf;
1273 size_t bufsize;
1274 size_t bufpos;
1275 int ShowExp,Exponent,ExponentLen = 0,Afters,Sign,Point,OrigExpp,h;
1276 streng *retval;
1277 char ExponentBuffer[80]; /* enough even on a 256-bit-machine for an int */
1278 mat_tsd_t *mt;
1280 mt = TSD->mat_tsd;
1282 if (mt->OldRegina)
1283 return(str_format_orig(TSD, input, Before, After, Expp, Expt));
1285 /* Convert the input to a number and check if it is a number at all. */
1286 if (getdescr(TSD,input,&mt->fdescr))
1287 exiterror(ERR_INCORRECT_CALL, 11, "FORMAT", 1, tmpstr_of(TSD, input));
1289 /* Round the number according to NUMERIC DIGITS */
1290 str_round( &mt->fdescr, TSD->currlevel->currnumsize ) ;
1292 /* We have done the "call CheckArgs" of the ANSI function. */
1294 /* In the simplest case the first is the only argument. */
1295 if ((Before == -1) && (After == -1) && (Expp == -1) && (Expt == -1))
1296 return str_norm(TSD, &mt->fdescr, NULL);
1298 if (Expt == -1)
1299 Expt = TSD->currlevel->currnumsize;
1301 /* The number is already set up but check twice that we don't have leading
1302 zeros:
1304 /*str_strip(&mt->fdescr); FGC: FIX: commented off for testing purpose */
1306 /* Trailing zeros are confusing, too: */
1307 while ((mt->fdescr.size > 1) && (mt->fdescr.num[mt->fdescr.size - 1] == '0'))
1308 mt->fdescr.size--;
1309 /* The number is normalized, now.
1310 * Usage of the variables:
1311 * mt->fdescr.num: Mantissa in the ANSI-standard and defined as usual, zeros
1312 * may be padded at the end but never at the start because:
1313 * mt->fdescr.exp: true exponent for the mantissa.
1314 * Exponent: Used Exponent for the mantissa, e.g.:
1315 * mt->fdescr.num=1,mt->fdescr.exp=2 = 0.1E2 = 10E0
1316 * In this case Exponent may be 0 to reflect the exponent we
1317 * should display.
1318 * Point: Defined in the standard but not used. It is obviously
1319 * equal to (mt->fdescr.exp-Exponent)
1320 * examples with both mt->fdescr.num=Mantissa="101" and mt->fdescr.exp=-2:
1321 * Exponent=0: output may be "0.00101"
1322 * Exponent=-3: output may be "1.01E-3"
1325 Exponent = mt->fdescr.exp;
1326 Sign = (mt->fdescr.negative) ? 1 : 0;
1327 /* Sign, Mantissa(mt->fdescr.num) and Exponent now reflect the Number.
1328 Keep in mind that the Mantissa of a num_descr is always normalized to
1329 a value smaller than 1. Thus, mt->fdescr(num=1,exp=1) means 0.1E1=1)
1332 /* Decide whether exponential form to be used, setting ShowExp.
1333 These tests have to be on the number before any rounding since
1334 decision on whether to have exponent affects what digits surround
1335 the decimal point.
1336 Only after this decision can Before and After arguments be checked.
1338 ShowExp = 0;
1339 /* There is a test about maximum number of digits in the part before
1340 the decimal point, if non-exponential is to be used.
1341 eg 123.4E+3 becomes 1234E+2 by point removal, 123400 non-floating
1343 if (Exponent > Expt)
1344 ShowExp = 1;
1345 /* Also a test on digits after the point.
1346 If the Exponent is negative at this point in the calculation there
1347 is a possibility that the non-exponential form would have too many
1348 zeros after the decimal point.
1349 For classic this test is:
1351 if (mt->ClassicFormat)
1353 if ((-(Exponent - mt->fdescr.size) > 2*Expt) && (-Exponent >= 0))
1354 ShowExp = 1;
1356 else /* For modern it is: */
1358 /* The non-exponential value needs to be at least a millionth. */
1359 if (-Exponent >= 6)
1360 ShowExp = 1;
1362 /* An over-riding rule for exponential form: */
1363 if (Expp == 0)
1364 ShowExp = 0;
1366 /* ShowExp now indicates whether to show an exponent. */
1367 if (ShowExp)
1369 /* Construct the exponential part of the result.
1370 Exponent at this point in the calculation reflects an integer
1371 mantissa. It has to be changed to reflect a decimal point at
1372 Point from the left. */
1373 /* set the Point to 1 as required for 'SCIENTIFIC': */
1374 Exponent--;
1375 h = Exponent % 3;
1376 if ((TSD->currlevel->numform == NUM_FORM_ENG) && h)
1378 if (h < 0) /* integer division may return values < 0 */
1379 h += 3;
1380 Enlarge(mt->fdescr,h);
1381 memset(mt->fdescr.num + mt->fdescr.size,'0',h);
1382 mt->fdescr.size += h;
1383 Exponent -= h;
1386 else /* Force the exponent to zero for non-exponential format. */
1388 if (Exponent > mt->fdescr.size)
1390 Enlarge(mt->fdescr,Exponent - mt->fdescr.size);
1391 memset(mt->fdescr.num + mt->fdescr.size,'0',Exponent - mt->fdescr.size);
1392 mt->fdescr.size = Exponent; /* mt->fdescr.size += Exponent - mt->fdescr.size; */
1394 /* else add zeros at the start of the Mantissa in the standard */
1395 Exponent = 0;
1396 /* Now Exponent is Zero and Mantissa with Point reflects Number */
1399 /* Deal with right of decimal point first since that can affect the
1400 left. Ensure the requested number of digits there.
1402 Afters = mt->fdescr.size - (mt->fdescr.exp - Exponent);
1403 assert(Afters >= 0);
1404 if (After == -1)
1405 After = Afters; /* Note default. */
1406 /* Make Afters match the requested After */
1407 if (Afters < After)
1409 h = After - Afters;
1410 Enlarge(mt->fdescr,h);
1411 memset(mt->fdescr.num + mt->fdescr.size,'0',h);
1412 mt->fdescr.size += h;
1414 else if (Afters > After)
1416 /* Round by adding 5 at the right place. */
1417 h = mt->fdescr.exp - Exponent + After;
1418 /* h == position of overflowed digit or < 0 if it doesn't exist */
1419 if (h < 0) /* default it to zero, it's a special case of truncating */
1421 mt->fdescr.size = 1;
1422 mt->fdescr.num[0] = '0'; /* always enough space for one character */
1423 Sign = 0;
1424 mt->fdescr.exp = -After + 1; /* let the mantissa been filled up later */
1426 else if (mt->fdescr.num[h] >= '5')
1428 mt->fdescr.size = h;
1429 /* Just in this case the value may change significantly! We leave
1430 * the normal transcription of the standard here to do it faster
1431 * and maybe better. Keep in mind some problems with rounding and
1432 * the awful ENGINEERING format.
1434 for (--h;h >= 0;h--)
1436 if (++mt->fdescr.num[h] <= '9')
1437 break;
1438 mt->fdescr.num[h] = '0';
1440 if (h < 0) /* overflow! we have to add one in the front! */
1442 Enlarge(mt->fdescr,1);
1443 memmove(mt->fdescr.num + 1,mt->fdescr.num,mt->fdescr.size);
1444 mt->fdescr.size++;
1445 mt->fdescr.num[0] = '1';
1446 mt->fdescr.exp++;
1447 if ( (mt->fdescr.exp - Exponent > Expt)
1448 && Expp != 0 ) /* bug 20000727-84858 */
1449 ShowExp = 1;
1450 if (ShowExp)
1452 Exponent = mt->fdescr.exp - 1;
1453 h = Exponent % 3;
1454 if ((TSD->currlevel->numform == NUM_FORM_ENG) && h)
1456 if (h < 0) /* integer division may return values < 0 */
1457 h += 3;
1458 Exponent -= h;
1462 for (h = mt->fdescr.size - 1;h >= 0;h--)
1464 if (mt->fdescr.num[h] != '0')
1465 break;
1466 else if (h == 0) /* completely zero */
1467 Sign = 0;
1469 /* The value itself is correct now but check the format: */
1471 else /* rounding by truncating */
1473 mt->fdescr.size = h;
1474 for (--h;h >= 0;h--)
1476 if (mt->fdescr.num[h] != '0')
1477 break;
1478 else if (h == 0) /* completely zero */
1479 Sign = 0;
1482 } /* Rounded */
1483 /* That's all for now with the right part */
1485 /* Now deal with the part of the result before the decimal point. */
1486 Point = mt->fdescr.exp - Exponent; /* Point doesn't change never more */
1487 h = Point;
1488 if (h <= 0) /* missing front of the number? */
1489 h = 1; /* assume 1 char for "0" of "0.xxx" */
1490 if (Before == -1)
1491 Before = h + Sign; /* Note default. */
1492 /* Make Point match Before */
1493 if (h > Before - Sign)
1494 exiterror(ERR_INCORRECT_CALL, 38, "FORMAT", 2, tmpstr_of(TSD, input));
1496 /* We check the length of the exponent field, first. This allows to
1497 * allocate a sufficient string for the complete number.
1499 OrigExpp = Expp;
1500 if (ShowExp)
1502 if ((h = Exponent) < 0)
1503 h = -h;
1504 sprintf(ExponentBuffer,"%d",h);
1505 ExponentLen = strlen(ExponentBuffer);
1506 if (Expp == -1)
1507 Expp = ExponentLen;
1508 if (ExponentLen > Expp)
1509 exiterror(ERR_INCORRECT_CALL, 38, "FORMAT", 4, tmpstr_of(TSD, input));
1511 else
1512 Expp = 0; /* don't format an exponent */
1514 bufsize = Before + After + Expp + 4; /* Point, "E+", term. zero */
1515 buf = MallocTSD(bufsize);
1517 /* Now do the formatting, it's a little bit complicated, since the parts
1518 * of the number may not exist (partially).
1520 /* Format the part before the point */
1521 if (Point <= 0) /* denormalized number */
1523 memset(buf,' ',Before - 1);
1524 buf[Before - 1] = '0';
1525 if (Sign)
1526 buf[Before - 2] = '-';
1528 else
1530 assert(Point <= mt->fdescr.size); /* before part should always fit */
1531 memset(buf,' ',Before - Point);
1532 memcpy(buf + Before - Point,mt->fdescr.num,Point);
1533 if (Sign)
1534 buf[Before - Point - 1] = '-';
1536 bufpos = Before;
1538 /* Process the part after the decimal point */
1539 if (After > 0)
1541 buf[bufpos++] = '.';
1542 if (Point < 0)
1543 { /* Denormalized mantissa, we must fill up with zeros */
1544 h = -Point;
1545 if (h > After)
1546 h = After; /* beware of an overrun */
1547 memset(buf + bufpos,'0',h);
1548 assert(After - h <= mt->fdescr.size);
1549 memcpy(buf + bufpos + h,mt->fdescr.num,After - h);
1551 else
1553 assert(Point + After <= mt->fdescr.size);
1554 memcpy(buf + bufpos,mt->fdescr.num + Point,After);
1556 bufpos += After;
1559 /* Finally process the exponent. ExponentBuffer contents the exponent
1560 * without the sign.
1562 if (ShowExp)
1564 if (Exponent == 0)
1566 if (OrigExpp != -1)
1568 memset(buf + bufpos,' ',Expp + 2);
1569 bufpos += Expp + 2;
1572 else
1574 buf[bufpos++] = 'E';
1575 buf[bufpos++] = (char) ((Exponent < 0) ? '-' : '+');
1576 memset(buf + bufpos,'0',Expp - ExponentLen);
1577 memcpy(buf + bufpos + Expp - ExponentLen,ExponentBuffer,ExponentLen);
1578 bufpos += Expp;
1582 assert(bufpos < bufsize);
1583 buf[bufpos] = '\0';
1585 retval = Str_creTSD(buf);
1586 FreeTSD(buf);
1587 return retval;
1588 #undef Enlarge
1592 #define OPTIMIZE
1593 #ifdef OPTIMIZE
1596 streng *str_norm( const tsd_t *TSD, num_descr *in, streng *try )
1598 streng *result=NULL ;
1599 char frst=0 ;
1600 int i=0, j=0, k=0, p=0 ;
1601 int sdigs=0, top=0 ;
1602 int size=0, exp=0 ;
1603 mat_tsd_t *mt;
1605 mt = TSD->mat_tsd;
1607 IS_AT_LEAST( mt->norm_out, mt->norm_outsize, 3*TSD->currlevel->currnumsize+5 ) ;
1608 #ifdef TRACEMEM
1609 mt->outptr2 = mt->norm_out ;
1610 #endif
1612 if (in->negative)
1613 mt->norm_out[k++] = '-' ;
1615 /* remove effect of leading zeros in the descriptor */
1616 for ( i=0; i<in->size ; i++)
1618 if ( in->num[i]!='0' )
1619 break ;
1622 exp = in->exp ;
1623 if (i==in->size) /* mantissa contains zeros only */
1625 in->size = in->exp = 1 ;
1626 in->negative = 0 ;
1627 in->num[0] = '0' ;
1628 if (try)
1631 * Replace assert() with real code - MDW 30012002
1632 assert( try->max > 0 ) ;
1633 try->value[0] = '0' ;
1634 try->len = 1 ;
1636 if (try->max)
1638 try->value[0] = '0' ;
1639 try->len = 1 ;
1641 else
1643 Free_stringTSD( try ) ;
1644 try = Str_creTSD( "0" ) ;
1647 else
1648 try = Str_creTSD( "0" ) ;
1649 return try ;
1651 else
1652 size = in->size ;
1654 if ((MAX_EXPONENT+1<exp) || ((-MAX_EXPONENT-1)>exp)) /* too late here! FGC */
1656 exiterror( ERR_ARITH_OVERFLOW, 0 ) ;
1657 return NULL ;
1660 exp -= top = i ; /* adjust the exponent */
1662 /* Again, it is too late for an adjustment to digits() here. The user may
1663 * have changed digits() in between. The rounding should have taken place
1664 * after the math operation (which happens) and that's all.
1665 * Benefit: We don't have to round and therefore can't change the
1666 * exponent by rounding. FGC
1668 sdigs = MIN( size-i, TSD->currlevel->currnumsize ) ; /* significant digits in answer */
1670 if ((exp <= -6) || ( exp > TSD->currlevel->currnumsize )) /* ANSI */
1672 /* Too late for rounding, must be fixed! FGC */
1673 if ((size-i > TSD->currlevel->currnumsize) && (in->num[TSD->currlevel->currnumsize+i] > '4'))
1675 for (j=TSD->currlevel->currnumsize+i-1; j>=i; j--)
1677 if (((in->num[j])++)=='9')
1678 in->num[j] = '0' ;
1679 else
1680 break ;
1682 if (j<i)
1684 in->num[i] = '1' ;
1685 exp++ ;
1689 frst = mt->norm_out[k++] = (char) (( i<size ) ? in->num[i++] : '0') ;
1690 if (i<size && (1<=TSD->currlevel->currnumsize-(frst!='0')))
1691 mt->norm_out[k++] = '.' ;
1693 for (j=1; (i<size) && (j<=TSD->currlevel->currnumsize-(frst!='0')); j++ )
1694 mt->norm_out[k++] = in->num[i++] ;
1696 /* avrunding */
1697 j = exp - 1 ;
1698 mt->norm_out[k++] = 'E' ;
1699 sprintf( &mt->norm_out[k], "%+d", j ) ;
1701 return Str_creTSD( mt->norm_out ) ;
1704 if (exp <= 0)
1706 mt->norm_out[k++] = '0' ;
1707 if (exp<0)
1709 mt->norm_out[k++] = '.' ;
1710 for ( j=0; j>exp; j-- )
1711 mt->norm_out[k++] = '0' ;
1715 top = MIN( sdigs, TSD->currlevel->currnumsize ) + i ;
1716 if ((size-i > TSD->currlevel->currnumsize) && (in->num[TSD->currlevel->currnumsize+i] > '4'))
1718 /* Too late for rounding, must be fixed! FGC */
1719 for (j=TSD->currlevel->currnumsize+i-1; j>=i; j--)
1721 if (((in->num[j])++)=='9')
1722 in->num[j] = '0' ;
1723 else
1724 break ;
1726 if (j<i)
1728 top-- ;
1729 p += mt->norm_out[k++] = '1' ;
1733 j = exp ;
1734 for (; i<top; i++ )
1736 if (j--==0)
1737 mt->norm_out[k++] = '.' ;
1739 p += (mt->norm_out[k++] = in->num[i]) - '0' ;
1741 while (j-- > 0) /* Fill possibly lost zeros */
1742 mt->norm_out[k++] = '0';
1744 if (p==0)
1745 mt->norm_out[(k=1)-1] = '0' ;
1747 if (try)
1749 if (try->max>=k)
1750 result = try ;
1751 else
1753 result = Str_makeTSD( k ) ;
1754 Free_stringTSD( try ) ;
1757 else
1758 result = Str_makeTSD( k ) ;
1760 memcpy( result->value, mt->norm_out, result->len=k ) ;
1761 return result ;
1764 #else
1767 streng *str_norm( const tsd_t *TSD, num_descr *in, streng *try )
1769 streng *result=NULL ;
1770 char frst=0 ;
1771 int i=0, j=0, k=0, p=0 ;
1772 int sdigs=0, top=0 ;
1773 int exp=0, size=0, neg=0 ;
1774 mat_tsd_t *mt;
1776 mt = TSD->mat_tsd;
1778 IS_AT_LEAST( mt->norm_out, mt->norm_outsize, 3*TSD->currlevel->currnumsize+5 ) ;
1779 #ifdef TRACEMEM
1780 mt->outptr2 = mt->norm_out ;
1781 #endif
1783 if (in->negative)
1784 mt->norm_out[k++] = '-' ;
1786 /* remove effect of leading zeros in the descriptor */
1787 for ( i=0; i<in->size ; i++)
1789 if ( in->num[i]!='0' )
1790 break ;
1792 exp = in->exp ;
1793 size = in->size ;
1794 neg = in->negative ;
1795 if (i==size)
1796 size = exp = neg = 0 ;
1798 if ((MAX_EXPONENT+1<exp) || ((-MAX_EXPONENT)-1>exp))
1799 exiterror( ERR_ARITH_OVERFLOW, 0 ) ;
1801 exp -= top = i ; /* adjust the exponent */
1802 sdigs = MIN(size-i,TSD->currlevel->currnumsize); /* significant digits in answer */
1804 if (( sdigs - exp > 2*TSD->currlevel->currnumsize ) || ( sdigs < exp ))
1807 if ((size-i > TSD->currlevel->currnumsize) && (in->num[TSD->currlevel->currnumsize+i] > '4'))
1809 for (j=TSD->currlevel->currnumsize+i-1; j>=i; j--)
1811 if (((in->num[j])++)=='9')
1812 in->num[j] = '0' ;
1813 else
1814 break ;
1816 if (j<i)
1818 in->num[i] = '1' ;
1819 exp++ ;
1824 frst = mt->norm_out[k++] = ( i<size ) ? in->num[i++] : '0' ;
1825 if (i<size)
1826 mt->norm_out[k++] = '.' ;
1828 for (j=1; (i<size) && (j<=TSD->currlevel->currnumsize-(frst!='0')); j++ )
1829 mt->norm_out[k++] = in->num[i++] ;
1831 /* avrunding */
1832 j = exp - 1 ;
1833 mt->norm_out[k++] = 'E' ;
1834 sprintf( &mt->norm_out[k], "%+d", j ) ;
1836 return Str_creTSD( mt->norm_out ) ;
1839 if (exp <= 0)
1841 mt->norm_out[k++] = '0' ;
1842 if (exp<0)
1844 mt->norm_out[k++] = '.' ;
1845 for ( j=0; j>exp; j-- )
1846 mt->norm_out[k++] = '0' ;
1850 top = MIN( sdigs, TSD->currlevel->currnumsize ) + i ;
1851 if ((size-i > TSD->currlevel->currnumsize) && (in->num[TSD->currlevel->currnumsize+i] > '4'))
1853 for (j=TSD->currlevel->currnumsize+i-1; j>=i; j--)
1855 if (((in->num[j])++)=='9')
1856 in->num[j] = '0' ;
1857 else
1858 break ;
1860 if (j<i)
1862 top-- ;
1863 p += mt->norm_out[k++] = '1' ;
1867 j = exp ;
1868 for (; i<top; i++ )
1870 if (j--==0)
1871 mt->norm_out[k++] = '.' ;
1873 p += (mt->norm_out[k++] = in->num[i]) - '0' ;
1875 if (p==0)
1876 mt->norm_out[(k=1)-1] = '0' ;
1878 if (try)
1880 if (try->max>=k)
1881 result = try ;
1882 else
1884 result = Str_makeTSD( k ) ;
1885 Free_stringTSD( try ) ;
1888 else
1889 result = Str_makeTSD( k ) ;
1891 memcpy( result->value, mt->norm_out, result->len=k ) ;
1892 return result ;
1896 #endif
1899 int string_test( const tsd_t *TSD, const num_descr *first, const num_descr *second )
1901 int i=0, top=0, fnul=0, snul=0 ;
1902 char fchar=' ', schar=' ' ;
1904 if ( first->negative != second->negative ) /* have different signs */
1905 return ( first->negative ? -1 : 1 ) ;
1907 fnul = ( first->size==1 && first->exp==1 && first->num[0]=='0') ;
1908 snul = ( second->size==1 && second->exp==1 && second->num[0]=='0') ;
1909 if (fnul || snul)
1911 if (fnul && snul) return 0 ;
1912 if (fnul) return (second->negative ? 1 : -1 ) ;
1913 else return (first->negative ? -1 : 1 ) ;
1916 if ( first->exp != second->exp ) /* have different order */
1917 return (log_xor( first->negative, first->exp>second->exp ) ? 1 : -1 ) ;
1919 /* same order and sign, have to compare TSD->currlevel->currnumsize-TSD->currlevel->numfuzz first */
1920 top = MIN( TSD->currlevel->currnumsize-TSD->currlevel->numfuzz, MAX( first->size, second->size )) ;
1921 for ( i=0; i<top; i++ )
1923 fchar = (char) ((first->size > i) ? first->num[i] : '0') ;
1924 schar = (char) ((second->size > i) ? second->num[i] : '0') ;
1925 if ( fchar != schar )
1926 return log_xor( first->negative, fchar>schar ) ? 1 : -1 ;
1929 /* hmmm, last resort: can the numbers be rounded to make a difference */
1930 fchar = (char) ((first->size > i) ? first->num[i] : '0') ;
1931 schar = (char) ((second->size > i) ? second->num[i] : '0') ;
1932 if (((fchar>'4') && (schar>'4')) || ((fchar<'5') && (schar<'5')))
1933 return 0 ; /* equality! */
1935 /* now, one is rounded upwards, the other downwards */
1936 return log_xor( first->negative, fchar>'5' ) ? 1 : -1 ;
1941 num_descr *string_incr( const tsd_t *TSD, num_descr *input )
1943 int last=0 ;
1944 char *cptr=NULL ;
1946 assert( input->size > 0 ) ;
1948 if (input->size != input->exp || input->exp >= TSD->currlevel->currnumsize)
1950 static const num_descr one = { "1", 0, 1, 1, 1 } ;
1952 string_add( TSD, input, &one, input ) ;
1953 str_round(input,TSD->currlevel->currnumsize) ;
1954 return input ;
1957 cptr = input->num ;
1958 last = input->size-1 ;
1960 for (;;)
1962 if (input->negative)
1964 if (cptr[last] > '1')
1966 cptr[last]-- ;
1967 return input ;
1969 else if (cptr[last]=='1')
1971 cptr[last]-- ;
1972 if (last==0)
1973 str_strip( input ) ;
1974 return input ;
1976 else
1978 assert( cptr[last] == '0' ) ;
1979 assert( last ) ;
1980 cptr[last--] = '9' ;
1983 else
1985 if (cptr[last] < '9')
1987 cptr[last]++ ;
1988 return input ;
1990 else
1992 assert( cptr[last] == '9' ) ;
1993 cptr[last--] = '0' ;
1997 if (last<0)
1999 if (input->size >= input->max)
2001 char *new ;
2003 assert( input->size == input->max ) ;
2004 new = MallocTSD( input->max * 2 + 2 ) ;
2005 memcpy( new+1, input->num, input->size ) ;
2006 new[0] = '0' ;
2007 input->size++ ;
2008 input->exp++ ;
2009 input->max = input->max*2 + 2 ;
2010 FreeTSD( input->num ) ;
2011 cptr = input->num = new ;
2013 else
2015 memmove( input->num+1, input->num, input->size ) ;
2016 input->size++ ;
2017 input->exp++ ;
2018 input->num[0] = '0' ;
2020 last++ ;
2027 void string_div( const tsd_t *TSD, const num_descr *f, const num_descr *s, num_descr *r, num_descr *r2, int type )
2029 int ssize=0, tstart=0, tcnt=0, finished=0, tend=0 ;
2030 int i=0, cont=0, outp=0, test=0, loan=0 ;
2031 int origneg=0, origexp=0 ;
2032 mat_tsd_t *mt;
2034 mt = TSD->mat_tsd;
2036 IS_AT_LEAST( mt->div_out, mt->div_outsize, (TSD->currlevel->currnumsize+1)*2+1 ) ;
2037 IS_AT_LEAST( r->num, r->max, TSD->currlevel->currnumsize+1 ) ;
2038 #ifdef TRACEMEM
2039 mt->outptr3 = mt->div_out ;
2040 #endif
2042 ssize = MIN( s->size, TSD->currlevel->currnumsize+1 ) ;
2043 r->exp = 1 + f->exp - s->exp ;
2044 r->negative = log_xor( f->negative, s->negative ) ;
2046 /* initialize the pointers */
2047 tstart = 0 ;
2048 tend = tstart + MIN( f->size, TSD->currlevel->currnumsize+1 ) ;
2050 /* first, fill out tmp to the size of ssize */
2051 for (tcnt=tstart; tcnt< ssize; tcnt++ )
2052 mt->div_out[tcnt] = (char) ((tcnt<tend) ? f->num[tcnt] : '0') ;
2054 /* if the ssize first digits in f->num form a number which is smaller */
2055 /* than s->num, we must add an additional digit to f->num */
2057 for (i=0; i<ssize; i++)
2059 if ( mt->div_out[i] > s->num[i] )
2060 break ;
2061 else if ( mt->div_out[i] < s->num[i] )
2063 mt->div_out[tcnt] = (char) ((tcnt<tend) ? f->num[tcnt] : '0') ;
2064 tcnt++ ;
2065 r->exp-- ;
2066 break ;
2070 /* situation: s->num[1..ssize] contains the devisor, and the array */
2071 /* div_out[tstart==0..tcnt-1] hold the (first part of the) devidend. The */
2072 /* array f->num[tcnt..tend-1] (which may be empty) holds the last */
2073 /* part of the devidend */
2075 /* then foreach resulting digit we want */
2076 for (outp=0; outp<TSD->currlevel->currnumsize+1 && !finished; outp++)
2078 r->num[outp] = '0' ;
2079 if ((tcnt-tstart > ssize) && (mt->div_out[tstart]=='0'))
2080 tstart++ ;
2082 /* if this is integer division, and we have hit the decimal point... */
2083 if ((type!=DIVTYPE_NORMAL) && (outp>=r->exp))
2085 finished = 1 ;
2086 continue ;
2089 /* try to subtract as many times as possible */
2090 for (cont=1; cont; )
2092 /* can we subtract one more time? */
2093 if (tcnt-tstart == ssize)
2094 for (i=0; i<ssize; i++ )
2096 test = mt->div_out[tstart+i] - s->num[i] ;
2097 if (test<0)
2098 cont = 0 ;
2099 if (test!=0)
2100 break ;
2103 /* if we can continue, subtract it */
2104 loan = 0 ;
2105 if (cont)
2107 r->num[outp]++ ;
2108 for (i=0; i<ssize; i++)
2110 /* When do M$ ever build usable compilers, sigh: */
2111 char h = (char) ( s->num[ssize-1-i] - '0' + loan ) ;
2112 mt->div_out[tcnt-1-i] = (char) (mt->div_out[tcnt-1-i] - h);
2113 if ((loan = (mt->div_out[tcnt-1-i] < '0'))!=0)
2114 mt->div_out[tcnt-1-i] += 10 ;
2116 if (loan)
2118 /* decrement it and check for '0' */
2119 mt->div_out[tstart] -= 1 ;
2120 if ((tcnt-tstart > ssize) && (mt->div_out[tstart]=='0'))
2121 tstart++ ;
2125 } /* for each possible subtraction */
2127 if ((tcnt-tstart > ssize) && (mt->div_out[tstart]=='0'))
2128 tstart++ ;
2130 /* do we have anything left of the devidend, only meaningful if */
2131 /* all digits in original divident have been processed, it is */
2132 /* also safe to assume that divident and devisor have equal size */
2134 assert( tcnt-tstart == ssize ) ;
2135 mt->div_out[tcnt] = (char) ((tcnt<tend) ? f->num[tcnt] : '0') ;
2136 if (++tcnt > tend)
2138 finished = 1 ;
2139 for (i=tstart; i<tcnt; i++)
2141 if (mt->div_out[i]!='0')
2143 finished = 0 ;
2144 break ;
2149 } /* for each digit wanted in the result */
2151 origexp = f->exp ;
2152 origneg = f->negative ;
2154 if (type==DIVTYPE_BOTH) /* return both answers */
2156 IS_AT_LEAST( r2->num, r2->max, outp ) ;
2158 memcpy(r2->num, r->num, outp) ;
2159 r2->negative = r->negative ;
2160 r2->size = r->size ;
2161 r2->exp = r->exp ;
2163 for (r2->size = outp; (r2->size > r2->exp) && (r2->size > 1); r2->size--)
2165 if (r2->num[r2->size-1]!='0')
2166 break ;
2170 if ((type == DIVTYPE_REMINDER) || (type == DIVTYPE_BOTH))
2172 /* we are really interested in the reminder, so swap things */
2173 for (i=0; i<MIN(MAX(tend,tcnt)-tstart,TSD->currlevel->currnumsize+1); i++ )
2174 r->num[i] = (char) (i<tcnt-tstart ? mt->div_out[tstart+i] : f->num[tstart+i]) ;
2176 r->size = outp = i ;
2177 r->exp = origexp - tstart ;
2178 r->negative = origneg ;
2181 /* then, at the end, we have to strip of trailing zeros that come */
2182 /* after the decimal point, first do we have any decimals? */
2183 for (r->size = outp; (r->size > r->exp) && (r->size > 1); r->size--)
2185 if (r->num[r->size-1]!='0')
2186 break ;
2193 /* The multiplication table for two single-digits numbers */
2194 static const char mult[10][10][3] = {
2195 { "00", "00", "00", "00", "00", "00", "00", "00", "00", "00" },
2196 { "00", "01", "02", "03", "04", "05", "06", "07", "08", "09" },
2197 { "00", "02", "04", "06", "08", "10", "12", "14", "16", "18" },
2198 { "00", "03", "06", "09", "12", "15", "18", "21", "24", "27" },
2199 { "00", "04", "08", "12", "16", "20", "24", "28", "32", "36" },
2200 { "00", "05", "10", "15", "20", "25", "30", "35", "40", "45" },
2201 { "00", "06", "12", "18", "24", "30", "36", "42", "48", "54" },
2202 { "00", "07", "14", "21", "28", "35", "42", "49", "56", "63" },
2203 { "00", "08", "16", "24", "32", "40", "48", "56", "64", "72" },
2204 { "00", "09", "18", "27", "36", "45", "54", "63", "72", "81" },
2208 void string_mul( const tsd_t *TSD, const num_descr *f, const num_descr *s, num_descr *r )
2210 char *outp=NULL;
2211 const char *answer=NULL ;
2212 int i=0, sskip=0, fskip=0, sstart=0, fstart=0, base=0, offset=0, carry=0, j=0 ;
2213 mat_tsd_t *mt;
2215 mt = TSD->mat_tsd;
2217 IS_AT_LEAST( mt->mul_out, mt->mul_outsize, 2*(TSD->currlevel->currnumsize+1) ) ;
2218 #ifdef TRACEMEM
2219 mt->outptr4 = mt->mul_out ;
2220 #endif
2222 for (i=0; i<2*(TSD->currlevel->currnumsize+1); mt->mul_out[i++]='0') ;
2223 outp = &mt->mul_out[2*(TSD->currlevel->currnumsize+1)-1] ;
2225 for (sskip=0; (sskip<s->size) && (s->num[sskip]=='0'); sskip++ ) ;
2226 sstart = MIN( sskip+TSD->currlevel->currnumsize+1, s->size-1 ) ;
2228 for (fskip=0; (fskip<f->size) && (f->num[fskip]=='0'); fskip++ ) ;
2229 fstart = MIN( fskip+TSD->currlevel->currnumsize+1, f->size-1 ) ;
2231 base = 2*(TSD->currlevel->currnumsize+1)-1 ;
2232 offset = carry = 0 ;
2233 for ( i=sstart; i>=sskip; i-- )
2235 offset = carry = 0 ;
2236 assert( base >= 0 ) ;
2237 for ( j=fstart; j>=fskip; j-- )
2239 answer = mult[f->num[j]-'0'][s->num[i]-'0'] ;
2240 assert( base-offset >= 0 ) ;
2241 /* Stupid MSVC likes this only: */
2242 mt->mul_out[base-offset] = (char) (mt->mul_out[base-offset] +
2243 answer[1] - '0' + carry) ;
2244 carry = answer[0] - '0' ;
2245 for (; mt->mul_out[base-offset]>'9'; )
2247 mt->mul_out[base-offset] -= 10 ;
2248 carry++ ;
2250 offset++ ;
2252 if (base-offset >= 0)
2253 mt->mul_out[base-offset++] = (char) (carry + '0') ;
2254 else
2255 exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "" ) ;
2257 base-- ;
2260 IS_AT_LEAST( r->num, r->max, /*2*(TSD->currlevel->currnumsize+1)*/
2261 outp-mt->mul_out-base+offset ) ;
2262 j = 0 ;
2263 for (i=base-offset+2; (i<=outp-mt->mul_out); i++ )
2264 r->num[j++] = mt->mul_out[i] ;
2266 if (j==0)
2268 r->num[j++] = '0' ;
2269 r->exp = 1 ;
2271 else
2272 r->exp = s->exp + f->exp ;
2274 r->negative = log_xor( f->negative, s->negative ) ;
2275 r->size = j ;
2276 str_round( r, TSD->currlevel->currnumsize ) ;
2280 static void descr_strip( const tsd_t *TSD, const num_descr *from, num_descr *to )
2282 int i=0, j=0 ;
2284 IS_AT_LEAST( to->num, to->max, TSD->currlevel->currnumsize+1 ) ;
2286 to->negative = from->negative ;
2287 for (i=0; (i<from->size) && (from->num[i]=='0'); i++ ) ;
2288 to->exp = from->exp - i ;
2289 for (j=0; j+i<from->size; j++ )
2290 to->num[j] = from->num[i+j] ;
2292 if ((to->exp-1 > MAX_EXPONENT) || ( -MAX_EXPONENT > to->exp+1))
2293 exiterror( ERR_ARITH_OVERFLOW, 0 ) ;
2295 to->size = j ;
2300 void string_pow( const tsd_t *TSD, const num_descr *num, int power, num_descr *acc, num_descr *res )
2302 static const num_descr one = { "1", 0, 1, 1, 2 } ;
2303 int ineg=0, pow=0, cnt=0 ;
2305 IS_AT_LEAST( res->num, res->max, TSD->currlevel->currnumsize+1 ) ;
2306 IS_AT_LEAST( acc->num, acc->max, TSD->currlevel->currnumsize+1 ) ;
2308 acc->exp = 1 ;
2309 acc->size = 1 ;
2310 acc->negative = 0 ;
2311 acc->num[0] = '1' ;
2313 if (power < 0)
2315 power = -power ;
2316 ineg = 1 ;
2318 pow = power;
2320 for (cnt=0; pow; cnt++ )
2321 pow = pow>>1 ;
2323 for ( ;cnt ; )
2325 if (power & (1<<(cnt-1)))
2327 /* multiply acc with *f, and put answer into acc */
2328 string_mul( TSD, acc, num, res ) ;
2329 assert( acc->size <= acc->max && res->size <= res->max ) ;
2330 descr_strip( TSD, res, acc ) ;
2331 assert( acc->size <= acc->max && res->size <= res->max ) ;
2334 if ((--cnt)==0)
2335 break ; /* horrible example of dataflow */
2337 /* then, square the contents of acc */
2338 string_mul( TSD, acc, acc, res ) ;
2339 assert( acc->size <= acc->max && res->size <= res->max ) ;
2340 descr_strip( TSD, res, acc ) ;
2341 assert( acc->size <= acc->max && res->size <= res->max ) ;
2344 if (ineg)
2345 /* may hang if acc==zero ? */
2346 string_div( TSD, &one, acc, res, NULL, DIVTYPE_NORMAL ) ;
2347 else
2348 descr_strip( TSD, acc, res ) ;
2349 assert( acc->size <= acc->max && res->size <= res->max ) ;
2353 /* ========= interface routines to the arithmetic routines ========== */
2355 streng *str_add( const tsd_t *TSD, const void *descr, const streng *second )
2357 mat_tsd_t *mt;
2359 mt = TSD->mat_tsd;
2360 if (getdescr( TSD, second, &mt->sdescr ))
2361 exiterror( ERR_BAD_ARITHMETIC, 0 ) ;
2363 string_add( TSD, descr, &mt->sdescr, &mt->rdescr ) ;
2364 return str_norm( TSD, &mt->rdescr, NULL ) ;
2367 int descr_sign( const void *descr )
2369 return( ((num_descr*)descr)->negative ? -1 : 1 ) ;
2373 void free_a_descr( const tsd_t *TSD, num_descr *in )
2375 assert( in->size <= in->max ) ;
2377 if ( in->num )
2378 FreeTSD( in->num ) ;
2380 FreeTSD( in ) ;
2384 num_descr *get_a_descr( const tsd_t *TSD, const streng *num )
2386 num_descr *descr=NULL ;
2388 descr=MallocTSD( sizeof(num_descr)) ;
2389 descr->max = 0 ;
2390 descr->num = NULL ;
2392 if (getdescr( TSD, num, descr ))
2393 exiterror( ERR_BAD_ARITHMETIC, 0 ) ;
2395 return (void*)descr ;
2399 int str_true( const tsd_t *TSD, const streng *input )
2401 mat_tsd_t *mt;
2403 mt = TSD->mat_tsd;
2404 if (input->len != 1)
2405 exiterror( ERR_UNLOGICAL_VALUE, 0 ) ;
2407 switch (input->value[0])
2409 case '1':
2410 return 1 ;
2411 case '0':
2412 return 0 ;
2413 default:
2414 exiterror( ERR_UNLOGICAL_VALUE, 0 ) ;
2417 /* Too keep the compiler happy */
2418 return 1 ;
2422 streng *str_abs( const tsd_t *TSD, const streng *input)
2424 mat_tsd_t *mt;
2426 mt = TSD->mat_tsd;
2427 if (getdescr(TSD,input,&mt->fdescr))
2428 exiterror( ERR_BAD_ARITHMETIC, 0 ) ;
2430 mt->fdescr.negative = 0 ;
2431 return str_norm( TSD, &mt->fdescr, NULL ) ;
2435 streng *str_trunc( const tsd_t *TSD, const streng *number, int deci )
2437 int i=0, j=0, k=0, size=0, top=0 ;
2438 streng *result=NULL ;
2439 mat_tsd_t *mt;
2441 mt = TSD->mat_tsd;
2443 /* first, convert number to internal representation */
2444 if (getdescr( TSD, number, &mt->fdescr ))
2445 exiterror( ERR_BAD_ARITHMETIC, 0 ) ;
2447 /* get rid of possible excessive precision */
2448 if (mt->fdescr.size>TSD->currlevel->currnumsize)
2449 str_round( &mt->fdescr, TSD->currlevel->currnumsize ) ;
2451 /* who big must the result string be? */
2452 if ((i=mt->fdescr.exp) > 0 )
2453 size = mt->fdescr.exp + deci ;
2454 else
2455 size = deci ;
2458 * Adrian Sutherland <adrian@dealernet.co.uk>
2459 * Changed the following line from '+ 2' to '+ 3',
2460 * because I was getting core dumps ... I think that we need this
2461 * because negative numbers BIGGER THAN -1 need a sign, a zero and
2462 * a decimal point ... A.
2464 result = Str_makeTSD( size + 3 ) ; /* allow for sign and decimal point */
2465 j = 0 ;
2467 if (mt->fdescr.negative)
2468 result->value[j++] = '-' ;
2470 /* first fill in the known numerals of the integer part */
2471 top = MIN( mt->fdescr.exp, mt->fdescr.size ) ;
2472 for (i=0; i<top; i++)
2473 result->value[j++] = mt->fdescr.num[i] ;
2475 /* pad out with '0' in the integer part, if necessary */
2476 for (k=i; k<mt->fdescr.exp; k++)
2477 result->value[j++] = '0' ;
2479 if (k==0)
2480 result->value[j++] = '0' ;
2482 k = 0 ;
2483 if (deci>0)
2485 result->value[j++] = '.' ;
2487 /* pad with zeros between decimal point and number */
2488 for (k=0; k>mt->fdescr.exp; k--)
2489 result->value[j++] = '0' ;
2492 /* fill in with the decimals, if any */
2493 top = MIN( mt->fdescr.size-mt->fdescr.exp, deci ) + i + k ;
2494 for (; i<top; i++ )
2495 result->value[j++] = mt->fdescr.num[i] ;
2497 /* pad with zeros if necessary */
2498 for (; i<deci+MIN(mt->fdescr.exp,mt->fdescr.size); i++ )
2499 result->value[j++] = '0' ;
2501 result->len = j ;
2502 assert( (result->len <= result->max) && (result->len<=size+2) ) ;
2503 return( result ) ;
2508 /* ------------------------------------------------------------------
2509 * This function converts a packed binary string to a decimal integer.
2510 * It is equivalent of interpreting the binary string as a number of
2511 * base 256, and converting it to base 10 (the actual algorithm uses
2512 * a number of base 2, padded to a multiple of 8 digits). Negative
2513 * numbers are interpreted as two's complement.
2515 * First parameter is the packed binary string; second parameter is
2516 * the number of initial characters to skip (i.e. the position of the
2517 * most significant byte in 'input'; the third parameter is a boolean
2518 * telling if this number is signed or not.
2520 * The significance of the 'too_large' variable: If the number has
2521 * leading zeros, that is not an error, so the 'fdescr' might be set
2522 * to values larger than it can hold. However, the error occurs only
2523 * if that value is used. Therefore, if 'fdescr' becomes bigger than
2524 * the max whole number, 'too_large' is set. If attempts are made to
2525 * use 'fdescr' while 'too_large' is set, an error occurs.
2527 * Note that this algoritm requires that string_mul and string_add
2528 * does not change anything in their first two parameters.
2530 * The 'input' variable is assumed to have at least one digit, so don't
2531 * call this function with a null string. Maybe the compiler could
2532 * optimize this function better if [esf]descr were locals?
2535 streng *str_digitize( const tsd_t *TSD, const streng *input, int start, int sign )
2537 int cur_byte=0 ; /* current byte in 'input' */
2538 int cur_bit=0 ; /* current bit in 'input' */
2539 int too_large=0 ; /* error flag (see above) */
2540 mat_tsd_t *mt;
2542 mt = TSD->mat_tsd;
2544 /* do we have anything to work on? */
2545 assert( start < Str_len(input) ) ;
2547 /* ensure that temporary number descriptors has enough space */
2548 IS_AT_LEAST( mt->fdescr.num, mt->fdescr.max, TSD->currlevel->currnumsize+2 ) ;
2549 IS_AT_LEAST( mt->edescr.num, mt->edescr.max, TSD->currlevel->currnumsize+2 ) ;
2550 IS_AT_LEAST( mt->sdescr.num, mt->sdescr.max, TSD->currlevel->currnumsize+2 ) ;
2553 * Initialize the temporary number descriptors: 'fdescr', 'sdescr'
2554 * and 'edescr'. They will be initialized to 0, 1 and 2 respectively.
2555 * They are used for:
2557 * fdescr: contains the value of the current bit of the current
2558 * byte, e.g the third last bit in the last byte will
2559 * have the value '0100'b (=4). This value is multiplied
2560 * with two at each iteration of the inner loop. Is
2561 * initialized to the value '1', and will have the same
2562 * sign as 'input'.
2564 * sdescr: contains '2', to make doubling of 'fdescr' easy
2566 * edescr: contains the answer, initially set to '0' if 'input'
2567 * is positive, or '-1' if 'input' is negative. The
2568 * descriptor 'fdescr' is added to (or implicitly
2569 * subtracted from) this number.
2571 mt->fdescr.size = mt->sdescr.size = mt->edescr.size = 1 ;
2572 mt->fdescr.negative = mt->sdescr.negative = mt->edescr.negative = 0 ;
2573 mt->fdescr.exp = mt->sdescr.exp = mt->edescr.exp = 1 ;
2575 mt->edescr.num[0] = '0' ; /* the resulting number */
2576 mt->fdescr.num[0] = '1' ; /* the value of each binary digit */
2577 mt->sdescr.num[0] = '2' ; /* the number to multiply 'fdescr' in */
2580 * If 'input' is signed, but positive, treat as if it was unsigned.
2581 * 'sign' is then effectively a boolean stating whether 'input' is
2582 * a negative number. In that case, 'edescr' should be set to '-1'.
2583 * Also, 'fdescr' is set to negative, so that it is subtracted from
2584 * 'edescr' when given to string_add().
2586 if (sign)
2588 if (input->value[start] & 0x80)
2590 mt->edescr.num[0] = '1' ;
2591 mt->edescr.negative = 1 ;
2592 mt->fdescr.negative = 1 ;
2594 else
2595 sign = 0 ;
2599 * Each iteration of the outer loop will process a byte in 'input',
2600 * starting with the last (least significant) byte. Each iteration
2601 * of the inner loop will process one bit in the byte currently
2602 * processed by the outer loop.
2604 for (cur_byte=Str_len(input)-1; cur_byte>=start; cur_byte--)
2606 for (cur_bit=0; cur_bit<8; cur_bit++)
2608 /* does the precision hold? if not, set flag */
2609 if (mt->fdescr.size > TSD->currlevel->currnumsize)
2610 too_large = 1 ;
2613 * If the current bit (the j'th bit in the i'th byte) is set
2614 * and input is positive; or if current bit is not set and
2615 * input is negative, then increase the value of the result.
2616 * This is not really a bitwise xor, but a logical xor, but
2617 * the values are always 1 or 0, so it doesn't matter.
2619 if ((sign) ^ ((input->value[cur_byte] >> cur_bit) & 1))
2621 if (too_large)
2622 exiterror( ERR_INVALID_INTEGER, 0 ) ;
2624 string_add( TSD, &mt->edescr, &mt->fdescr, &mt->edescr ) ;
2628 * Str_ip away any leading zeros. If this is not done, the
2629 * accuracy of the operation will deter, since string_add()
2630 * return answer with leading zero, and the accumulative
2631 * effect of this would make 'edescr' zero after a few
2632 * iterations of the loop.
2634 str_strip( &mt->edescr ) ;
2637 * Increase the value of 'fdescr', so that it corresponds with
2638 * the significance of the current bit in 'input'. But don't
2639 * do this if 'fdescr' isn't capable of holding that number.
2641 if (!too_large)
2643 string_mul( TSD, &mt->fdescr, &mt->sdescr, &mt->fdescr ) ;
2644 str_strip( &mt->fdescr ) ;
2649 /* normalize answer and return to caller */
2650 return str_norm( TSD, &mt->edescr, NULL ) ;
2653 streng *str_binerize( const tsd_t *TSD, const streng *number, int length )
2655 int i=0 ;
2656 streng *result=NULL ;
2657 char *res_ptr=NULL ;
2660 * We are going to need two number in this algoritm, so we can
2661 * just as well make them right away. We could initialize these on
2662 * the first invocation of this routine, and thereby saving some
2663 * space, but that would 1) take CPU on every invocation; 2) it
2664 * would probably cost just as much space in the text segment.
2665 * (Would have to set NUMERIC DIGIT to at least 4 before calling
2666 * getdescr with these.)
2668 static const num_descr minus_one = { "1", 1, 1, 1, 2 } ;
2669 static const num_descr byte = { "256", 0, 3, 3, 4 } ;
2671 mat_tsd_t *mt;
2673 mt = TSD->mat_tsd;
2676 * First, let us convert the number into a descriptor. If that is
2677 * not possible, then we don't have a number, which is an error.
2679 if (getdescr( TSD, number, &mt->edescr ))
2680 exiterror( ERR_BAD_ARITHMETIC, 0 ) ;
2683 * If the number is negative, then we *must* know how long it is,
2684 * since we are going to pad with 'ff'x bytes at the left up the
2685 * the wanted length. If we don't know the length, report an error.
2687 if ((length==(-1)) && (mt->edescr.negative))
2688 exiterror( ERR_INCORRECT_CALL, 0 ) ;
2691 * Then we have to determine if this actually is a whole number.
2692 * There are two things that might be wrong: a non-zero fractional
2693 * part (checked in the 'else' part below; or a insufficient
2694 * precition (handled in the if part below).
2696 if (mt->edescr.size < mt->edescr.exp)
2698 exiterror( ERR_INVALID_INTEGER, 0 ) ;
2700 else if (mt->edescr.size > mt->edescr.exp)
2703 * Make sure that all digits in the fractional part is zero
2705 for (i=MIN(mt->edescr.exp,0); i<mt->edescr.exp; i++)
2707 if (mt->edescr.num[i]!='0')
2709 exiterror( ERR_INVALID_INTEGER, 0 ) ;
2715 * If the number is zero, a special case applies, the return value
2716 * is a nullstring. Same if length is zero. I am not sure if this
2717 * is needed, or if the rest of the algoritm is general enough to
2718 * handle these cases too.
2720 if ((length==0) /* || ((mt->edescr.num[0]=='0') && (mt->edescr.size=1)) */)
2721 result=nullstringptr() ;
2724 * Here comes the real work. To ease the implementation it is
2725 * devided into two parts based on whether or not length is
2726 * specified.
2728 else if (length==(-1))
2731 * First, let's estimate the size of the output string that
2732 * we need. A crude (over)estimate is one char for every second
2733 * decimal digits. Also set length, just to chache the value.
2734 * (btw: isn't that MAX( ,0) unneeded? Since number don't have
2735 * a decimal part, and since it must have a integer part (else
2736 * it would be zero, and then trapped above.)
2738 assert(mt->edescr.exp > 0) ;
2739 result = Str_makeTSD( (length=(MAX(mt->edescr.exp,0))/2) + 1 ) ;
2740 res_ptr = result->value ;
2744 * Let's loop from the least significant part of edescr. For each
2745 * iteration we devide mt->edescr by 256, stopping when edescr is
2746 * zero.
2748 for (i=length; ; i--)
2751 * Perform the integer divition, edescr gets the quotient,
2752 * while fdescr get the reminder. Afterwards, perform some
2753 * makeup on the numbers (that might not be needed?)
2755 /* may hang if acc==zero ? */
2756 string_div( TSD, &mt->edescr, &byte, &mt->fdescr, &mt->edescr, DIVTYPE_BOTH ) ;
2757 str_strip( &mt->edescr ) ;
2758 str_strip( &mt->fdescr ) ;
2761 * Now, fdescr has the reminder, stuff it into the result string
2762 * before it escapes :-) (don't we have to cast lvalue here?)
2763 * Afterwards, check to see if there are more digits to extract.
2765 result->value[i] = (char) descr_to_int( &mt->fdescr ) ;
2766 if ((mt->edescr.num[0]=='0') && (mt->edescr.size==1))
2767 break ;
2771 * That's it, now we just have to align the answer and set the
2772 * correct length. Have to use memmove() since strings may
2773 * overlap.
2775 memmove( result->value, &result->value[i], length+1-i ) ;
2776 result->len = length + 1 - i ;
2778 else
2781 * We do have a specified length for the number. At least that
2782 * makes it easy to deside how large the result string should be.
2784 result = Str_makeTSD( length ) ;
2785 res_ptr = result->value ;
2788 * In the loop, iterate once for each divition of 256, but stop
2789 * only when we have reached the start of the result string.
2790 * Below, edescr gets the quotient and fdescr gets the reminder.
2792 for (i=length-1; i>=0; i--)
2794 /* may hang if acc==zero ? */
2795 string_div( TSD, &mt->edescr, &byte, &mt->fdescr, &mt->edescr, DIVTYPE_BOTH ) ;
2796 str_strip( &mt->edescr ) ;
2797 str_strip( &mt->fdescr ) ;
2800 * If the reminder is negative (i.e. quotient is negative too)
2801 * then add 256 to the reminder, to bring it into the range of
2802 * an unsigned char. To compensate for that, subtract one from
2803 * the quotient. Store the reminder.
2805 if (mt->fdescr.negative)
2807 /* the following two lines are not needed, but it does not
2808 work without them. */
2809 if ((mt->edescr.size==1) && (mt->edescr.num[0]=='0'))
2810 mt->edescr.exp = 1 ;
2812 string_add( TSD, &mt->edescr, &minus_one, &mt->edescr ) ;
2813 str_strip( &mt->edescr ) ;
2814 string_add( TSD, &mt->fdescr, &byte, &mt->fdescr ) ;
2816 result->value[i] = (char) descr_to_int( &mt->fdescr ) ;
2819 * That's it, store the length
2821 result->len = length ;
2825 * We're finished ... hope it works ...
2827 return result ;
2831 streng *str_normalize( const tsd_t *TSD, const streng *number )
2833 mat_tsd_t *mt;
2835 mt = TSD->mat_tsd;
2836 if (getdescr( TSD, number, &mt->fdescr ))
2837 exiterror( ERR_BAD_ARITHMETIC, 0 ) ;
2839 return str_norm( TSD, &mt->fdescr, NULL ) ;
2844 num_descr *is_a_descr( const tsd_t *TSD, const streng *number )
2846 num_descr *new=NULL ;
2847 mat_tsd_t *mt;
2849 mt = TSD->mat_tsd;
2851 if (getdescr( TSD, number, &mt->fdescr ))
2852 return NULL ;
2854 new = MallocTSD( sizeof( num_descr )) ;
2855 new->max = 0 ;
2856 new->num = NULL ;
2858 descr_copy( TSD, &mt->fdescr, new ) ;
2859 return new ;
2863 int myiswnumber( const tsd_t *TSD, const streng *number )
2866 * Checks if number is a whole number according to NUMERIC DIGITS.
2867 * We DON'T check for NUMERIC FORM if the resulting string would have
2868 * a decimal point.
2870 int i=0;
2871 mat_tsd_t *mt;
2873 mt = TSD->mat_tsd;
2875 if (getdescr(TSD, number, &mt->fdescr))
2876 return 0;
2877 str_round(&mt->fdescr, TSD->currlevel->currnumsize);
2878 i = mt->fdescr.exp;
2879 if (i <= 0) /* 0.?? */
2880 return 0;
2881 for (; i < mt->fdescr.size; i++)
2883 if (mt->fdescr.num[i] != '0') /* decimal part not zero */
2884 return 0;
2886 return 1;
2890 /* Converts number to an integer. Sets *error to 1 on error (0 otherwise) */
2891 int streng_to_int( const tsd_t *TSD, const streng *number, int *error )
2893 int result=0 ;
2894 int i=0 ;
2895 mat_tsd_t *mt;
2897 mt = TSD->mat_tsd;
2899 if (getdescr( TSD, number, &mt->fdescr ))
2900 goto errorout ;
2902 str_round( &mt->fdescr, TSD->currlevel->currnumsize ) ;
2903 if (mt->fdescr.exp > mt->fdescr.size) /* precision of less than one */
2904 goto errorout ;
2906 i = mt->fdescr.exp ;
2907 if (i<0)
2908 i = 0 ;
2909 for (; i<mt->fdescr.size; i++)
2911 if (mt->fdescr.num[i] != '0') /* decimal part not zero */
2912 goto errorout ;
2914 if (mt->fdescr.exp>9)
2915 goto errorout ; /* thus, a 32 bit integer should be sufficient */
2917 result = 0 ;
2918 for (i=0; i<mt->fdescr.exp; i++)
2919 result = result * 10 + (mt->fdescr.num[i]-'0') ;
2921 if (mt->fdescr.negative)
2922 result = -result ;
2924 *error = 0;
2925 return result ;
2926 errorout:
2927 *error = 1;
2928 return(0);