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