xref: /linux/lib/math/rational.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
12c64e9cbSAndy Shevchenko // SPDX-License-Identifier: GPL-2.0
22c64e9cbSAndy Shevchenko /*
32c64e9cbSAndy Shevchenko  * rational fractions
42c64e9cbSAndy Shevchenko  *
52c64e9cbSAndy Shevchenko  * Copyright (C) 2009 emlix GmbH, Oskar Schirmer <oskar@scara.com>
6323dd2c3STrent Piepho  * Copyright (C) 2019 Trent Piepho <tpiepho@gmail.com>
72c64e9cbSAndy Shevchenko  *
82c64e9cbSAndy Shevchenko  * helper functions when coping with rational numbers
92c64e9cbSAndy Shevchenko  */
102c64e9cbSAndy Shevchenko 
112c64e9cbSAndy Shevchenko #include <linux/rational.h>
122c64e9cbSAndy Shevchenko #include <linux/compiler.h>
132c64e9cbSAndy Shevchenko #include <linux/export.h>
14b296a6d5SAndy Shevchenko #include <linux/minmax.h>
1565a0d3c1STrent Piepho #include <linux/limits.h>
16bcda5fd3SGeert Uytterhoeven #include <linux/module.h>
172c64e9cbSAndy Shevchenko 
182c64e9cbSAndy Shevchenko /*
192c64e9cbSAndy Shevchenko  * calculate best rational approximation for a given fraction
202c64e9cbSAndy Shevchenko  * taking into account restricted register size, e.g. to find
212c64e9cbSAndy Shevchenko  * appropriate values for a pll with 5 bit denominator and
222c64e9cbSAndy Shevchenko  * 8 bit numerator register fields, trying to set up with a
232c64e9cbSAndy Shevchenko  * frequency ratio of 3.1415, one would say:
242c64e9cbSAndy Shevchenko  *
252c64e9cbSAndy Shevchenko  * rational_best_approximation(31415, 10000,
262c64e9cbSAndy Shevchenko  *		(1 << 8) - 1, (1 << 5) - 1, &n, &d);
272c64e9cbSAndy Shevchenko  *
282c64e9cbSAndy Shevchenko  * you may look at given_numerator as a fixed point number,
292c64e9cbSAndy Shevchenko  * with the fractional part size described in given_denominator.
302c64e9cbSAndy Shevchenko  *
312c64e9cbSAndy Shevchenko  * for theoretical background, see:
32d89775fcSAlexander A. Klimov  * https://en.wikipedia.org/wiki/Continued_fraction
332c64e9cbSAndy Shevchenko  */
342c64e9cbSAndy Shevchenko 
rational_best_approximation(unsigned long given_numerator,unsigned long given_denominator,unsigned long max_numerator,unsigned long max_denominator,unsigned long * best_numerator,unsigned long * best_denominator)352c64e9cbSAndy Shevchenko void rational_best_approximation(
362c64e9cbSAndy Shevchenko 	unsigned long given_numerator, unsigned long given_denominator,
372c64e9cbSAndy Shevchenko 	unsigned long max_numerator, unsigned long max_denominator,
382c64e9cbSAndy Shevchenko 	unsigned long *best_numerator, unsigned long *best_denominator)
392c64e9cbSAndy Shevchenko {
40323dd2c3STrent Piepho 	/* n/d is the starting rational, which is continually
41323dd2c3STrent Piepho 	 * decreased each iteration using the Euclidean algorithm.
42323dd2c3STrent Piepho 	 *
43323dd2c3STrent Piepho 	 * dp is the value of d from the prior iteration.
44323dd2c3STrent Piepho 	 *
45323dd2c3STrent Piepho 	 * n2/d2, n1/d1, and n0/d0 are our successively more accurate
46323dd2c3STrent Piepho 	 * approximations of the rational.  They are, respectively,
47323dd2c3STrent Piepho 	 * the current, previous, and two prior iterations of it.
48323dd2c3STrent Piepho 	 *
49323dd2c3STrent Piepho 	 * a is current term of the continued fraction.
50323dd2c3STrent Piepho 	 */
51323dd2c3STrent Piepho 	unsigned long n, d, n0, d0, n1, d1, n2, d2;
522c64e9cbSAndy Shevchenko 	n = given_numerator;
532c64e9cbSAndy Shevchenko 	d = given_denominator;
542c64e9cbSAndy Shevchenko 	n0 = d1 = 0;
552c64e9cbSAndy Shevchenko 	n1 = d0 = 1;
56323dd2c3STrent Piepho 
572c64e9cbSAndy Shevchenko 	for (;;) {
58323dd2c3STrent Piepho 		unsigned long dp, a;
59323dd2c3STrent Piepho 
602c64e9cbSAndy Shevchenko 		if (d == 0)
612c64e9cbSAndy Shevchenko 			break;
62323dd2c3STrent Piepho 		/* Find next term in continued fraction, 'a', via
63323dd2c3STrent Piepho 		 * Euclidean algorithm.
64323dd2c3STrent Piepho 		 */
65323dd2c3STrent Piepho 		dp = d;
662c64e9cbSAndy Shevchenko 		a = n / d;
672c64e9cbSAndy Shevchenko 		d = n % d;
68323dd2c3STrent Piepho 		n = dp;
69323dd2c3STrent Piepho 
70323dd2c3STrent Piepho 		/* Calculate the current rational approximation (aka
71323dd2c3STrent Piepho 		 * convergent), n2/d2, using the term just found and
72323dd2c3STrent Piepho 		 * the two prior approximations.
73323dd2c3STrent Piepho 		 */
74323dd2c3STrent Piepho 		n2 = n0 + a * n1;
75323dd2c3STrent Piepho 		d2 = d0 + a * d1;
76323dd2c3STrent Piepho 
77323dd2c3STrent Piepho 		/* If the current convergent exceeds the maxes, then
78323dd2c3STrent Piepho 		 * return either the previous convergent or the
79323dd2c3STrent Piepho 		 * largest semi-convergent, the final term of which is
80323dd2c3STrent Piepho 		 * found below as 't'.
81323dd2c3STrent Piepho 		 */
82323dd2c3STrent Piepho 		if ((n2 > max_numerator) || (d2 > max_denominator)) {
8365a0d3c1STrent Piepho 			unsigned long t = ULONG_MAX;
84323dd2c3STrent Piepho 
8565a0d3c1STrent Piepho 			if (d1)
8665a0d3c1STrent Piepho 				t = (max_denominator - d0) / d1;
8765a0d3c1STrent Piepho 			if (n1)
8865a0d3c1STrent Piepho 				t = min(t, (max_numerator - n0) / n1);
8965a0d3c1STrent Piepho 
9065a0d3c1STrent Piepho 			/* This tests if the semi-convergent is closer than the previous
9165a0d3c1STrent Piepho 			 * convergent.  If d1 is zero there is no previous convergent as this
9265a0d3c1STrent Piepho 			 * is the 1st iteration, so always choose the semi-convergent.
93323dd2c3STrent Piepho 			 */
9465a0d3c1STrent Piepho 			if (!d1 || 2u * t > a || (2u * t == a && d0 * dp > d1 * d)) {
95323dd2c3STrent Piepho 				n1 = n0 + t * n1;
96323dd2c3STrent Piepho 				d1 = d0 + t * d1;
97323dd2c3STrent Piepho 			}
98323dd2c3STrent Piepho 			break;
99323dd2c3STrent Piepho 		}
1002c64e9cbSAndy Shevchenko 		n0 = n1;
101323dd2c3STrent Piepho 		n1 = n2;
1022c64e9cbSAndy Shevchenko 		d0 = d1;
103323dd2c3STrent Piepho 		d1 = d2;
1042c64e9cbSAndy Shevchenko 	}
1052c64e9cbSAndy Shevchenko 	*best_numerator = n1;
1062c64e9cbSAndy Shevchenko 	*best_denominator = d1;
1072c64e9cbSAndy Shevchenko }
1082c64e9cbSAndy Shevchenko 
1092c64e9cbSAndy Shevchenko EXPORT_SYMBOL(rational_best_approximation);
110bcda5fd3SGeert Uytterhoeven 
111*8547d115SJeff Johnson MODULE_DESCRIPTION("Rational fraction support library");
112bcda5fd3SGeert Uytterhoeven MODULE_LICENSE("GPL v2");
113