[goffice] dtoa: new implementation from musl.



commit 9c301394fc1721a090dcd0f7c4914beddf35dbe6
Author: Morten Welinder <terra gnome org>
Date:   Fri Apr 4 00:03:59 2014 -0400

    dtoa: new implementation from musl.
    
    This was adapted to implement round-ties-away-from-zero.  (Aka biased
    rounding.)  This is a printf-style flag, so if need be we can make
    it a compile setting.

 ChangeLog                   |    6 +
 NEWS                        |    2 +-
 goffice/Makefile.am         |    1 +
 goffice/math/go-dtoa.c      |  465 +++++++++++++++++++++++++++++++++++++++++++
 goffice/math/go-dtoa.h      |   17 ++
 goffice/math/go-math.c      |  174 ----------------
 goffice/math/go-math.h      |    6 +-
 goffice/math/goffice-math.h |    1 +
 goffice/utils/go-format.c   |   50 +++---
 9 files changed, 517 insertions(+), 205 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 72e2e14..debb2f5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2014-04-04  Morten Welinder  <terra gnome org>
+
+       * goffice/math/go-dtoa.c (go_dtoa): New function.
+
+       * goffice/utils/go-format.c: Use go_dtoa extensively.
+
 2014-04-02  Morten Welinder  <terra gnome org>
 
        * goffice/utils/go-format.c (go_format_output_number_to_odf): Also
diff --git a/NEWS b/NEWS
index 7c8c1a0..bf8c9b7 100644
--- a/NEWS
+++ b/NEWS
@@ -8,8 +8,8 @@ Morten:
        * Implement numbered colours in number formats.
        * Fix plugin deactivation.
        * Plug leaks.
-       * New function go_ascii_dtoa.
        * Fix rounding problem.
+       * Use musl code for dtoa.  Adapt to round-ties-away-from-0.
 
 --------------------------------------------------------------------------
 goffice 0.10.13:
diff --git a/goffice/Makefile.am b/goffice/Makefile.am
index e4865eb..22ccda8 100644
--- a/goffice/Makefile.am
+++ b/goffice/Makefile.am
@@ -338,6 +338,7 @@ gtk_HEADERS =                                       \
 
 math_SOURCES =                                 \
        math/go-accumulator.c                   \
+       math/go-dtoa.c                          \
        math/go-math.c                          \
        math/go-rangefunc.c                     \
        math/go-regression.c                    \
