source: trunk/libs/newlib/src/newlib/libm/test/dcvt.c @ 471

Last change on this file since 471 was 444, checked in by satin@…, 6 years ago

add newlib,libalmos-mkh, restructure shared_syscalls.h and mini-libc

File size: 10.7 KB
Line 
1
2
3#include <limits.h>
4#include <math.h>
5#include <stdio.h>
6#include <float.h>
7#include <ieeefp.h>
8#include <stdlib.h>
9#include <string.h>
10#define _MAX_CHARS 512
11
12static char *lcset = "0123456789abcdef";
13
14static struct p {
15        double pvalue, nvalue;
16        int exp;
17} powers[] = 
18{ 
19{ 1e32, 1e-32, 32},
20{ 1e16, 1e-16, 16},
21{ 1e8, 1e-8, 8},
22{ 1e4, 1e-4, 4},
23{ 1e2, 1e-2, 2},
24{ 1e1, 1e-1, 1 },
25{ 1e0, 1e-0, 0 }
26};
27
28#define _MAX_PREC 16
29
30static char 
31nextdigit (double *value)
32{
33  double tmp;
34 
35  *value = modf (*value * 10, &tmp) ;
36  return  lcset[(int)tmp];
37}
38
39
40static char *
41print_nan (char *buffer,
42       double value,
43       int precision)
44{
45  size_t  i;
46 
47  if (isnan(value))
48    {
49      strcpy(buffer, "nan");
50      i = 3;
51   
52    }
53  else 
54    {
55      strcpy(buffer, "infinity");
56      i = 8;
57    }
58
59  while (i < precision) 
60    {
61      buffer[i++] = ' ';
62    }
63  buffer[i++] = 0;
64  return buffer;
65 
66}
67
68/* A convert info struct */
69typedef struct 
70{
71  char *buffer ;                /* Destination of conversion */
72  double value;                 /* scratch Value to convert */
73  double original_value;        /* saved Value to convert */
74  int value_neg;                /* OUT: 1 if value initialiy neg */
75  int abs_exp;                  /* abs Decimal exponent of value */
76  int abs_exp_sign;             /* + or - */
77  int exp;                      /* exp not sgned */
78  int type;                     /* fFeEgG used in printing before exp */
79
80  int print_trailing_zeros;     /* Print 00's after a . */
81 
82  int null_idx;  /* Index of the null at the end */
83 
84/* These ones are read only */
85  int decimal_places;           /* the number of digits to print after
86                                   the decimal */
87  int max_digits;               /* total number of digits to print */
88  int buffer_size;              /* Size of output buffer */
89 
90  /* Two sorts of dot ness.
91     0  never ever print a dot
92     1  print a dot if followed by a digit
93     2  always print a dot, even if no digit following
94     */
95  enum { dot_never, dot_sometimes, dot_always} dot; /* Print a decimal point, always */
96  int dot_idx;                  /* where the dot went, or would have gone */
97} cvt_info_type;
98
99
100void
101renormalize (cvt_info_type *in)
102{
103
104  /* Make sure all numbers are less than 1 */
105
106  while (in->value >= 1.0) 
107  {
108    in->value = in->value * 0.1;
109    in->exp++;
110  }
111
112  /* Now we have only numbers between 0 and .9999.., and have adjusted
113     exp to account for the shift */ 
114
115  if (in->exp >= 0)
116  {
117    in->abs_exp_sign = '+';
118    in->abs_exp = in->exp;
119  }
120  else 
121  {
122    in->abs_exp_sign  = '-';
123    in->abs_exp = -in->exp;
124  }
125
126}
127
128/* This routine looks at original_value, and makes it between 0 and 1,
129   modifying exp as it goes
130 */
131
132static void 
133normalize (double value,
134       cvt_info_type *in)
135{
136  int j;
137  int texp;
138  if (value != 0) 
139  {
140     texp = -1;
141
142 
143  if (value < 0.0) 
144  {
145    in->value_neg =1 ;
146    value = - value;
147  }
148  else 
149  {
150    in->value_neg = 0;
151  }
152
153               
154  /* Work out texponent & normalise value */
155
156  /* If value > 1, then shrink it */
157  if (value >= 1.0) 
158  {
159    for (j = 0; j < 6; j++) 
160    {
161      while (value >= powers[j].pvalue) 
162      {
163        value /= powers[j].pvalue;
164        texp += powers[j].exp;
165      }
166    }
167  }
168  else if (value != 0.0) 
169  {
170    for (j = 0; j < 6; j++) 
171    {
172      while (value <= powers[j].nvalue) 
173      {
174        value *= powers[j].pvalue;
175        texp -= powers[j].exp;
176      }
177    }
178  }
179   }
180 
181  else
182  {
183    texp = 0;
184  }
185 
186
187  in->exp = texp;
188  in->value = value;
189  in->original_value = value; 
190  renormalize(in);
191 
192}
193int
194round (cvt_info_type *in,
195       char *start,
196       char *now,
197       char ch)
198{
199  double rounder = 5.0;
200
201  char *p;
202  int ok = 0;
203
204  now --;
205
206  /* If the next digit to output would have been a '5' run back and */
207  /* see if we can create a more rounded number. If we can then do it.
208     If not (like when the number was 9.9 and the last char was
209     another 9), then we'll have to modify the number and try again */
210  if (ch < '5') 
211   return 0;
212 
213
214  for (p = now;!ok && p >= start; p--) 
215  {
216    switch (*p) 
217    {
218    default:
219      abort();
220    case '.':
221      break;
222    case '9':
223      rounder = rounder * 0.1;
224      break;
225    case '8':
226    case '7':
227    case '6':
228    case '5':
229    case '4':
230    case '3':
231    case '2':
232    case '1':
233    case '0':
234      p = now;
235      while (1) {
236          if (*p == '9') {
237              *p = '0';
238            }
239          else if (*p != '.') {
240              (*p)++;
241              return 0;
242            }
243          p--;
244        }
245    }
246
247  }
248
249  /* Getting here means that we couldn't round the number in place
250     textually - there have been all nines.
251     We'll have to add to it and try the conversion again
252     eg
253     .99999[9] can't be rounded in place, so add
254     .000005   to it giving:
255     1.000004   we notice that the result is > 1 so add to exp and
256     divide by 10
257     .100004
258     */
259
260  in->original_value = in->value = in->original_value + rounder;
261  normalize(in->original_value , in);
262  return 1; 
263
264 
265}
266
267
268
269void
270_cvte (register  cvt_info_type *in)
271{
272  int buffer_idx  =0;
273  int digit = 0;
274 
275  int after_decimal =0;
276
277  in->buffer[buffer_idx++] = nextdigit(&(in->value));
278  digit++;
279  in->dot_idx = buffer_idx;
280
281 
282  switch (in->dot) 
283  {
284  case dot_never:
285    break;
286  case dot_sometimes:
287    if (in->decimal_places
288        && digit < in->max_digits) 
289    {
290      in->buffer[buffer_idx++] = '.';
291    }
292    break;
293  case dot_always: 
294    in->buffer[buffer_idx++] = '.';   
295  }
296
297 
298  while (buffer_idx < in->buffer_size
299         && after_decimal < in->decimal_places
300         && digit < in->max_digits)
301  {
302    in->buffer[buffer_idx] = nextdigit(&(in->value));
303    after_decimal++;
304    buffer_idx++;
305    digit++;
306   
307  }
308
309  if (round(in,
310            in->buffer,
311            in->buffer+buffer_idx,
312            nextdigit(&(in->value)))) 
313  {
314    _cvte(in);
315  }
316  else 
317  {
318    in->buffer[buffer_idx++] = in->type;                       
319    in->buffer[buffer_idx++] = in->abs_exp_sign;
320
321    if (in->abs_exp >= 100) 
322    {
323      in->buffer[buffer_idx++] = lcset[in->abs_exp / 100];
324      in->abs_exp %= 100;
325    }
326    in->buffer[buffer_idx++] = lcset[in->abs_exp / 10];
327    in->buffer[buffer_idx++] = lcset[in->abs_exp % 10];
328  }
329 
330  in->buffer[buffer_idx++] = 0;
331}
332
333
334
335
336/* Produce NNNN.FFFF */
337void
338_cvtf (cvt_info_type *in)
339{
340 
341  int buffer_idx = 0;           /* Current char being output */
342  int after_decimal = 0;
343  int digit =0;
344
345 
346  in->dot_idx = in->exp + 1;
347 
348  /* Two sorts of number, NNN.FFF and 0.0000...FFFF */
349
350
351  /* Print all the digits up to the decimal point */
352 
353  while (buffer_idx <= in->exp
354         && digit < in->max_digits
355         && buffer_idx < in->buffer_size)
356  {
357    in->buffer[buffer_idx]  = nextdigit(&(in->value));
358    buffer_idx++;
359    digit ++;
360  }
361
362
363  /* And the decimal point if we should */
364  if (buffer_idx < in->buffer_size) 
365  {
366   
367    switch (in->dot) 
368    {
369    case dot_never:
370      break;
371    case dot_sometimes:
372      /* Only print a dot if following chars */
373      if (in->decimal_places
374          && digit < in->max_digits )
375      {
376       in->buffer[buffer_idx++] = '.';     
377     }
378     
379      break;
380    case dot_always:
381      in->buffer[buffer_idx++] = '.';
382    }
383 
384    after_decimal = 0;
385
386    /* And the digits following the point if necessary */
387
388    /* Only print the leading zeros if a dot was possible */
389    if (in->dot || in->exp>0) 
390    {
391     while (buffer_idx < in->buffer_size
392            && (in->abs_exp_sign == '-' && digit < in->abs_exp - 1)
393            && (after_decimal < in->decimal_places)
394            && (digit < in->max_digits))
395     {
396       in->buffer[buffer_idx] = '0';
397       buffer_idx++;
398       digit++;
399       after_decimal++;
400     }
401   }
402   
403    while (buffer_idx < in->buffer_size
404           && after_decimal < in->decimal_places
405           && digit < in->max_digits)
406    {
407      in->buffer[buffer_idx]  = nextdigit(&(in->value));
408      buffer_idx++;
409      digit++;
410      after_decimal++;
411    }
412  }
413
414  in->null_idx = buffer_idx; 
415  in->buffer[buffer_idx] = 0;
416  if (round(in, in->buffer, in->buffer+buffer_idx,
417            nextdigit(&(in->value)))) 
418  {
419      _cvtf(in);
420  }
421 
422
423
424 
425}
426
427
428
429char *
430_dcvt (char *buffer,
431       double invalue,
432       int precision,
433       int width,
434       char type,
435       int dot)
436{
437  cvt_info_type in;
438
439
440
441  in.buffer = buffer;
442  in.buffer_size = 512;
443
444  if (!finite(invalue))
445  {
446    return print_nan(buffer, invalue, precision);
447  }   
448
449
450  normalize(invalue, &in);
451   
452  in.type = type;
453  in.dot = dot? dot_always: dot_sometimes;
454
455  switch (type)
456  {
457 
458  case 'g':
459  case 'G':
460    /* When formatting a g, the precision refers to the number of
461       char positions *total*, this leads to various off by ones */     
462  {
463    /* A precision of 0 means 1 */
464    if (precision == 0)
465     precision = 1;
466     
467    /* A g turns into an e if there are more digits than the
468       precision, or it's smaller than e-4 */
469    if (in.exp >= precision || in.exp < -4) 
470    {
471      in.type = (type == 'g' ? 'e' : 'E');
472      in.decimal_places = _MAX_CHARS;
473      in.max_digits = precision;
474      in.print_trailing_zeros = 1;
475      _cvte(&in);
476    }
477    else 
478    {
479      /* G means total number of chars to print */
480      in.decimal_places = _MAX_CHARS;
481      in.max_digits = precision;
482      in.type = (type == 'g' ? 'f' : 'F');
483      in.print_trailing_zeros = 0;
484      _cvtf(&in);
485
486   if (!dot) {
487       /* trim trailing zeros */
488       int j = in.null_idx -1;
489       while (j > 0 && in.buffer[j] == '0') 
490       {
491         in.buffer[j] = 0;
492         j--;
493       }
494       /* Stamp on a . if not followed by zeros */
495       if (j > 0 && buffer[j] == '.')
496        in.buffer[j] = 0;
497     }
498    }
499   
500     
501    break;
502  case 'f':
503  case 'F':
504    in.decimal_places= precision;
505    in.max_digits = _MAX_CHARS;
506      in.print_trailing_zeros = 1;   
507    _cvtf(&in);
508    break;
509  case 'e':
510  case 'E':
511      in.print_trailing_zeros = 1;
512    in.decimal_places = precision;
513    in.max_digits = _MAX_CHARS;
514    _cvte(&in);
515    break;
516  }
517
518  }
519
520
521  return buffer;
522}
523
524
525
526
527char *
528fcvtbuf (double invalue,
529       int ndigit,
530       int *decpt,
531       int *sign,
532       char *fcvt_buf)
533{
534  cvt_info_type in;
535  in.buffer = fcvt_buf;
536  in.buffer_size = 512;
537   
538  if (!finite(invalue))
539    {
540      return print_nan(fcvt_buf, invalue, ndigit);
541    }   
542
543  normalize(invalue, &in);
544
545  in.dot = dot_never;                   /* Don't print a decimal point */
546  in.max_digits = _MAX_CHARS;
547  in.buffer_size = _MAX_CHARS;          /* Take as many as needed */
548  in.decimal_places = ndigit;
549  _cvtf(&in);
550  *decpt = in.dot_idx;
551  *sign = in.value_neg;
552  return in.buffer;
553}
554
555
556char *
557ecvtbuf (double invalue,
558       int ndigit,
559       int *decpt,
560       int *sign,
561       char *fcvt_buf)
562{
563  cvt_info_type in;
564  in.buffer = fcvt_buf;
565   
566  if (!finite(invalue))
567    {
568      return print_nan(fcvt_buf, invalue, ndigit);
569    }   
570
571  normalize(invalue, &in);
572
573
574  in.dot = dot_never;                   /* Don't print a decimal point */
575/* We can work out how many digits go after the decimal point */
576
577  in.buffer_size =_MAX_CHARS;
578  in.decimal_places = _MAX_CHARS;
579  in.max_digits = ndigit;               /* Take as many as told */
580  _cvtf(&in);
581  *decpt = in.dot_idx;
582  *sign = in.value_neg;
583  return in.buffer;
584}
585
586
587
588char *
589gcvt (double d,
590   int ndigit,
591   char *buf)
592{
593  return _dcvt(buf, d, ndigit, 0, 'g', 1);
594}
Note: See TracBrowser for help on using the repository browser.