msvcrt: correct implementation of _ecvt() with tests.

Rein Klazes wijn at online.nl
Mon Apr 13 02:06:41 CDT 2009


fix for bug #15575 "Word Pro table nubmers get reformatted"
---
 dlls/msvcrt/math.c         |   38 ++++++++++++++++---
 dlls/msvcrt/tests/printf.c |   85 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 117 insertions(+), 6 deletions(-)

diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c
index 0c6290c..3b026a3 100644
--- a/dlls/msvcrt/math.c
+++ b/dlls/msvcrt/math.c
@@ -836,16 +836,42 @@ double CDECL _nextafter(double num, double next)
  */
 char * CDECL _ecvt( double number, int ndigits, int *decpt, int *sign )
 {
+    int prec;
     thread_data_t *data = msvcrt_get_thread_data();
-    char *dec;
-
+    /* FIXME: check better for overflow (native supports over 300 chars's) */
+    ndigits = min( ndigits, 80 - 7); /* 7 : space for dec point, 1 for "e",
+                                      * 4 for exponent and one for
+                                      * terminating '\0' */
     if (!data->efcvt_buffer)
         data->efcvt_buffer = MSVCRT_malloc( 80 ); /* ought to be enough */
 
-    snprintf(data->efcvt_buffer, 80, "%.*e", ndigits /* FIXME wrong */, number);
-    *sign = (number < 0);
-    dec = strchr(data->efcvt_buffer, '.');
-    *decpt = (dec) ? dec - data->efcvt_buffer : -1;
+    if( number < 0) {
+        *sign = TRUE;
+        number = -number;
+    } else
+        *sign = FALSE;
+    /* handle cases with zero ndigits or less */
+    prec = ndigits;
+    if( prec < 1) prec = 2;
+    snprintf(data->efcvt_buffer, 80, "%.*le", prec - 1, number);
+    /* take the decimal "point away */
+    if( prec != 1)
+        strcpy( data->efcvt_buffer + 1, data->efcvt_buffer + 2);
+    /* take the exponential "e" out */
+    data->efcvt_buffer[ prec] = '\0';
+    /* read the exponent */
+    sscanf( data->efcvt_buffer + prec + 1, "%d", decpt);
+    (*decpt)++;
+    /* adjust for some border cases */
+    if( data->efcvt_buffer[0] == '0')/* value is zero */
+        *decpt = 0;
+    /* handle cases with zero ndigits or less */
+    if( ndigits < 1){
+        if( data->efcvt_buffer[ 0] >= '5')
+            (*decpt)++;
+        data->efcvt_buffer[ 0] = '\0';
+    }
+    TRACE("out=\"%s\"\n",data->efcvt_buffer);
     return data->efcvt_buffer;
 }
 
diff --git a/dlls/msvcrt/tests/printf.c b/dlls/msvcrt/tests/printf.c
index 5f72b47..66bab15 100644
--- a/dlls/msvcrt/tests/printf.c
+++ b/dlls/msvcrt/tests/printf.c
@@ -698,6 +698,90 @@ static void test_fcvt(void)
     ok( 0 == sign, "sign wrong\n");
 }
 
