QGIS API Documentation 3.99.0-Master (21b3aa880ba)
Loading...
Searching...
No Matches
qgsmathutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmathutils.cpp
3 ----------------------
4 begin : July 2025
5 copyright : (C) 2025 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16
17#include "qgsmathutils.h"
18
19#include "qgis.h"
20
21#include "moc_qgsmathutils.cpp"
22
23void QgsMathUtils::doubleToRational( double value, qlonglong &numerator, qlonglong &denominator, double tolerance, int maxIterations )
24{
25 // This method uses the "continued fraction" algorithm to calculate approximate rational fractions
26
27 if ( qgsDoubleNear( value, 0.0 ) )
28 {
29 numerator = 0;
30 denominator = 1;
31 return;
32 }
33
34 const int sign = ( value > 0 ) ? 1 : -1;
35 const double x = std::abs( value );
36
37 // convergents:
38 // numerator continuants
39 long long previousAConvergent = 0;
40 long long currentAConvergent = 1;
41 // denominator continuants
42 long long previousBConvergent = 1;
43 long long currentBConvergent = 0;
44
45 double fractionalPart = x;
46 const double relativeTolerance = tolerance * x;
47
48 for ( int i = 0; i < maxIterations; ++i )
49 {
50 long long a = static_cast< long long >( std::floor( fractionalPart ) );
51
52 // guard against overflows before continuing, if so, abort early
53 if ( currentAConvergent != 0 && a > ( std::numeric_limits<long long>::max() - previousAConvergent ) / currentAConvergent )
54 {
55 break;
56 }
57 if ( currentBConvergent != 0 && a > ( std::numeric_limits<long long>::max() - previousBConvergent ) / currentBConvergent )
58 {
59 break;
60 }
61
62 long long nextHConvergent = a * currentAConvergent + previousAConvergent;
63 long long nextKConvergent = a * currentBConvergent + previousBConvergent;
64 previousAConvergent = currentAConvergent;
65 previousBConvergent = currentBConvergent;
66 currentAConvergent = nextHConvergent ;
67 currentBConvergent = nextKConvergent;
68
69 // is approximation within the specified tolerance?
70 if ( currentBConvergent != 0 && std::abs( x - static_cast<double>( currentAConvergent ) / static_cast<double>( currentBConvergent ) ) <= relativeTolerance )
71 {
72 // if so, we've found our answer...
73 break;
74 }
75
76 // update the fractional part for the next iteration
77 const double remainder = fractionalPart - static_cast< double >( a );
78 if ( qgsDoubleNear( remainder, 0.0 ) )
79 {
80 // found exact representation
81 break;
82 }
83 fractionalPart = 1.0 / remainder;
84 }
85
86 numerator = sign * currentAConvergent;
87 denominator = currentBConvergent;
88}
static Q_INVOKABLE void doubleToRational(double value, qlonglong &numerator, qlonglong &denominator, double tolerance=1.0e-9, int maxIterations=100)
Converts a double value to a rational fraction.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6607