- Rewrite the function, getting rid of alloca and unneeded code parts
- Relies on working sprintf, especially allows to detect the decimal point position independent of wanted precision
- Fixes all msvcrt printf winetests

svn path=/trunk/; revision=42383
This commit is contained in:
Gregor Schneider 2009-08-04 15:17:00 +00:00
parent a08bde0eb5
commit f9ae6c7786
3 changed files with 102 additions and 114 deletions

View file

@ -317,7 +317,6 @@
<file>abort.c</file>
<file>atexit.c</file>
<file>ecvt.c</file>
<file>ecvtbuf.c</file>
<file>errno.c</file>
<file>fcvt.c</file>
<file>fcvtbuf.c</file>

View file

@ -1,7 +1,12 @@
/* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */
#include <precomp.h>
/*
* PROJECT: ReactOS CRT
* LICENSE: See COPYING in the top level directory
* PURPOSE: CRT's ecvt
* FILE: lib/sdk/crt/stdlib/ecvt.c
* PROGRAMERS: Gregor Schneider (parts based on ecvtbuf.c by DJ Delorie)
*/
char *ecvtbuf (double, int, int *, int *, char *);
#include <precomp.h>
/*
* @implemented
@ -9,6 +14,98 @@ char *ecvtbuf (double, int, int *, int *, char *);
char *
_ecvt (double value, int ndigits, int *decpt, int *sign)
{
static char ecvt_buf[DBL_MAX_10_EXP + 10];
return ecvtbuf (value, ndigits, decpt, sign, ecvt_buf);
char *ecvtbuf, *cvtbuf;
char *s, *d;
s = cvtbuf = (char*)malloc(ndigits + 18); /* sign, dot, null, 15 for alignment */
d = ecvtbuf = (char*)malloc(DBL_MAX_10_EXP + 10);
*sign = 0;
*decpt = 0;
if (cvtbuf == NULL || ecvtbuf == NULL)
{
return NULL;
}
sprintf(cvtbuf, "%-+.*E", ndigits, value);
/* Treat special values */
if (strncmp(s, "NaN", 3) == 0)
{
memcpy(ecvtbuf, s, 4);
}
else if (strncmp(s + 1, "Inf", 3) == 0)
{
memcpy(ecvtbuf, s, 5);
}
else
{
/* Set the sign */
if (*s && *s == '-')
{
*sign = 1;
}
s++;
/* Copy the first digit */
if (*s && *s != '.')
{
if (d - ecvtbuf < ndigits)
{
*d++ = *s++;
}
else
{
s++;
}
}
/* Skip the decimal point */
if (*s && *s == '.')
{
s++;
}
/* Copy fractional digits */
while (*s && *s != 'E')
{
if (d - ecvtbuf < ndigits)
{
*d++ = *s++;
}
else
{
s++;
}
}
/* Skip the exponent */
if (*s && *s == 'E')
{
s++;
}
/* Set the decimal point to the exponent value plus the one digit we copied */
*decpt = atoi(s) + 1;
/* Handle special decimal point cases */
if (cvtbuf[1] == '0')
{
*decpt = 0;
}
if (ndigits < 1)
{
/* Need enhanced precision*/
char* tbuf = (char*)malloc(ndigits + 18);
sprintf(tbuf, "%-+.*E", ndigits + 2, value);
if (tbuf[1] >= '5')
{
(*decpt)++;
}
free(tbuf);
}
/* Pad with zeroes */
while (d - ecvtbuf < ndigits)
{
*d++ = '0';
}
/* Terminate */
*d = '\0';
}
free(cvtbuf);
return ecvtbuf;
}

View file

@ -1,108 +0,0 @@
/* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <float.h>
#include <malloc.h>
// #include <msvcrt/locale.h>
void __ecvround (char *, char *, const char *, int *);
void
__ecvround (char *numbuf, char *last_digit, const char *after_last, int *decpt)
{
char *p;
int carry = 0;
/* Do we have at all to round the last digit? */
if (*after_last > '4')
{
p = last_digit;
carry = 1;
/* Propagate the rounding through trailing '9' digits. */
do {
int sum = *p + carry;
carry = sum > '9';
*p-- = sum - carry * 10;
} while (carry && p >= numbuf);
/* We have 9999999... which needs to be rounded to 100000.. */
if (carry && p == numbuf)
{
*p = '1';
*decpt += 1;
}
}
}
char *
ecvtbuf (double value, int ndigits, int *decpt, int *sign, char *buf)
{
static char INFINITY[] = "Infinity";
char decimal = '.' /* localeconv()->decimal_point[0] */;
char *cvtbuf = (char *)_alloca (ndigits + 20); /* +3 for sign, dot, null; */
/* two extra for rounding */
/* 15 extra for alignment */
char *s = cvtbuf, *d = buf;
/* Produce two extra digits, so we could round properly. */
sprintf (cvtbuf, "%-+.*E", ndigits + 2, value);
*decpt = 0;
/* The sign. */
if (*s++ == '-')
*sign = 1;
else
*sign = 0;
/* Special values get special treatment. */
if (strncmp (s, "Inf", 3) == 0)
{
/* SunOS docs says we have return "Infinity" for NDIGITS >= 8. */
memcpy (buf, INFINITY, ndigits >= 8 ? 9 : 3);
if (ndigits < 8)
buf[3] = '\0';
}
else if (strcmp (s, "NaN") == 0)
memcpy (buf, s, 4);
else
{
char *last_digit, *digit_after_last;
/* Copy (the single) digit before the decimal. */
while (*s && *s != decimal && d - buf < ndigits)
*d++ = *s++;
/* If we don't see any exponent, here's our decimal point. */
*decpt = d - buf;
if (*s)
s++;
/* Copy the fraction digits. */
while (*s && *s != 'E' && d - buf < ndigits)
*d++ = *s++;
/* Remember the last digit copied and the one after it. */
last_digit = d > buf ? d - 1 : d;
digit_after_last = s;
/* Get past the E in exponent field. */
while (*s && *s++ != 'E')
;
/* Adjust the decimal point by the exponent value. */
*decpt += atoi (s);
/* Pad with zeroes if needed. */
while (d - buf < ndigits)
*d++ = '0';
/* Zero-terminate. */
*d = '\0';
/* Round if necessary. */
__ecvround (buf, last_digit, digit_after_last, decpt);
}
return buf;
}