diff options
Diffstat (limited to 'newlib/libc/stdlib/ecvtbuf.c')
-rw-r--r-- | newlib/libc/stdlib/ecvtbuf.c | 470 |
1 files changed, 470 insertions, 0 deletions
diff --git a/newlib/libc/stdlib/ecvtbuf.c b/newlib/libc/stdlib/ecvtbuf.c new file mode 100644 index 0000000..146a4fe --- /dev/null +++ b/newlib/libc/stdlib/ecvtbuf.c @@ -0,0 +1,470 @@ +/* +FUNCTION +<<ecvtbuf>>, <<fcvtbuf>>---double or float to string + +INDEX + ecvtbuf +INDEX + fcvtbuf + +ANSI_SYNOPSIS + #include <stdio.h> + + char *ecvtbuf(double <[val]>, int <[chars]>, int *<[decpt]>, + int *<[sgn]>, char *<[buf]>); + + char *fcvtbuf(double <[val]>, int <[decimals]>, int *<[decpt]>, + int *<[sgn]>, char *<[buf]>); + +TRAD_SYNOPSIS + #include <stdio.h> + + char *ecvtbuf(<[val]>, <[chars]>, <[decpt]>, <[sgn]>, <[buf]>); + double <[val]>; + int <[chars]>; + int *<[decpt]>; + int *<[sgn]>; + char *<[buf]>; + + char *fcvtbuf(<[val]>, <[decimals]>, <[decpt]>, <[sgn]>, <[buf]>); + double <[val]>; + int <[decimals]>; + int *<[decpt]>; + int *<[sgn]>; + char *<[buf]>; + +DESCRIPTION + <<ecvtbuf>> and <<fcvtbuf>> produce (null-terminated) strings + of digits representating the <<double>> number <[val]>. + + The only difference between <<ecvtbuf>> and <<fcvtbuf>> is the + interpretation of the second argument (<[chars]> or + <[decimals]>). For <<ecvtbuf>>, the second argument <[chars]> + specifies the total number of characters to write (which is + also the number of significant digits in the formatted string, + since these two functions write only digits). For <<fcvtbuf>>, + the second argument <[decimals]> specifies the number of + characters to write after the decimal point; all digits for + the integer part of <[val]> are always included. + + Since <<ecvtbuf>> and <<fcvtbuf>> write only digits in the + output string, they record the location of the decimal point + in <<*<[decpt]>>>, and the sign of the number in <<*<[sgn]>>>. + After formatting a number, <<*<[decpt]>>> contains the number + of digits to the left of the decimal point. <<*<[sgn]>>> + contains <<0>> if the number is positive, and <<1>> if it is + negative. For both functions, you supply a pointer <[buf]> to + an area of memory to hold the converted string. + +RETURNS + Both functions return a pointer to <[buf]>, the string + containing a character representation of <[val]>. + +PORTABILITY + Neither function is ANSI C. + +Supporting OS subroutines required: <<close>>, <<fstat>>, <<isatty>>, +<<lseek>>, <<read>>, <<sbrk>>, <<write>>. +*/ + +#include <_ansi.h> +#include <stdlib.h> +#include <string.h> +#include <reent.h> +#include "mprec.h" +#include "local.h" + +static void +_DEFUN (print_f, (ptr, buf, invalue, ndigit, type, dot, mode), + struct _reent *ptr _AND + char *buf _AND + double invalue _AND + int ndigit _AND + char type _AND + int dot _AND + int mode) +{ + int decpt; + int sign; + char *p, *start, *end; + + start = p = _dtoa_r (ptr, invalue, mode, ndigit, &decpt, &sign, &end); + + if (decpt == 9999) + { + strcpy (buf, p); + return; + } + while (*p && decpt > 0) + { + *buf++ = *p++; + decpt--; + } + /* Even if not in buffer */ + while (decpt > 0) + { + *buf++ = '0'; + decpt--; + } + + if (dot || *p) + { + if (p == start) + *buf++ = '0'; + *buf++ = '.'; + while (decpt < 0 && ndigit > 0) + { + *buf++ = '0'; + decpt++; + ndigit--; + } + + /* Print rest of stuff */ + while (*p && ndigit > 0) + { + *buf++ = *p++; + ndigit--; + } + /* And trailing zeros */ + while (ndigit > 0) + { + *buf++ = '0'; + ndigit--; + } + } + *buf++ = 0; +} + +/* Print number in e format with width chars after. + + TYPE is one of 'e' or 'E'. It may also be one of 'g' or 'G' indicating + that _gcvt is calling us and we should remove trailing zeroes. + + WIDTH is the number of digits of precision after the decimal point. */ + +static void +_DEFUN (print_e, (ptr, buf, invalue, width, type, dot), + struct _reent *ptr _AND + char *buf _AND + double invalue _AND + int width _AND + char type _AND + int dot) +{ + int dp; + int sign; + char *end; + char *p; + int decpt; + int top; + int ndigit = width; + + p = _dtoa_r (ptr, invalue, 2, width + 1, &decpt, &sign, &end); + + if (decpt == 9999) + { + strcpy (buf, p); + return; + } + + *buf++ = *p++; + if (dot || ndigit != 0) + *buf++ = '.'; + + while (*p && ndigit > 0) + { + *buf++ = *p++; + ndigit--; + } + + /* Add trailing zeroes to fill out to ndigits unless this is 'g' format. + Also, convert g/G to e/E. */ + + if (type == 'g') + type = 'e'; + else if (type == 'G') + type = 'E'; + else + { + while (ndigit > 0) + { + *buf++ = '0'; + ndigit--; + } + } + + /* Add the exponent. */ + + *buf++ = type; + decpt--; + if (decpt < 0) + { + *buf++ = '-'; + decpt = -decpt; + } + else + { + *buf++ = '+'; + } + if (decpt > 99) + { + int top = decpt / 100; + *buf++ = top + '0'; + decpt -= top * 100; + } + top = decpt / 10; + *buf++ = top + '0'; + decpt -= top * 10; + *buf++ = decpt + '0'; + + *buf++ = 0; +} + +#ifndef _REENT_ONLY + +/* Undocumented behaviour: when given NULL as a buffer, return a + pointer to static space in the rent structure. This is only to + support ecvt and fcvt, which aren't ANSI anyway. */ + +char * +_DEFUN (fcvtbuf, (invalue, ndigit, decpt, sign, fcvt_buf), + double invalue _AND + int ndigit _AND + int *decpt _AND + int *sign _AND + char *fcvt_buf) +{ + char *save; + char *p; + char *end; + int done = 0; + + if (fcvt_buf == NULL) + { + if (_REENT->_cvtlen <= ndigit + 35) + { + if ((fcvt_buf = (char *) _realloc_r (_REENT, _REENT->_cvtbuf, + ndigit + 36)) == NULL) + return NULL; + _REENT->_cvtlen = ndigit + 36; + _REENT->_cvtbuf = fcvt_buf; + } + + fcvt_buf = _REENT->_cvtbuf ; + } + + save = fcvt_buf; + + if (invalue < 1.0 && invalue > -1.0) + { + p = _dtoa_r (_REENT, invalue, 2, ndigit, decpt, sign, &end); + } + else + { + p = _dtoa_r (_REENT, invalue, 3, ndigit, decpt, sign, &end); + } + + /* Now copy */ + + done = -*decpt; + while (p < end) + { + *fcvt_buf++ = *p++; + done++; + } + /* And unsuppress the trailing zeroes */ + while (done < ndigit) + { + *fcvt_buf++ = '0'; + done++; + } + *fcvt_buf++ = 0; + return save; +} + +char * +_DEFUN (ecvtbuf, (invalue, ndigit, decpt, sign, fcvt_buf), + double invalue _AND + int ndigit _AND + int *decpt _AND + int *sign _AND + char *fcvt_buf) +{ + char *save; + char *p; + char *end; + int done = 0; + + if (fcvt_buf == NULL) + { + if (_REENT->_cvtlen <= ndigit) + { + if ((fcvt_buf = (char *) _realloc_r (_REENT, _REENT->_cvtbuf, + ndigit + 1)) == NULL) + return NULL; + _REENT->_cvtlen = ndigit + 1; + _REENT->_cvtbuf = fcvt_buf; + } + + fcvt_buf = _REENT->_cvtbuf ; + } + + save = fcvt_buf; + + p = _dtoa_r (_REENT, invalue, 2, ndigit, decpt, sign, &end); + + /* Now copy */ + + while (p < end) + { + *fcvt_buf++ = *p++; + done++; + } + /* And unsuppress the trailing zeroes */ + while (done < ndigit) + { + *fcvt_buf++ = '0'; + done++; + } + *fcvt_buf++ = 0; + return save; +} + +#endif + +char * +_DEFUN (_gcvt, (ptr, invalue, ndigit, buf, type, dot), + struct _reent *ptr _AND + double invalue _AND + int ndigit _AND + char *buf _AND + char type _AND + int dot) +{ + char *save = buf; + + if (invalue < 0) + { + invalue = -invalue; + } + + if (invalue == 0) + { + *buf++ = '0'; + *buf = '\0'; + } + else + /* Which one to print ? + ANSI says that anything with more that 4 zeros after the . or more + than precision digits before is printed in e with the qualification + that trailing zeroes are removed from the fraction portion. */ + + if (0.0001 >= invalue || invalue >= _mprec_log10 (ndigit)) + { + /* We subtract 1 from ndigit because in the 'e' format the precision is + the number of digits after the . but in 'g' format it is the number + of significant digits. + + We defer changing type to e/E so that print_e() can know it's us + calling and thus should remove trailing zeroes. */ + + print_e (ptr, buf, invalue, ndigit - 1, type, dot); + } + else + { + int decpt; + int sign; + char *end; + char *p; + + if (invalue < 1.0) + { + /* what we want is ndigits after the point */ + p = _dtoa_r (ptr, invalue, 3, ndigit, &decpt, &sign, &end); + } + else + { + p = _dtoa_r (ptr, invalue, 2, ndigit, &decpt, &sign, &end); + } + + if (decpt == 9999) + { + strcpy (buf, p); + return save; + } + while (*p && decpt > 0) + { + *buf++ = *p++; + decpt--; + ndigit--; + } + /* Even if not in buffer */ + while (decpt > 0 && ndigit > 0) + { + *buf++ = '0'; + decpt--; + ndigit--; + } + + if (dot || *p) + { + if (buf == save) + *buf++ = '0'; + *buf++ = '.'; + while (decpt < 0 && ndigit > 0) + { + *buf++ = '0'; + decpt++; + ndigit--; + } + + /* Print rest of stuff */ + while (*p && ndigit > 0) + { + *buf++ = *p++; + ndigit--; + } + /* And trailing zeros */ + if (dot) + { + while (ndigit > 0) + { + *buf++ = '0'; + ndigit--; + } + } + } + *buf++ = 0; + } + + return save; +} + +char * +_DEFUN (_dcvt, (ptr, buffer, invalue, precision, width, type, dot), + struct _reent *ptr _AND + char *buffer _AND + double invalue _AND + int precision _AND + int width _AND + char type _AND + int dot) +{ + switch (type) + { + case 'f': + case 'F': + print_f (ptr, buffer, invalue, precision, type, precision == 0 ? dot : 1, 3); + break; + case 'g': + case 'G': + if (precision == 0) + precision = 1; + _gcvt (ptr, invalue, precision, buffer, type, dot); + break; + case 'e': + case 'E': + print_e (ptr, buffer, invalue, precision, type, dot); + } + return buffer; +} |