QGIS API Documentation 4.3.0-Master (90cb4ecf9ef)
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 <cmath>
20
21#include "qgis.h"
22
23#include "moc_qgsmathutils.cpp"
24
25void QgsMathUtils::doubleToRational( double value, qlonglong &numerator, qlonglong &denominator, double tolerance, int maxIterations )
26{
27 // This method uses the "continued fraction" algorithm to calculate approximate rational fractions
28
29 if ( qgsDoubleNear( value, 0.0 ) )
30 {
31 numerator = 0;
32 denominator = 1;
33 return;
34 }
35
36 const int sign = ( value > 0 ) ? 1 : -1;
37 const double x = std::abs( value );
38
39 // convergents:
40 // numerator continuants
41 long long previousAConvergent = 0;
42 long long currentAConvergent = 1;
43 // denominator continuants
44 long long previousBConvergent = 1;
45 long long currentBConvergent = 0;
46
47 double fractionalPart = x;
48 const double relativeTolerance = tolerance * x;
49
50 for ( int i = 0; i < maxIterations; ++i )
51 {
52 long long a = static_cast< long long >( std::floor( fractionalPart ) );
53
54 // guard against overflows before continuing, if so, abort early
55 if ( currentAConvergent != 0 && a > ( std::numeric_limits<long long>::max() - previousAConvergent ) / currentAConvergent )
56 {
57 break;
58 }
59 if ( currentBConvergent != 0 && a > ( std::numeric_limits<long long>::max() - previousBConvergent ) / currentBConvergent )
60 {
61 break;
62 }
63
64 long long nextHConvergent = a * currentAConvergent + previousAConvergent;
65 long long nextKConvergent = a * currentBConvergent + previousBConvergent;
66 previousAConvergent = currentAConvergent;
67 previousBConvergent = currentBConvergent;
68 currentAConvergent = nextHConvergent;
69 currentBConvergent = nextKConvergent;
70
71 // is approximation within the specified tolerance?
72 if ( currentBConvergent != 0 && std::abs( x - static_cast<double>( currentAConvergent ) / static_cast<double>( currentBConvergent ) ) <= relativeTolerance )
73 {
74 // if so, we've found our answer...
75 break;
76 }
77
78 // update the fractional part for the next iteration
79 const double remainder = fractionalPart - static_cast< double >( a );
80 if ( qgsDoubleNear( remainder, 0.0 ) )
81 {
82 // found exact representation
83 break;
84 }
85 fractionalPart = 1.0 / remainder;
86 }
87
88 numerator = sign * currentAConvergent;
89 denominator = currentBConvergent;
90}
91
92double QgsMathUtils::roundingInterval( double span, int divisions )
93{
94 if ( !std::isfinite( span ) || span <= 0 || divisions < 1 )
95 return 0;
96
97 return std::pow( 10.0, std::floor( std::log10( span / divisions ) ) );
98}
99
101{
102 const double interval = roundingInterval( range.upper() - range.lower() );
103 if ( interval <= 0 )
104 return range;
105
106 double lower = std::floor( range.lower() / interval ) * interval;
107 double upper = std::ceil( range.upper() / interval ) * interval;
108
109 const int decimals = -static_cast<int>( std::floor( std::log10( interval ) ) );
110 if ( decimals > 0 )
111 {
112 // strip the noise the multiplication above leaves in fractional values
113 lower = qgsRound( lower, decimals );
114 upper = qgsRound( upper, decimals );
115 }
116 return QgsDoubleRange( lower, upper );
117}
QgsRange which stores a range of double values.
Definition qgsrange.h:217
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.
static QgsDoubleRange roundedRange(const QgsDoubleRange &range)
Expands a range outward to round values, e.g.
static double roundingInterval(double span, int divisions=10)
Returns a round interval, a power of ten, which splits the given span into at least divisions parts.
T lower() const
Returns the lower bound of the range.
Definition qgsrange.h:79
T upper() const
Returns the upper bound of the range.
Definition qgsrange.h:86
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:7651
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7557