[recipes] Display doubles as mixed fractions too
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [recipes] Display doubles as mixed fractions too
- Date: Sun, 25 Jun 2017 03:30:12 +0000 (UTC)
commit c827803ee81cfb230fd7e91a5ed40520aad54c18
Author: Matthias Clasen <mclasen redhat com>
Date: Sat Jun 24 15:47:42 2017 -0400
Display doubles as mixed fractions too
Use rational approximation with continued fractions.
src/gr-number.c | 128 ++++++++++++++++++++++++++++++++++++++++++++----------
1 files changed, 104 insertions(+), 24 deletions(-)
---
diff --git a/src/gr-number.c b/src/gr-number.c
index 89e8041..f5564e7 100644
--- a/src/gr-number.c
+++ b/src/gr-number.c
@@ -20,6 +20,7 @@
#include <glib/gi18n.h>
#include <gio/gio.h>
+#include <math.h>
#include "gr-number.h"
#include "gr-utils.h"
@@ -44,6 +45,78 @@ gcd (int m, int n)
return m;
}
+/*
+ * http://www.ics.uci.edu/~eppstein/numth/frap.c
+ *
+ * find rational approximation to given real number
+ * David Eppstein / UC Irvine / 8 Aug 1993
+ *
+ * With corrections from Arno Formella, May 2008
+ *
+ * based on the theory of continued fractions
+ * if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...)))
+ * then best approximation is found by truncating this series
+ * (with some adjustments in the last term).
+ *
+ * Note the fraction can be recovered as the first column of the matrix
+ * ( a1 1 ) ( a2 1 ) ( a3 1 ) ...
+ * ( 1 0 ) ( 1 0 ) ( 1 0 )
+ * Instead of keeping the sequence of continued fraction terms,
+ * we just keep the last partial product of these matrices.
+*/
+static void
+rational_approximation (double input,
+ int max_denom,
+ int *num,
+ int *denom)
+{
+ long m[2][2];
+ double x, n0, d0, n1, d1;
+ long ai;
+
+ x = input;
+
+ m[0][0] = m[1][1] = 1;
+ m[0][1] = m[1][0] = 0;
+
+ while (m[1][0] * (ai = (long)x) + m[1][1] <= max_denom) {
+ long t;
+
+ t = m[0][0] * ai + m[0][1];
+ m[0][1] = m[0][0];
+ m[0][0] = t;
+ t = m[1][0] * ai + m[1][1];
+ m[1][1] = m[1][0];
+ m[1][0] = t;
+
+ if (x == (double)ai)
+ break;
+
+ x = 1 / (x - (double)ai);
+
+ if (x > (double)0x7fffffff)
+ break;
+ }
+
+ n0 = (double)m[0][0];
+ d0 = (double)m[1][0];
+
+ ai = (max_denom - m[1][1]) / m[1][0];
+ m[0][0] = m[0][0] * ai + m[0][1];
+ m[1][0] = m[1][0] * ai + m[1][1];
+
+ n1 = (double)m[0][0];
+ d1 = (double)m[1][0];
+
+ if (fabs (input - n0 / d0) < fabs (input - n1 / d1)) {
+ *num = (int)n0;
+ *denom = (int)d0;
+ } else {
+ *num = (int)n1;
+ *denom = (int)d1;
+ }
+}
+
void
gr_number_set_fraction (GrNumber *number,
int num,
@@ -310,47 +383,54 @@ append_digits (GString *s,
}
static char *
-format_fraction (int num, int denom)
+format_fraction (int integral,
+ int num,
+ int denom)
{
int i;
GString *s;
+ s = g_string_new ("");
+
+ if (integral != 0)
+ g_string_append_printf (s, "%d", integral);
+
+ if (num == 0)
+ goto out;
+
+ if (integral != 0)
+ g_string_append (s, " ");
+
for (i = 0; i < G_N_ELEMENTS (fractions); i++) {
- if (fractions[i].num == num && fractions[i].denom == denom)
- return g_strdup (fractions[i].ch);
+ if (fractions[i].num == num && fractions[i].denom == denom) {
+ g_string_append (s, fractions[i].ch);
+ goto out;
+ }
}
- s = g_string_new ("");
-
append_digits (s, sup, num);
g_string_append (s, "⁄");
append_digits (s, sub, denom);
+out:
return g_string_free (s, FALSE);
}
char *
gr_number_format (GrNumber *number)
{
- if (number->fraction) {
- if (number->denom == 1)
- return g_strdup_printf ("%d", number->num);
- else {
- int integral;
- g_autofree char *fraction = NULL;
-
- if (number->denom == 0)
- return g_strdup ("");
-
- integral = number->num / number->denom;
- fraction = format_fraction (number->num % number->denom, number->denom);
- if (integral == 0)
- return g_strdup_printf ("%s", fraction);
- else
- return g_strdup_printf ("%d %s", integral, fraction);
- }
- }
+ if (number->fraction)
+ return format_fraction (0, number->num, number->denom);
else {
- return g_strdup_printf ("%g", number->value);
+ double integral, rem;
+ int num, denom;
+
+ integral = floor (number->value);
+ rem = number->value - integral;
+
+ if (rational_approximation (rem, 20, &num, &denom))
+ return format_fraction ((int)integral, num, denom);
+ else
+ return g_strdup_printf ("%g", number->value);
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]