msvcrt: Make _ecvt and _fcvt work (yet again)

Stephen Moehle smoehle at comcast.net
Tue Oct 17 01:27:33 CDT 2006


I am resubmitting my patches for _ecvt and _fcvt. They are the same as 
before except they are attached to avoid word-wrapping problems. The 
patches are alternate ways of implementing _ecvt and _fcvt. The first is 
my own implementation. The second uses ecvt_r and fcvt_r from glibc. 
Personally, I prefer the second mainly for its simplicity, but my 
testing shows my own implementation agrees closely with the versions of 
_ecvt and _fcvt in Visual Studio 2005. The existing code had at least 
the following problems: added a decimal point to the output, used 
scientific notation in the output, did not remove the leading '-' for 
negative numbers, and got the decimal point offset wrong for numbers 
like 0.0003.

As suggested, I am also attaching the results my testing. My 
implementation agrees with VS2005 except in the last case where the 0 
padding is wrong for _fcvt. The value and decimal place are still 
correct, however. The existing wine implementation is wrong in every 
case and never even close. If anyone wants to see the C program, I will 
provide it.

Why I created this patch: I noticed that the Topo program (map 
viewing/printing from National Geographic, version 3.4.3) was displaying 
coordinates using scientific notation and was using two decimal points. 
So instead of displaying 38.098 for the latitude, I was seeing 
3..8098e+01. I believe this also fixes  Bug 6413 in WineHQ Bugzilla.

From: Stephen Moehle <smoehle at comcast.net>

Changelog for my implementation of _ecvt and _fcvt:
msvcrt: Make _ecvt and _fcvt work by largely rewriting them.

Changelog for using ecvt_r and fcvt_r:
msvcrt: Make _ecvt and _fcvt work by using ecvt_r and fcvt_r.
-------------- next part --------------
>From e35a536371239f469ace202dd5a656eac3d8f646 Mon Sep 17 00:00:00 2001
From: Stephen Moehle <smoehle at comcast.net>
Date: Mon, 16 Oct 2006 21:34:06 -0700
Subject: msvcrt: Make _ecvt and _fcvt work by largely rewriting them.

---
 dlls/msvcrt/math.c |   93 ++++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 72 insertions(+), 21 deletions(-)

diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c
index f06166b..2c07f88 100644
--- a/dlls/msvcrt/math.c
+++ b/dlls/msvcrt/math.c
@@ -20,6 +20,7 @@
 #include "config.h"
 
 #include <stdio.h>
+#include <stdlib.h>
 #define __USE_ISOC9X 1
 #define __USE_ISOC99 1
 #include <math.h>
@@ -849,22 +850,67 @@ double CDECL _nextafter(double num, doub
   return retval;
 }
 
-/*********************************************************************
- *		_ecvt (MSVCRT.@)
- */
-char * CDECL _ecvt( double number, int ndigits, int *decpt, int *sign )
+
+static char *efcvt_helper(double number, int ndigits,  int *decpt, int *sign)
 {
     thread_data_t *data = msvcrt_get_thread_data();
-    char *dec;
+    char *exp;
+    char *buf;
 
     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;
-    return data->efcvt_buffer;
+    buf = data->efcvt_buffer;
+
+    if (ndigits < 15) {
+        ndigits = 15;
+    }
+
+    snprintf(buf, 80, "%.*e", ndigits, number);
+
+    if (*buf == '-') {
+        *sign = 1;
+        ++buf;
+    }
+    else {
+        *sign = 0;
+    }
+
+    exp = strchr(buf, 'e');
+    if (exp) {
+        *decpt = atoi(exp + 1) + 1;
+        *exp = '\0';
+    }
+    else {
+        *decpt = 1;
+    }
+
+    if (*(buf + 1) == '.') {
+        memmove(buf + 1, buf + 2, strlen(buf + 1));
+    }
+
+    return buf;
+}
+
+/*********************************************************************
+ *		_ecvt (MSVCRT.@)
+ */
+char * CDECL _ecvt( double number, int ndigits, int *decpt, int *sign )
+{
+    char *buf = efcvt_helper(number, ndigits, decpt, sign);
+    /* manually round to ndigits */
+    if (ndigits == 0) {
+        *buf = '\0';
+    }
+    else {
+        int len = strlen(buf);
+        if (len > ndigits) {
+            double d = atof(buf);
+            d /= pow(10.0, len - ndigits);
+            snprintf(buf, 79, "%.0f", d);
+        }
+    }
+    return buf;
 }
 
 /***********************************************************************
@@ -872,17 +918,22 @@ char * CDECL _ecvt( double number, int n
  */
 char * CDECL _fcvt( double number, int ndigits, int *decpt, int *sign )
 {
-    thread_data_t *data = msvcrt_get_thread_data();
-    char *dec;
-
-    if (!data->efcvt_buffer)
-        data->efcvt_buffer = MSVCRT_malloc( 80 ); /* ought to be enough */
-
-    snprintf(data->efcvt_buffer, 80, "%.*e", ndigits, number);
-    *sign = (number < 0);
-    dec = strchr(data->efcvt_buffer, '.');
-    *decpt = (dec) ? dec - data->efcvt_buffer : -1;
-    return data->efcvt_buffer;
+    char *buf = efcvt_helper(number, ndigits, decpt, sign);
+    /* manually round to ndigits */
+    int len = strlen(buf);
+    int digs = ndigits + *decpt;
+    if (digs < 0) {
+        *buf = '\0';
+    }
+    else if (len > digs) {
+        double d = atof(buf);
+        d /= pow(10.0, len - digs);
+        snprintf(buf, 79, "%.0f", d);
+        if (*buf == '0') {
+            *buf = '\0';
+        }
+    }
+    return buf;
 }
 
 /***********************************************************************
-- 
1.4.2.3

-------------- next part --------------
>From 2e9cbe9d26e569a145df46635b6a9406b9a65351 Mon Sep 17 00:00:00 2001
From: Stephen Moehle <smoehle at comcast.net>
Date: Sun, 15 Oct 2006 20:28:01 -0700
Subject: msvcrt: Make _ecvt and _fcvt work by using ecvt_r and fcvt_r.

---
 dlls/msvcrt/math.c |   17 +++++++----------
 1 files changed, 7 insertions(+), 10 deletions(-)

diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c
index f06166b..d0d3f29 100644
--- a/dlls/msvcrt/math.c
+++ b/dlls/msvcrt/math.c
@@ -20,6 +20,11 @@
 #include "config.h"
 
 #include <stdio.h>
+#ifdef __USE_MISC
+#undef __USE_MISC
+#endif
+#define __USE_MISC
+#include <stdlib.h>
 #define __USE_ISOC9X 1
 #define __USE_ISOC99 1
 #include <math.h>
@@ -855,15 +860,11 @@ double CDECL _nextafter(double num, doub
 char * CDECL _ecvt( double number, int ndigits, int *decpt, int *sign )
 {
     thread_data_t *data = msvcrt_get_thread_data();
-    char *dec;
 
     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;
+    ecvt_r(number, ndigits, decpt, sign, data->efcvt_buffer, 80);
     return data->efcvt_buffer;
 }
 
@@ -873,15 +874,11 @@ char * CDECL _ecvt( double number, int n
 char * CDECL _fcvt( double number, int ndigits, int *decpt, int *sign )
 {
     thread_data_t *data = msvcrt_get_thread_data();
-    char *dec;
 
     if (!data->efcvt_buffer)
         data->efcvt_buffer = MSVCRT_malloc( 80 ); /* ought to be enough */
 
