msvcrt: Make _ecvt and _fcvt work (again)

Stephen Moehle smoehle at comcast.net
Sun Oct 15 23:05:05 CDT 2006


Please ignore my previous patch for this. _ecvt and _fcvt needed a lot 
more work than I first realized. 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. 
This patch fixes all that and very closely matches the behavior of ecvt 
an fcvt in glibc and _ecvt and _fcvt in Visual Studio 2005. I have 
dropped the changes to _gcvt. I will deal with that later.

I have another patch that I will submit shortly that simply implements 
_ecvt and _fcvt in terms of ecvt_r and fcvt_r. I actually like that 
patch better since it is much simpler and leverages glibc better. I am 
submitting both patches because I am not sure why ecvt_r and fcvt_r were 
not used in the first place. Maybe they are not portable enough or 
something like that.

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 lattitude, I was seeing 
3..8098e+01. I believe this also fixes  Bug 6413 in WineHQ Bugzilla.

From: Stephen Moehle <smoehle at comcast.net>

Changelog
msvcrt: Make _ecvt and _fcvt work.

---
 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




More information about the wine-patches mailing list