+static struct {
+    double value;
+    int nrdigits;
+    const char *expstr_e;
+    const char *expstr_f;
+    int expdecpt_e;
+    int expdecpt_f;
+    int expsign;
+} test_cvt_testcases[] = {
+    {          45.0,   2,        "45",           "4500",          2,      2,      0 },
+    /* Numbers less than 1.0 with different precisions */
+    {        0.0001,   1,         "1",               "",         -3,     -3,     0 },
+    {        0.0001, -10,          "",               "",         -3,     -3,     0 },
+    {        0.0001,  10,"1000000000",        "1000000",         -3,     -3,     0 },
+    /* Basic sign test */
+    {     -111.0001,   5,     "11100",       "11100010",          3,      3,     1 },
+    {      111.0001,   5,     "11100",       "11100010",          3,      3,     0 },
+    /* big numbers with low precision */
+    {        3333.3,   2,        "33",         "333330",          4,      4,     0 },
+    {999999999999.9,   3,       "100","999999999999900",         13,     12,     0 },
+    /* 0.0 with different precisions */
+    {           0.0,   5,     "00000",          "00000",          0,      0,     0 },
+    {           0.0,   0,          "",               "",          0,      0,     0 },
+    {           0.0,  -1,          "",               "",          0,      0,     0 },
+    /* Numbers > 1.0 with 0 or -ve precision */
+    {     -123.0001,   0,          "",            "123",          3,      3,     1 },
+    {     -123.0001,  -1,          "",             "12",          3,      3,     1 },
+    {     -123.0001,  -2,          "",              "1",          3,      3,     1 },
+    {     -123.0001,  -3,          "",               "",          3,      3,     1 },
+    /* Numbers > 1.0, but with rounding at the point of precision */
+    {         99.99,   1,         "1",           "1000",          3,      3,     0 },
+    /* Numbers < 1.0 where rounding occurs at the point of precision */
+    {        0.0063,   2,        "63",              "1",         -2,     -1,     0 },
+    {        0.0063,   3,        "630",             "6",         -2,     -2,     0 },
+    { 0.09999999996,   2,        "10",             "10",          0,      0,     0 },
+    {           0.6,   1,         "6",              "6",          0,      0,     0 },
+    {           0.6,   0,          "",              "1",          1,      1,     0 },
+    {           0.4,   0,          "",               "",          0,      0,     0 },
+    {          0.49,   0,          "",               "",          0,      0,     0 },
+    {          0.51,   0,          "",              "1",          1,      1,     0 },
+    /* ask ridiculous amunt of precision, ruin formatting this table */
+    {           1.0,  30, "100000000000000000000000000000",
+                      "1000000000000000000000000000000",          1,      1,      0},
+    {           123456789012345678901.0,  30, "123456789012345680000000000000",
+                      "123456789012345680000000000000000000000000000000000",         21,    21,      0},
+    /* end marker */
+    { 0, 0, "END"}
+};
+
+static void test_xcvt(void)
+{
+    char *str;
+    int i, decpt, sign;
+    for( i = 0; strcmp( test_cvt_testcases[i].expstr_e, "END"); i++){
+        decpt = sign = 100;
+        str = _ecvt( test_cvt_testcases[i].value,
+                test_cvt_testcases[i].nrdigits,
+                &decpt,
+                &sign);
+        ok( 0 == strncmp( str, test_cvt_testcases[i].expstr_e, 15),
+               "_ecvt() bad return, got \n'%s' expected \n'%s'\n", str,
+              test_cvt_testcases[i].expstr_e);
+        ok( decpt == test_cvt_testcases[i].expdecpt_e,
+                "_ecvt() decimal point wrong, got %d expected %d\n", decpt,
+                test_cvt_testcases[i].expdecpt_e);
+    }
+    for( i = 0; strcmp( test_cvt_testcases[i].expstr_e, "END"); i++){
+        decpt = sign = 100;
+        str = _fcvt( test_cvt_testcases[i].value,
+                test_cvt_testcases[i].nrdigits,
+                &decpt,
+                &sign);
+        ok( 0 == strncmp( str, test_cvt_testcases[i].expstr_f, 15),
+               "_fcvt() bad return, got \n'%s' expected \n'%s'\n", str,
+              test_cvt_testcases[i].expstr_f);
+        ok( decpt == test_cvt_testcases[i].expdecpt_f,
+                "_fcvt() decimal point wrong, got %d expected %d\n", decpt,
+                test_cvt_testcases[i].expdecpt_f);
+        ok( sign == test_cvt_testcases[i].expsign,
+                "_ecvt() sign wrong, got %d expected %d\n", sign,
+                test_cvt_testcases[i].expsign);
+    }
+}
+
 static int _vsnwprintf_wrapper(wchar_t *str, size_t len, const wchar_t *format, ...)
 {
     int ret;
@@ -774,6 +858,7 @@ START_TEST(printf)
     test_swprintf();
     test_snprintf();
     test_fcvt();
+    test_xcvt();
     test_vsnwprintf();
 
     p__vscprintf = (void *)GetProcAddress(GetModuleHandle("msvcrt.dll"), "_vscprintf");
-- 
1.6.2.1




More information about the wine-patches mailing list