diff --git a/goffice/math/go-dtoa.c b/goffice/math/go-dtoa.c
new file mode 100644
index 0000000..58ad350
--- /dev/null
+++ b/goffice/math/go-dtoa.c
@@ -0,0 +1,465 @@
+/*
+ * go-dtoa.c: double-to-string conversion.
+ *
+ * Copyright 2014 Morten Welinder <terra gnome org>
+ *
+ *
+ *
+ * Large portions of this code was copied from http://www.musl-libc.org/
+ * under the following license:
+ *
+ * Copyright © 2005-2014 Rich Felker, et al.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/math/go-math.h>
+#include "go-dtoa.h"
+#include <string.h>
+#include <inttypes.h>
+#include <stdarg.h>
+
+typedef GString FAKE_FILE;
+
+/* musl code starts here */
+
+/* Some useful macros */
+
+#ifdef MUSL_ORIGINAL
+#define MAX(a,b) ((a)>(b) ? (a) : (b))
+#define MIN(a,b) ((a)<(b) ? (a) : (b))
+#endif
+#define CONCAT2(x,y) x ## y
+#define CONCAT(x,y) CONCAT2(x,y)
+
+/* Convenient bit representation for modifier flags, which all fall
+ * within 31 codepoints of the space character. */
+
+#define ALT_FORM   (1U<<('#'-' '))
+#define ZERO_PAD   (1U<<('0'-' '))
+#define LEFT_ADJ   (1U<<('-'-' '))
+#define PAD_POS    (1U<<(' '-' '))
+#define MARK_POS   (1U<<('+'-' '))
+#define GROUPED    (1U<<('\''-' '))
+#ifdef MUSL_ORIGINAL
+#define FLAGMASK (ALT_FORM|ZERO_PAD|LEFT_ADJ|PAD_POS|MARK_POS|GROUPED)
+#else
+#define FLAG_TIES_AWAY_0 (1U<<('"'-' '))
+#define FLAG_SHORTEST (1U<<('!'-' '))
+#define FLAG_TRUNCATE (1U<<('='-' '))
+#define FLAG_ASCII (1U<<(','-' '))
+#endif
+
+
+static void out(FAKE_FILE *f, const char *s, size_t l)
+{
+#ifdef MUSL_ORIGINAL
+  __fwritex((void *)s, l, f);
+#else
+  g_string_append_len (f, s, l);
+#endif
+}
+
+#ifndef MUSL_ORIGINAL
+static void
+outdecimal (FAKE_FILE *f, int fl)
+{
+       if (fl & FLAG_ASCII)
+               out(f, ".", 1);
+       else {
+               GString const *dec = go_locale_get_decimal();
+               out(f, dec->str, dec->len);
+       }
+}
+#endif
+
+
+static void pad(FAKE_FILE *f, char c, int w, int l, int fl)
+{
+       char pad[256];
+       if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return;
+       l = w - l;
+#ifdef MUSL_ORIGINAL
+       memset(pad, c, l>sizeof pad ? sizeof pad : l);
+       for (; l >= sizeof pad; l -= sizeof pad)
+               out(f, pad, sizeof pad);
+#else
+       /* Just kill some warnings */
+       memset(pad, c, (size_t)l>sizeof pad ? sizeof pad : (size_t)l);
+       for (; (size_t)l >= sizeof pad; l -= sizeof pad)
+               out(f, pad, sizeof pad);
+#endif
+       out(f, pad, l);
+}
+
+static const char xdigits[16] = {
+       "0123456789ABCDEF"
+};
+
+static char *fmt_u(uintmax_t x, char *s)
+{
+       unsigned long y;
+       for (   ; x>ULONG_MAX; x/=10) *--s = '0' + x%10;
+       for (y=x;           y; y/=10) *--s = '0' + y%10;
+       return s;
+}
+
+/* Do not override this check. The floating point printing code below
+ * depends on the float.h constants being right. If they are wrong, it
+ * may overflow the stack. */
+#if LDBL_MANT_DIG == 53
+typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)];
+#endif
+
+static int fmt_fp(FAKE_FILE *f, long double y, int w, int p, int fl, int t)
+{
+       uint32_t big[(LDBL_MANT_DIG+28)/29 + 1          // mantissa expansion
+               + (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion
+       uint32_t *a, *d, *r, *z;
+#ifdef MUSL_ORIGINAL
+       int e2=0, e, i, j, l;
+#else
+       /* Make i unsigned kills some warnings */
+       int e2=0, e, j, l;
+       unsigned i;
+#endif
+       char buf[9+LDBL_MANT_DIG/4], *s;
+       const char *prefix="-0X+0X 0X-0x+0x 0x";
+       int pl;
+       char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr;
+
+#ifndef MUSL_ORIGINAL
+       if (fl & FLAG_TRUNCATE) g_string_truncate (f, 0);
+#endif
+
+       pl=1;
+       if (signbit(y)) {
+               y=-y;
+       } else if (fl & MARK_POS) {
+               prefix+=3;
+       } else if (fl & PAD_POS) {
+               prefix+=6;
+       } else prefix++, pl=0;
+
+       if (!go_finitel(y)) {
+               const char *s = (t&32)?"inf":"INF";
+               if (y!=y) s=(t&32)?"nan":"NAN", pl=0;
+               pad(f, ' ', w, 3+pl, fl&~ZERO_PAD);
+               out(f, prefix, pl);
+               out(f, s, 3);
+               pad(f, ' ', w, 3+pl, fl^LEFT_ADJ);
+               return MAX(w, 3+pl);
+       }
+
+       y = frexpl(y, &e2) * 2;
+       if (y) e2--;
+
+       if ((t|32)=='a') {
+               long double round = 8.0;
+               int re;
+
+               if (t&32) prefix += 9;
+               pl += 2;
+
+               if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0;
+               else re=LDBL_MANT_DIG/4-1-p;
+
+               if (re) {
+                       while (re--) round*=16;
+                       if (*prefix=='-') {
+                               y=-y;
+                               y-=round;
+                               y+=round;
+                               y=-y;
+                       } else {
+                               y+=round;
+                               y-=round;
+                       }
+               }
+
+               estr=fmt_u(e2<0 ? -e2 : e2, ebuf);
+               if (estr==ebuf) *--estr='0';
+               *--estr = (e2<0 ? '-' : '+');
+               *--estr = t+('p'-'a');
+
+               s=buf;
+               do {
+                       int x=y;
+                       *s++=xdigits[x]|(t&32);
+                       y=16*(y-x);
+                       if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.';
+               } while (y);
+
+               if (p && s-buf-2 < p)
+                       l = (p+2) + (ebuf-estr);
+               else
+                       l = (s-buf) + (ebuf-estr);
+
+               pad(f, ' ', w, pl+l, fl);
+               out(f, prefix, pl);
+               pad(f, '0', w, pl+l, fl^ZERO_PAD);
+               out(f, buf, s-buf);
+               pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0);
+               out(f, estr, ebuf-estr);
+               pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
+               return MAX(w, pl+l);
+       }
+       if (p<0) p=6;
+
+       if (y) y *= 0x1p28, e2-=28;
+
+       if (e2<0) a=r=z=big;
+       else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1;
+
+       do {
+               *z = y;
+               y = 1000000000*(y-*z++);
+       } while (y);
+
+       while (e2>0) {
+               uint32_t carry=0;
+               int sh=MIN(29,e2);
+               for (d=z-1; d>=a; d--) {
+                       uint64_t x = ((uint64_t)*d<<sh)+carry;
+                       *d = x % 1000000000;
+                       carry = x / 1000000000;
+               }
+               if (!z[-1] && z>a) z--;
+               if (carry) *--a = carry;
+               e2-=sh;
+       }
+       while (e2<0) {
+               uint32_t carry=0, *b;
+               int sh=MIN(9,-e2), need=1+(p+LDBL_MANT_DIG/3+8)/9;
+               for (d=a; d<z; d++) {
+                       uint32_t rm = *d & ((1<<sh)-1);
+                       *d = (*d>>sh) + carry;
+                       carry = (1000000000>>sh) * rm;
+               }
+               if (!*a) a++;
+               if (carry) *z++ = carry;
+               /* Avoid (slow!) computation past requested precision */
+               b = (t|32)=='f' ? r : a;
+               if (z-b > need) z = b+need;
+               e2+=sh;
+       }
+
+       if (a<z) for (i=10, e=9*(r-a); *a>=i; i*=10, e++);
+       else e=0;
+
+       /* Perform rounding: j is precision after the radix (possibly neg) */
+       j = p - ((t|32)!='f')*e - ((t|32)=='g' && p);
+       if (j < 9*(z-r-1)) {
+               uint32_t x;
+               /* We avoid C's broken division of negative numbers */
+               d = r + 1 + ((j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP);
+               j += 9*LDBL_MAX_EXP;
+               j %= 9;
+               for (i=10, j++; j<9; i*=10, j++);
+               x = *d % i;
+               /* Are there any significant digits past j? */
+               if (x || d+1!=z) {
+                       long double round = CONCAT(0x1p,LDBL_MANT_DIG);
+                       long double small;
+#ifdef MUSL_ORIGINAL
+                       if (*d/i & 1) round += 2;
+#else
+                       /*
+                        * For ties-away-from-zero, just pretend we have
+                        * an odd digit.
+                        */
+                       if ((fl & FLAG_TIES_AWAY_0) || (*d/i & 1)) round += 2;
+#endif
+                       if (x<i/2) small=0x0.8p0;
+                       else if (x==i/2 && d+1==z) small=0x1.0p0;
+                       else small=0x1.8p0;
+                       if (pl && *prefix=='-') round*=-1, small*=-1;
+                       *d -= x;
+                       /* Decide whether to round by probing round+small */
+                       if (round+small != round) {
+                               *d = *d + i;
+                               while (*d > 999999999) {
+                                       *d--=0;
+                                       (*d)++;
+                               }
+                               if (d<a) a=d;
+                               for (i=10, e=9*(r-a); *a>=i; i*=10, e++);
+                       }
+               }
+               if (z>d+1) z=d+1;
+               for (; !z[-1] && z>a; z--);
+       }
+       
+       if ((t|32)=='g') {
+               if (!p) p++;
+               if (p>e && e>=-4) {
+                       t--;
+                       p-=e+1;
+               } else {
+                       t-=2;
+                       p--;
+               }
+               if (!(fl&ALT_FORM)) {
+                       /* Count trailing zeros in last place */
+                       if (z>a && z[-1]) for (i=10, j=0; z[-1]%i==0; i*=10, j++);
+                       else j=9;
+                       if ((t|32)=='f')
+                               p = MIN(p,MAX(0,9*(z-r-1)-j));
+                       else
+                               p = MIN(p,MAX(0,9*(z-r-1)+e-j));
+               }
+       }
+       l = 1 + p + (p || (fl&ALT_FORM));
+       if ((t|32)=='f') {
+               if (e>0) l+=e;
+       } else {
+               estr=fmt_u(e<0 ? -e : e, ebuf);
+               while(ebuf-estr<2) *--estr='0';
+               *--estr = (e<0 ? '-' : '+');
+               *--estr = t;
+               l += ebuf-estr;
+       }
+
+       pad(f, ' ', w, pl+l, fl);
+       out(f, prefix, pl);
+       pad(f, '0', w, pl+l, fl^ZERO_PAD);
+
+       if ((t|32)=='f') {
+               if (a>r) a=r;
+               for (d=a; d<=r; d++) {
+                       char *s = fmt_u(*d, buf+9);
+                       if (d!=a) while (s>buf) *--s='0';
+                       else if (s==buf+9) *--s='0';
+                       out(f, s, buf+9-s);
+               }
+#ifdef MUSL_ORIGINAL
+               if (p || (fl&ALT_FORM)) out(f, ".", 1);
+#else
+               if (p || (fl&ALT_FORM)) outdecimal(f, fl);
+#endif
+               for (; d<z && p>0; d++, p-=9) {
+                       char *s = fmt_u(*d, buf+9);
+                       while (s>buf) *--s='0';
+                       out(f, s, MIN(9,p));
+               }
+               pad(f, '0', p+9, 9, 0);
+       } else {
+               if (z<=a) z=a+1;
+               for (d=a; d<z && p>=0; d++) {
+                       char *s = fmt_u(*d, buf+9);
+                       if (s==buf+9) *--s='0';
+                       if (d!=a) while (s>buf) *--s='0';
+                       else {
+                               out(f, s++, 1);
+#ifdef MUSL_ORIGINAL
+                               if (p>0||(fl&ALT_FORM)) out(f, ".", 1);
+#else
+                               if (p>0||(fl&ALT_FORM)) outdecimal(f, fl);
+#endif
+                       }
+                       out(f, s, MIN(buf+9-s, p));
+                       p -= buf+9-s;
+               }
+               pad(f, '0', p+18, 18, 0);
+               out(f, estr, ebuf-estr);
+       }
+
+       pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
+
+       return MAX(w, pl+l);
+}
+
+/* musl code ends here */
+
+static void
+parse_fmt (const char *fmt, va_list args,
+          int *w, int *p, int *fl, int *t, long double *d)
+{
+       gboolean is_long = FALSE;
+
+       *w = 1;
+       *p = -1;
+       *fl = 0;
+       *d = 0;
+       *t = 'g';
+
+       while (1) {
+               switch (*fmt) {
+               case '0': *fl |= ZERO_PAD; fmt++; continue;
+               case '^': *fl |= FLAG_TIES_AWAY_0; fmt++; continue;
+               case '+': *fl |= MARK_POS; fmt++; continue;
+               case '-': *fl |= LEFT_ADJ; fmt++; continue;
+               case '!': *fl |= FLAG_SHORTEST; fmt++; continue;
+               case '=': *fl |= FLAG_TRUNCATE; fmt++; continue;
+               case ',': *fl |= FLAG_ASCII; fmt++; continue;
+               }
+               break;
+       }
+
+       while (g_ascii_isdigit (*fmt))
+               *w = *w * 10 + (*fmt++ - '0');
+
+       if (*fmt == '.') {
+               if (fmt[1] == '*') {
+                       fmt += 2;
+                       *p = va_arg (args, int);
+               } else {
+                       *p = 0;
+                       fmt++;
+                       while (g_ascii_isdigit (*fmt))
+                               *p = *p * 10 + (*fmt++ - '0');
+               }
+       }
+
+       if (*fmt == 'L') {
+               is_long = TRUE;
+               fmt++;
+       }
+
+       if (!strchr ("efgaEFGA", *fmt))
+               return;
+       *t = *fmt;
+
+       if (is_long)
+               *d = va_arg (args, long double);
+       else
+               *d = va_arg (args, double);
+}
+
+
+
+void
+go_dtoa (GString *dst, const char *fmt, ...)
+{
+       int w, p, fl, t;
+       va_list args;
+       long double d;
+       gboolean debug = FALSE;
+
+       va_start (args, fmt);
+       parse_fmt (fmt, args, &w, &p, &fl, &t, &d);
+       va_end (args);
+
+       if (fl & FLAG_SHORTEST) p = 17;
+       if (debug) g_printerr ("%Lg [%s] t=%c  p=%d\n", d, fmt, t, p);
+       fmt_fp (dst, d, w, p, fl, t);
+       if (debug) g_printerr ("  --> %s\n", dst->str);
+}
diff --git a/goffice/math/go-dtoa.h b/goffice/math/go-dtoa.h
new file mode 100644
index 0000000..24f80b1
--- /dev/null
+++ b/goffice/math/go-dtoa.h
@@ -0,0 +1,17 @@
+#ifndef GO_DTOA_H__
+#define GO_DTOA_H__
+
+#include <goffice/goffice-features.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/* ------------------------------------------------------------------------- */
+
+void go_dtoa (GString *dst, const char *fmt, ...);
+
+/* ------------------------------------------------------------------------- */
+
+G_END_DECLS
+
+#endif /* GO_DTOA_H__ */
diff --git a/goffice/math/go-math.c b/goffice/math/go-math.c
index 5fc01a4..d568e1a 100644
--- a/goffice/math/go-math.c
+++ b/goffice/math/go-math.c
@@ -452,180 +452,6 @@ go_ascii_strtod (const char *s, char **end)
        return res;
 }
 
-static void
-go_ascii_dtoa_fmt_helper (char *buf, size_t bufsiz, char fmt,
-                         double d, int prec)
-{
-       char fmtstr[8], *p = fmtstr;
-
-       *p++ = '%';
-       *p++ = '.';
-       if (prec >= 10) *p++ = '0' + (prec / 10);
-       *p++ = '0' + (prec % 10);
-       *p++ = fmt;
-       *p = 0;
-
-       g_ascii_formatd (buf, bufsiz, fmtstr, d);
-}
-
-/**
- * go_ascii_dtoa:
- * @d: value to convert
- * @fmt: printf-style format specified; 'e', 'E', or 'g'.
- *
- * Returns: (transfer full): a string that when converted back into a floating-
- * point value will produce @d.  This function will use '.' and decimal
- * point.
- *
- * The resulting string is not necessarily the shortest possible, but some
- * care is put into avoiding needless ...0003 and ...9998 endings.
- */
-char *
-go_ascii_dtoa (double d, char fmt)
-{
-       char buf[128];
-       char *epos;
-       gboolean enotation;
-       static int prec;
-
-       if (!prec) {
-               double l10 = log10 (FLT_RADIX);
-               prec = (int)ceil (DBL_MANT_DIG * l10) + (l10 != (int)l10);
-               g_assert (prec >= 11 && prec <= 99);
-       }
-
-       if (!go_finite (d)) {
-               if (d > 0)
-                       return g_strdup ("+inf");
-               if (d < 0)
-                       return g_strdup ("-inf");
-               return g_strdup ("nan");
-       }
-
-       if (fabs (d) >= 1e16 || (d != 0 && fabs (d) < 1e-4))
-               fmt = g_ascii_islower (fmt) ? 'e' : 'E';
-       enotation = g_ascii_tolower (fmt) == 'e';
-
-       /* e-notation counts digits after the decimal point.  */
-       if (enotation)
-               prec--;
-
-       go_ascii_dtoa_fmt_helper (buf, 128, fmt, d, prec);
-       epos = (enotation ? strchr (buf, fmt) : buf + strlen (buf));
-       if (epos[-1] == '0') {
-               /* Nothing */
-       } else if ((epos[-1] <= '5' && epos[-2] == '0' && epos[-3] == '0') ||
-                  (epos[-1] >= '5' && epos[-2] == '9' && epos[-3] == '9')) {
-               char bufm1[128];
-               go_ascii_dtoa_fmt_helper (bufm1, 128, fmt, d, prec - 1);
-               if (go_ascii_strtod (bufm1, NULL) == d) {
-                       strcpy (buf, bufm1);
-                       epos = (enotation ? strchr (buf, fmt) : buf + strlen (buf));
-               }
-       }
-
-       if (epos[-1] == '0' && strchr (buf, '.')) {
-               char *p = epos;
-               while (p[-1] == '0') p--;
-               if (p[-1] == '.') p--;
-               memmove (p, epos, strlen (epos) + 1);
-       }
-
-       return g_strdup (buf);
-}
-
-
-#ifdef GOFFICE_WITH_LONG_DOUBLE
-
-static void
-go_ascii_ldtoa_fmt_helper (char *buf, size_t bufsiz, char fmt,
-                          long double d, int prec)
-{
-       char fmtstr[8], *p = fmtstr;
-       char *old_locale;
-
-       *p++ = '%';
-       *p++ = '.';
-       if (prec >= 10) *p++ = '0' + (prec / 10);
-       *p++ = '0' + (prec % 10);
-       *p++ = fmt;
-       *p = 0;
-
-       /* Just use setlocale and hope for the best.  */
-       old_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
-       g_snprintf (buf, bufsiz, fmtstr, d);
-       setlocale (LC_NUMERIC, old_locale);
-       g_free (old_locale);
-}
-
-
-/**
- * go_ascii_ldtoa:
- * @d: value to convert
- * @fmt: printf-style format specified; 'e', 'E', or 'g'.
- *
- * Returns: (transfer full): a string that when converted back into a floating-
- * point value will produce @d.  This function will use '.' and decimal
- * point.
- *
- * The resulting string is not necessarily the shortest possible, but some
- * care is put into avoiding needless ...0003 and ...9998 endings.
- */
-char *
-go_ascii_ldtoa (long double d, char fmt)
-{
-       char buf[128];
-       char *epos;
-       gboolean enotation;
-       static int prec;
-
-       if (!prec) {
-               double l10 = log10 (FLT_RADIX);
-               prec = (int)ceil (LDBL_MANT_DIG * l10) + (l10 != (int)l10);
-               g_assert (prec >= 11 && prec <= 99);
-       }
-
-       if (!go_finitel (d)) {
-               if (d > 0)
-                       return g_strdup ("+inf");
-               if (d < 0)
-                       return g_strdup ("-inf");
-               return g_strdup ("nan");
-       }
-
-       if (fabsl (d) >= 1e16 || (d != 0 && fabsl (d) < 1e-4))
-               fmt = g_ascii_islower (fmt) ? 'e' : 'E';
-       enotation = g_ascii_tolower (fmt) == 'e';
-
-       /* e-notation counts digits after the decimal point.  */
-       if (enotation)
-               prec--;
-
-       go_ascii_ldtoa_fmt_helper (buf, 128, fmt, d, prec);
-       epos = (enotation ? strchr (buf, fmt) : buf + strlen (buf));
-       if (epos[-1] == '0') {
-               /* Nothing */
-       } else if ((epos[-1] <= '5' && epos[-2] == '0' && epos[-3] == '0') ||
-                  (epos[-1] >= '5' && epos[-2] == '9' && epos[-3] == '9')) {
-               char bufm1[128];
-               go_ascii_ldtoa_fmt_helper (bufm1, 128, fmt, d, prec - 1);
-               if (go_ascii_strtold (bufm1, NULL) == d) {
-                       strcpy (buf, bufm1);
-                       epos = (enotation ? strchr (buf, fmt) : buf + strlen (buf));
-               }
-       }
-
-       if (epos[-1] == '0' && strchr (buf, '.')) {
-               char *p = epos;
-               while (p[-1] == '0') p--;
-               if (p[-1] == '.') p--;
-               memmove (p, epos, strlen (epos) + 1);
-       }
-
-       return g_strdup (buf);
-}
-#endif
-
 
 #ifdef GOFFICE_SUPPLIED_LOG1P
 double
diff --git a/goffice/math/go-math.h b/goffice/math/go-math.h
index 5a05fbc..84f2c94 100644
--- a/goffice/math/go-math.h
+++ b/goffice/math/go-math.h
@@ -1,8 +1,8 @@
 #ifndef __GO_MATH_H
 #define __GO_MATH_H
 
-#include <math.h>
 #include <goffice/goffice-features.h>
+#include <math.h>
 #include <glib.h>
 #include <goffice/goffice.h>
 
@@ -39,8 +39,6 @@ double go_pow10 (int n);
 double go_strtod (const char *s, char **end);
 double go_ascii_strtod (const char *s, char **end);
 
-char *go_ascii_dtoa (double d, char fmt);
-
 double go_sinpi (double x);
 double go_cospi (double x);
 double go_tanpi (double x);
@@ -87,8 +85,6 @@ long double go_pow10l (int n);
 long double go_strtold (const char *s, char **end);
 long double go_ascii_strtold (const char *s, char **end);
 
-char *go_ascii_ldtoa (long double d, char fmt);
-
 long double go_sinpil (long double x);
 long double go_cospil (long double x);
 long double go_tanpil (long double x);
diff --git a/goffice/math/goffice-math.h b/goffice/math/goffice-math.h
index b9fd172..519afbe 100644
--- a/goffice/math/goffice-math.h
+++ b/goffice/math/goffice-math.h
@@ -18,6 +18,7 @@ typedef struct GOQuadQRl_ GOQuadQRl;
 #include <goffice/math/go-complex.h>
 #include <goffice/math/go-cspline.h>
 #include <goffice/math/go-distribution.h>
+#include <goffice/math/go-dtoa.h>
 #include <goffice/math/go-fft.h>
 #include <goffice/math/go-math.h>
 #include <goffice/math/go-matrix.h>
diff --git a/goffice/utils/go-format.c b/goffice/utils/go-format.c
index 41b4858..09a39e0 100644
--- a/goffice/utils/go-format.c
+++ b/goffice/utils/go-format.c
@@ -2856,7 +2856,7 @@ SUFFIX(printf_engineering) (GString *dst, DOUBLE val, int n, int wd)
        const GString *decimal = go_locale_get_decimal ();
 
        if (wd <= 1 || val == 0 || !SUFFIX(go_finite) (val)) {
-               g_string_printf (dst, "%.*" FORMAT_E, n, val);
+               go_dtoa (dst, "=^.*" FORMAT_E, n, val);
                return;
        }
 
@@ -2866,7 +2866,7 @@ SUFFIX(printf_engineering) (GString *dst, DOUBLE val, int n, int wd)
                ? exponent_guess % wd
                : (wd - ((-exponent_guess) % wd)) % wd;
 
-       g_string_printf (dst, "%.*" FORMAT_E, n + nde, val);
+       go_dtoa (dst, "=^.*" FORMAT_E, n + nde, val);
        epos = (char *)strchr (dst->str, 'E');
        if (!epos)
                return;
@@ -4202,7 +4202,7 @@ SUFFIX(go_format_execute) (PangoLayout *layout, GString *dst,
                        const char *dot;
                        if (!numtxt)
                                numtxt = g_string_sized_new (100);
-                       g_string_printf (numtxt, "%.*" FORMAT_f, n, val);
+                       go_dtoa (numtxt, "=^.*" FORMAT_f, n, val);
                        dot = strstr (numtxt->str, decimal->str);
                        handle_chinese (numtxt, &dot,
                                        numeral_shape, shape_flags);
@@ -4797,6 +4797,9 @@ go_format_measure_strlen (const GString *str,
 static void
 drop_zeroes (GString *str, int *prec)
 {
+       if (*prec == 0)
+               return;
+
        while (str->str[str->len - 1] == '0') {
                g_string_truncate (str, str->len - 1);
                (*prec)--;
@@ -4925,7 +4928,7 @@ SUFFIX(go_render_general) (PangoLayout *layout, GString *str,
                g_print ("Room for whole part.\n");
 #endif
                if (val == SUFFIX(floor) (val) || digs_as_int == maxdigits) {
-                       g_string_printf (str, "%.0" FORMAT_f, val);
+                       go_dtoa (str, "=^.0" FORMAT_f, val);
                        HANDLE_NUMERAL_SHAPE;
                        HANDLE_SIGN (0);
                        SETUP_LAYOUT;
@@ -4937,7 +4940,7 @@ SUFFIX(go_render_general) (PangoLayout *layout, GString *str,
                g_print ("Maybe room for whole part.\n");
 #endif
 
-               g_string_printf (str, "%.0" FORMAT_f, val);
+               go_dtoa (str, "=^.0" FORMAT_f, val);
                HANDLE_NUMERAL_SHAPE;
                HANDLE_SIGN (0);
                SETUP_LAYOUT;
@@ -4950,10 +4953,10 @@ SUFFIX(go_render_general) (PangoLayout *layout, GString *str,
        }
 
        /* Number of digits in [aval].  */
-       digs = (aval >= 1 ? 1 + SUFFIX(ilog10) (aval) : 1);
+       digs = (aval >= 10 ? 1 + SUFFIX(ilog10) (aval) : 1);
 
        prec = maxdigits - digs;
-       g_string_printf (str, "%.*" FORMAT_f, prec, val);
+       go_dtoa (str, "=^.*" FORMAT_f, prec, val);
        if (check_val) {
                /*
                 * We're not width-limited; we may have to increase maxdigits
@@ -4961,7 +4964,7 @@ SUFFIX(go_render_general) (PangoLayout *layout, GString *str,
                 */
                if (val != STRTO(str->str, NULL)) {
                        maxdigits++, prec++;
-                       g_string_printf (str, "%.*" FORMAT_f, prec, val);
+                       go_dtoa (str, "=^.*" FORMAT_f, prec, val);
                }
        }
        HANDLE_NUMERAL_SHAPE;
@@ -4977,7 +4980,7 @@ SUFFIX(go_render_general) (PangoLayout *layout, GString *str,
                        return;
 
                prec--;
-               g_string_printf (str, "%.*" FORMAT_f, prec, val);
+               go_dtoa (str, "=^.*" FORMAT_f, prec, val);
                drop_zeroes (str, &prec);
                HANDLE_NUMERAL_SHAPE;
                HANDLE_SIGN (0);
@@ -5003,7 +5006,7 @@ SUFFIX(go_render_general) (PangoLayout *layout, GString *str,
                if (prec == 0 || !rounds_to_0) {
                        int w;
 
-                       g_string_printf (str, "%.0" FORMAT_E, val);
+                       go_dtoa (str, "=^.0" FORMAT_E, val);
                        HANDLE_NUMERAL_SHAPE;
                        HANDLE_SIGN (0);
                        epos = strchr (str->str, 'E') - str->str;
@@ -5020,7 +5023,7 @@ SUFFIX(go_render_general) (PangoLayout *layout, GString *str,
                goto zero;
        }
        prec = MIN (prec, PREFIX(DIG) - 1);
-       g_string_printf (str, "%.*" FORMAT_E, prec, val);
+       go_dtoa (str, "=^.*" FORMAT_E, prec, val);
        epos = strchr (str->str, 'E') - str->str;
        digs = 0;
        while (str->str[epos - 1 - digs] == '0')
@@ -5054,7 +5057,7 @@ SUFFIX(go_render_general) (PangoLayout *layout, GString *str,
                        if (prec < 0)
                                break;
                }
-               g_string_printf (str, "%.*" FORMAT_E, prec, val);
+               go_dtoa (str, "=^.*" FORMAT_E, prec, val);
        }
 
        if (rounds_to_0)
@@ -7508,7 +7511,7 @@ char *
 go_format_odf_style_map (GOFormat const *fmt, int cond_part)
 {
        char const *format_string;
-       char *res, *valstr;
+       GString *valstr;
 
        g_return_val_if_fail (fmt != NULL, NULL);
        g_return_val_if_fail (fmt->typ == GO_FMT_COND, NULL);
@@ -7541,11 +7544,9 @@ go_format_odf_style_map (GOFormat const *fmt, int cond_part)
                return NULL;
        }
 
-       valstr = go_ascii_dtoa (fmt->u.cond.conditions[cond_part].val, 'g');
-       res = g_strconcat (format_string, valstr, NULL);
-       g_free (valstr);
-
-       return res;
+       valstr = g_string_new (format_string);
+       go_dtoa (valstr, "!g", fmt->u.cond.conditions[cond_part].val);
+       return g_string_free (valstr, FALSE);
 }
 #endif
 
@@ -8931,9 +8932,8 @@ go_format_output_conditional_to_odf (GsfXMLOut *xout, gboolean with_extension,
                GOFormatCondition const *cond = &fmt->u.cond.conditions[i];
                const char *oper = NULL;
                double val = cond->val;
-               char *valstr;
                char *partname;
-               char *condition;
+               GString *condition;
 
                if (i == defi)
                        continue;
@@ -8955,17 +8955,17 @@ go_format_output_conditional_to_odf (GsfXMLOut *xout, gboolean with_extension,
 
                partname = g_strdup_printf ("%s-%d", name, i);
 
-               valstr = go_ascii_dtoa (val, 'g');
-               condition = g_strdup_printf ("value()%s%s", oper, valstr);
-               g_free (valstr);
+               condition = g_string_new ("value()");
+               g_string_append (condition, oper);
+               go_dtoa (condition, "!g", val);
 
                gsf_xml_out_start_element (xout, STYLE "map");
-               gsf_xml_out_add_cstr (xout, STYLE "condition", condition);
+               gsf_xml_out_add_cstr (xout, STYLE "condition", condition->str);
                gsf_xml_out_add_cstr (xout, STYLE "apply-style-name", partname);
                gsf_xml_out_end_element (xout); /* </style:map> */
 
                g_free (partname);
-               g_free (condition);
+               g_string_free (condition, TRUE);
        }
        /* Do we need to add a catch-all General?  */
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]