[goffice] Math: Introduce go_ascii_dtoa.
- From: Morten Welinder <mortenw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [goffice] Math: Introduce go_ascii_dtoa.
- Date: Wed, 2 Apr 2014 13:51:25 +0000 (UTC)
commit e5e569d3fa6d391547804b57cd2b54efcc2f6175
Author: Morten Welinder <terra gnome org>
Date: Wed Apr 2 09:15:19 2014 -0400
Math: Introduce go_ascii_dtoa.
This will format a round-trip safe string from a double with a nod to
avoiding unnecessary ...00003 and ...99998 endings.
ChangeLog | 4 +
NEWS | 1 +
goffice/math/go-math.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++-
goffice/math/go-math.h | 4 +
tests/test-math.c | 34 +++++++++
5 files changed, 219 insertions(+), 1 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 0ba26b5..c86a6f4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2014-04-02 Morten Welinder <terra gnome org>
+
+ * goffice/math/go-math.c (go_ascii_dtoa): New function.
+
2014-04-01 Morten Welinder <terra gnome org>
* goffice/utils/go-format.c (go_format_output_conditional_to_odf):
diff --git a/NEWS b/NEWS
index 417b573..1fdf864 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,7 @@ Morten:
* Implement numbered colours in number formats.
* Fix plugin deactivation.
* Plug leaks.
+ * New function go_ascii_dtoa.
--------------------------------------------------------------------------
goffice 0.10.13:
diff --git a/goffice/math/go-math.c b/goffice/math/go-math.c
index 574403a..7e5efde 100644
--- a/goffice/math/go-math.c
+++ b/goffice/math/go-math.c
@@ -29,6 +29,7 @@
#include <locale.h>
#include <signal.h>
#include <errno.h>
+#include <string.h>
double go_nan;
double go_pinf;
@@ -68,7 +69,7 @@ running_under_buggy_valgrind (void)
void
_go_math_init (void)
{
- const char *bug_url = "http://bugzilla.gnome.org/enter_bug.cgi?product=libgoffice";
+ const char *bug_url = "https://bugzilla.gnome.org/enter_bug.cgi?product=libgoffice";
char *old_locale;
double d;
#ifdef SIGFPE
@@ -436,6 +437,180 @@ 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 07bd147..5a05fbc 100644
--- a/goffice/math/go-math.h
+++ b/goffice/math/go-math.h
@@ -39,6 +39,8 @@ 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);
@@ -85,6 +87,8 @@ 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/tests/test-math.c b/tests/test-math.c
index bc49bbb..f3d7c2b 100644
--- a/tests/test-math.c
+++ b/tests/test-math.c
@@ -91,10 +91,44 @@ trig_tests (void)
/* ------------------------------------------------------------------------- */
+#define TEST1(a_) do { \
+ double d = (a_); \
+ char *s = go_ascii_dtoa (d, 'g'); \
+ double r = go_ascii_strtod (s, NULL); \
+ g_printerr ("dtoa: %.17g --> \"%s\" --> %.17g\n", d, s, r); \
+ g_free (s); \
+ g_assert (r == d); \
+} while (0)
+
+static void
+test_dtoa (void)
+{
+ TEST1 (0.1);
+ TEST1 (go_pinf);
+ TEST1 (50388143.0682372152805328369140625);
+ TEST1 (54167628.179999999701976776123046875);
+ TEST1 (9161196241250.05078125);
+ TEST1 (9.87e+031);
+ TEST1 (9.944932e+031);
+ TEST1 (8.948471e+015);
+ TEST1 (1.23456789012345e+300);
+ TEST1 (1.23456e-300);
+ TEST1 (1e-006);
+}
+
+#undef TEST1
+
+/* ------------------------------------------------------------------------- */
+
int
main (int argc, char **argv)
{
+ libgoffice_init ();
+
+ test_dtoa ();
trig_tests ();
+ libgoffice_shutdown ();
+
return 0;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]