rational.c
1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 /* 3 * Helper functions for rational numbers. 4 * 5 * Copyright (C) 2009 emlix GmbH, Oskar Schirmer <oskar@scara.com> 6 * Copyright (C) 2019 Trent Piepho <tpiepho@gmail.com> 7 */ 8 9 #include <commonlib/helpers.h> 10 #include <commonlib/rational.h> 11 #include <limits.h> 12 13 /* 14 * For theoretical background, see: 15 * https://en.wikipedia.org/wiki/Continued_fraction 16 */ 17 void rational_best_approximation( 18 unsigned long numerator, unsigned long denominator, 19 unsigned long max_numerator, unsigned long max_denominator, 20 unsigned long *best_numerator, unsigned long *best_denominator) 21 { 22 /* 23 * n/d is the starting rational, where both n and d will 24 * decrease in each iteration using the Euclidean algorithm. 25 * 26 * dp is the value of d from the prior iteration. 27 * 28 * n2/d2, n1/d1, and n0/d0 are our successively more accurate 29 * approximations of the rational. They are, respectively, 30 * the current, previous, and two prior iterations of it. 31 * 32 * a is current term of the continued fraction. 33 */ 34 unsigned long n, d, n0, d0, n1, d1, n2, d2; 35 n = numerator; 36 d = denominator; 37 n0 = d1 = 0; 38 n1 = d0 = 1; 39 40 for (;;) { 41 unsigned long dp, a; 42 43 if (d == 0) 44 break; 45 /* 46 * Find next term in continued fraction, 'a', via 47 * Euclidean algorithm. 48 */ 49 dp = d; 50 a = n / d; 51 d = n % d; 52 n = dp; 53 54 /* 55 * Calculate the current rational approximation (aka 56 * convergent), n2/d2, using the term just found and 57 * the two prior approximations. 58 */ 59 n2 = n0 + a * n1; 60 d2 = d0 + a * d1; 61 62 /* 63 * If the current convergent exceeds the maximum, then 64 * return either the previous convergent or the 65 * largest semi-convergent, the final term of which is 66 * found below as 't'. 67 */ 68 if ((n2 > max_numerator) || (d2 > max_denominator)) { 69 unsigned long t = ULONG_MAX; 70 71 if (d1) 72 t = (max_denominator - d0) / d1; 73 if (n1) 74 t = MIN(t, (max_numerator - n0) / n1); 75 76 /* 77 * This tests if the semi-convergent is closer than the previous 78 * convergent. If d1 is zero there is no previous convergent as 79 * this is the 1st iteration, so always choose the semi-convergent. 80 */ 81 if (!d1 || 2u * t > a || (2u * t == a && d0 * dp > d1 * d)) { 82 n1 = n0 + t * n1; 83 d1 = d0 + t * d1; 84 } 85 break; 86 } 87 n0 = n1; 88 n1 = n2; 89 d0 = d1; 90 d1 = d2; 91 } 92 93 *best_numerator = n1; 94 *best_denominator = d1; 95 }