-    snprintf(data->efcvt_buffer, 80, "%.*e", ndigits, number);
-    *sign = (number < 0);
-    dec = strchr(data->efcvt_buffer, '.');
-    *decpt = (dec) ? dec - data->efcvt_buffer : -1;
+    fcvt_r(number, ndigits, decpt, sign, data->efcvt_buffer, 80);
     return data->efcvt_buffer;
 }
 
-- 
1.4.2.3

-------------- next part --------------
	   number	ndigits
input: 12.34567 4
           output decpt sign
vc++ ecvt: 1235 2 0
my   ecvt: 1235 2 0
wine ecvt: 1.234567e+001 1 0
vc++ fcvt: 123457 2 0
my   fcvt: 123457 2 0
wine fcvt: 1.2346e+001 1 0

input: 12.34567 0
           output decpt sign
vc++ ecvt:  2 0
my   ecvt:  2 0
wine ecvt: 1.234567e+001 1 0
vc++ fcvt: 12 2 0
my   fcvt: 12 2 0
wine fcvt: 1e+001 -1 0

input: -123.4567 8
           output decpt sign
vc++ ecvt: 12345670 3 1
my   ecvt: 12345670 3 1
wine ecvt: -1.234567e+002 2 1
vc++ fcvt: 12345670000 3 1
my   fcvt: 12345670000 3 1
wine fcvt: -1.23456700e+002 2 1

input: .0003 6
           output decpt sign
vc++ ecvt: 300000 -3 0
my   ecvt: 300000 -3 0
wine ecvt: 3.000000e-004 1 0
vc++ fcvt: 300 -3 0
my   fcvt: 300 -3 0
wine fcvt: 3.000000e-004 1 0

input: .0003 4
           output decpt sign
vc++ ecvt: 3000 -3 0
my   ecvt: 3000 -3 0
wine ecvt: 3.000000e-004 1 0
vc++ fcvt: 3 -3 0
my   fcvt: 3 -3 0
wine fcvt: 3.0000e-004 1 0

input: .0003 3
           output decpt sign
vc++ ecvt: 300 -3 0
my   ecvt: 300 -3 0
wine ecvt: 3.000000e-004 1 0
vc++ fcvt:  -3 0
my   fcvt:  -3 0
wine fcvt: 3.000e-004 1 0

input: .0003 1
           output decpt sign
vc++ ecvt: 3 -3 0
my   ecvt: 3 -3 0
wine ecvt: 3.000000e-004 1 0
vc++ fcvt:  -3 0
my   fcvt:  -3 0
wine fcvt: 3.0e-004 1 0

input: .0003 0
           output decpt sign
vc++ ecvt:  -3 0
my   ecvt:  -3 0
wine ecvt: 3.000000e-004 1 0
vc++ fcvt:  -3 0
my   fcvt:  -3 0
wine fcvt: 3e-004 -1 0

input: 1234567.89 4
           output decpt sign
vc++ ecvt: 1235 7 0
my   ecvt: 1235 7 0
wine ecvt: 1.234568e+006 1 0
vc++ fcvt: 12345678900 7 0
my   fcvt: 12345678900 7 0
wine fcvt: 1.2346e+006 1 0

input: 1234567.89 8
           output decpt sign
vc++ ecvt: 12345679 7 0
my   ecvt: 12345679 7 0
wine ecvt: 1.234568e+006 1 0
vc++ fcvt: 123456789000000 7 0
my   fcvt: 123456789000000 7 0
wine fcvt: 1.23456789e+006 1 0

input: 1234567.89 14
           output decpt sign
vc++ ecvt: 12345678900000 7 0
my   ecvt: 12345678900000 7 0
wine ecvt:  1.234568e+006 2 0
vc++ fcvt: 123456788999999990000 7 0
my   fcvt: 1234567890000000 7 0
wine fcvt: 1.23456789000000e+006 1 0


More information about the wine-patches mailing list