2000-02-18 03:39:52 +08:00
|
|
|
/*
|
|
|
|
FUNCTION
|
|
|
|
<<ecvtbuf>>, <<fcvtbuf>>---double or float to string
|
|
|
|
|
|
|
|
INDEX
|
|
|
|
ecvtbuf
|
|
|
|
INDEX
|
|
|
|
fcvtbuf
|
|
|
|
|
2017-11-30 16:17:18 +08:00
|
|
|
SYNOPSIS
|
2000-02-18 03:39:52 +08:00
|
|
|
#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]>);
|
|
|
|
|
|
|
|
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"
|
|
|
|
|
Add --enable-newlib-reent-thread-local option
By default, Newlib uses a huge object of type struct _reent to store
thread-specific data. This object is returned by __getreent() if the
__DYNAMIC_REENT__ Newlib configuration option is defined.
The reentrancy structure contains for example errno and the standard input,
output, and error file streams. This means that if an application only uses
errno it has a dependency on the file stream support even if it does not use
it. This is an issue for lower end targets and applications which need to
qualify the software according to safety standards (for example ECSS-E-ST-40C,
ECSS-Q-ST-80C, IEC 61508, ISO 26262, DO-178, DO-330, DO-333).
If the new _REENT_THREAD_LOCAL configuration option is enabled, then struct
_reent is replaced by dedicated thread-local objects for each struct _reent
member. The thread-local objects are defined in translation units which use
the corresponding object.
2022-05-16 17:51:54 +08:00
|
|
|
#ifdef _REENT_THREAD_LOCAL
|
|
|
|
_Thread_local char *_tls_cvtbuf;
|
|
|
|
_Thread_local int _tls_cvtlen;
|
|
|
|
#endif
|
|
|
|
|
2000-02-18 03:39:52 +08:00
|
|
|
static void
|
2017-12-04 11:43:30 +08:00
|
|
|
print_f (struct _reent *ptr,
|
2017-12-04 09:31:41 +08:00
|
|
|
char *buf,
|
|
|
|
double invalue,
|
|
|
|
int ndigit,
|
|
|
|
char type,
|
|
|
|
int dot,
|
2000-02-18 03:39:52 +08:00
|
|
|
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';
|
2019-12-18 14:00:49 +08:00
|
|
|
if (decpt < 0 && ndigit > 0)
|
|
|
|
*buf++ = '.';
|
2000-02-18 03:39:52 +08:00
|
|
|
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
|
2017-12-04 11:43:30 +08:00
|
|
|
print_e (struct _reent *ptr,
|
2017-12-04 09:31:41 +08:00
|
|
|
char *buf,
|
|
|
|
double invalue,
|
|
|
|
int width,
|
|
|
|
char type,
|
2000-02-18 03:39:52 +08:00
|
|
|
int dot)
|
|
|
|
{
|
|
|
|
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++;
|
2019-12-18 14:00:49 +08:00
|
|
|
if (ndigit > 0)
|
|
|
|
dot = 1;
|
2000-02-18 03:39:52 +08:00
|
|
|
|
|
|
|
while (*p && ndigit > 0)
|
|
|
|
{
|
2019-12-18 14:00:49 +08:00
|
|
|
if (dot) {
|
|
|
|
*buf++ = '.';
|
|
|
|
dot = 0;
|
|
|
|
}
|
2000-02-18 03:39:52 +08:00
|
|
|
*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)
|
|
|
|
{
|
2019-12-18 14:00:49 +08:00
|
|
|
if (dot) {
|
|
|
|
*buf++ = '.';
|
|
|
|
dot = 0;
|
|
|
|
}
|
2000-02-18 03:39:52 +08:00
|
|
|
*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 *
|
2017-12-04 11:43:30 +08:00
|
|
|
fcvtbuf (double invalue,
|
2017-12-04 09:31:41 +08:00
|
|
|
int ndigit,
|
|
|
|
int *decpt,
|
|
|
|
int *sign,
|
2000-02-18 03:39:52 +08:00
|
|
|
char *fcvt_buf)
|
|
|
|
{
|
2013-04-30 05:06:23 +08:00
|
|
|
struct _reent *reent = _REENT;
|
2000-02-18 03:39:52 +08:00
|
|
|
char *save;
|
|
|
|
char *p;
|
|
|
|
char *end;
|
|
|
|
int done = 0;
|
|
|
|
|
|
|
|
if (fcvt_buf == NULL)
|
|
|
|
{
|
2022-02-03 18:19:14 +08:00
|
|
|
if (_REENT_CVTLEN(reent) <= ndigit + 35)
|
2000-02-18 03:39:52 +08:00
|
|
|
{
|
2022-02-03 19:24:26 +08:00
|
|
|
if ((fcvt_buf = (char *) _realloc_r (reent, _REENT_CVTBUF(reent),
|
2000-02-18 03:39:52 +08:00
|
|
|
ndigit + 36)) == NULL)
|
|
|
|
return NULL;
|
2022-02-03 18:19:14 +08:00
|
|
|
_REENT_CVTLEN(reent) = ndigit + 36;
|
2022-02-03 19:24:26 +08:00
|
|
|
_REENT_CVTBUF(reent) = fcvt_buf;
|
2000-02-18 03:39:52 +08:00
|
|
|
}
|
|
|
|
|
2022-02-03 19:24:26 +08:00
|
|
|
fcvt_buf = _REENT_CVTBUF(reent) ;
|
2000-02-18 03:39:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
save = fcvt_buf;
|
|
|
|
|
2019-12-19 00:49:05 +08:00
|
|
|
p = _dtoa_r (reent, invalue, 3, ndigit, decpt, sign, &end);
|
2000-02-18 03:39:52 +08:00
|
|
|
|
|
|
|
/* 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 *
|
2017-12-04 11:43:30 +08:00
|
|
|
ecvtbuf (double invalue,
|
2017-12-04 09:31:41 +08:00
|
|
|
int ndigit,
|
|
|
|
int *decpt,
|
|
|
|
int *sign,
|
2000-02-18 03:39:52 +08:00
|
|
|
char *fcvt_buf)
|
|
|
|
{
|
2013-04-30 05:06:23 +08:00
|
|
|
struct _reent *reent = _REENT;
|
2000-02-18 03:39:52 +08:00
|
|
|
char *save;
|
|
|
|
char *p;
|
|
|
|
char *end;
|
|
|
|
int done = 0;
|
|
|
|
|
|
|
|
if (fcvt_buf == NULL)
|
|
|
|
{
|
2022-02-03 18:19:14 +08:00
|
|
|
if (_REENT_CVTLEN(reent) <= ndigit)
|
2000-02-18 03:39:52 +08:00
|
|
|
{
|
2022-02-03 19:24:26 +08:00
|
|
|
if ((fcvt_buf = (char *) _realloc_r (reent, _REENT_CVTBUF(reent),
|
2000-02-18 03:39:52 +08:00
|
|
|
ndigit + 1)) == NULL)
|
|
|
|
return NULL;
|
2022-02-03 18:19:14 +08:00
|
|
|
_REENT_CVTLEN(reent) = ndigit + 1;
|
2022-02-03 19:24:26 +08:00
|
|
|
_REENT_CVTBUF(reent) = fcvt_buf;
|
2000-02-18 03:39:52 +08:00
|
|
|
}
|
|
|
|
|
2022-02-03 19:24:26 +08:00
|
|
|
fcvt_buf = _REENT_CVTBUF(reent) ;
|
2000-02-18 03:39:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
save = fcvt_buf;
|
|
|
|
|
2013-04-30 05:06:23 +08:00
|
|
|
p = _dtoa_r (reent, invalue, 2, ndigit, decpt, sign, &end);
|
2000-02-18 03:39:52 +08:00
|
|
|
|
|
|
|
/* 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 *
|
2017-12-04 11:43:30 +08:00
|
|
|
_gcvt (struct _reent *ptr,
|
2017-12-04 09:31:41 +08:00
|
|
|
double invalue,
|
|
|
|
int ndigit,
|
|
|
|
char *buf,
|
|
|
|
char type,
|
2000-02-18 03:39:52 +08:00
|
|
|
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;
|
|
|
|
|
2019-12-19 00:49:06 +08:00
|
|
|
/* We always want ndigits of precision, even if that means printing
|
|
|
|
* a bunch of leading zeros for numbers < 1.0
|
|
|
|
*/
|
|
|
|
p = _dtoa_r (ptr, invalue, 2, ndigit, &decpt, &sign, &end);
|
2000-02-18 03:39:52 +08:00
|
|
|
|
|
|
|
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++ = '.';
|
2019-12-19 00:49:06 +08:00
|
|
|
|
|
|
|
/* Leading zeros don't count towards 'ndigit' */
|
|
|
|
while (decpt < 0)
|
2000-02-18 03:39:52 +08:00
|
|
|
{
|
|
|
|
*buf++ = '0';
|
|
|
|
decpt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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 *
|
2017-12-04 11:43:30 +08:00
|
|
|
_dcvt (struct _reent *ptr,
|
2017-12-04 09:31:41 +08:00
|
|
|
char *buffer,
|
|
|
|
double invalue,
|
|
|
|
int precision,
|
|
|
|
int width,
|
|
|
|
char type,
|
2000-02-18 03:39:52 +08:00
|
|
|
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;
|
|
|
|
}
|