/src/libreoffice/tools/source/generic/fract.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
11 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
12 | | * contributor license agreements. See the NOTICE file distributed |
13 | | * with this work for additional information regarding copyright |
14 | | * ownership. The ASF licenses this file to you under the Apache |
15 | | * License, Version 2.0 (the "License"); you may not use this file |
16 | | * except in compliance with the License. You may obtain a copy of |
17 | | * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
18 | | */ |
19 | | |
20 | | #include <tools/fract.hxx> |
21 | | #include <tools/debug.hxx> |
22 | | #include <o3tl/hash_combine.hxx> |
23 | | #include <o3tl/numeric.hxx> |
24 | | #include <o3tl/safeint.hxx> |
25 | | #include <sal/log.hxx> |
26 | | #include <osl/diagnose.h> |
27 | | |
28 | | #include <algorithm> |
29 | | #include <bit> |
30 | | #include <cmath> |
31 | | #include <numeric> |
32 | | |
33 | | #include <boost/rational.hpp> |
34 | | |
35 | | static boost::rational<sal_Int32> rational_FromDouble(double dVal); |
36 | | static void rational_ReduceInaccurate(boost::rational<sal_Int32>& rRational, unsigned nSignificantBits); |
37 | | // Find the number of bits required to represent this number |
38 | 966M | static int impl_NumberOfBits(sal_uInt32 nNum) { return o3tl::number_of_bits(nNum); } |
39 | | |
40 | | static boost::rational<sal_Int32> toRational(sal_Int32 n, sal_Int32 d) |
41 | 711M | { |
42 | | // https://github.com/boostorg/boost/issues/335 when these are std::numeric_limits<sal_Int32>::min |
43 | 711M | if (n == d) |
44 | 167M | return 1; |
45 | | // tdf#144319 avoid boost::bad_rational e.g. if numerator=-476741369, denominator=-2147483648 |
46 | 543M | if (d < -std::numeric_limits<sal_Int32>::max()) |
47 | 691 | return 0; |
48 | 543M | return boost::rational<sal_Int32>(n, d); |
49 | 543M | } |
50 | | |
51 | | static constexpr bool isOutOfRange(sal_Int64 nNum) |
52 | 89.5M | { |
53 | 89.5M | return nNum < std::numeric_limits<sal_Int32>::min() |
54 | 89.5M | || nNum > std::numeric_limits<sal_Int32>::max(); |
55 | 89.5M | } |
56 | | |
57 | 44.7M | Fraction::Fraction( sal_Int64 nNum, sal_Int64 nDen ) : mnNumerator(nNum), mnDenominator(nDen) |
58 | 44.7M | { |
59 | 44.7M | if ( isOutOfRange(nNum) || isOutOfRange(nDen) ) |
60 | 3.66k | { |
61 | | // tdf#143200 |
62 | 3.66k | if (const auto gcd = std::gcd(nNum, nDen); gcd > 1) |
63 | 637 | { |
64 | 637 | nNum /= gcd; |
65 | 637 | nDen /= gcd; |
66 | 637 | } |
67 | 3.66k | SAL_WARN_IF(isOutOfRange(nNum) || isOutOfRange(nDen), |
68 | 3.66k | "tools.fraction", "values outside of range we can represent, doing reduction, which will reduce precision"); |
69 | 52.2k | while (isOutOfRange(nNum) || isOutOfRange(nDen)) |
70 | 48.5k | { |
71 | 48.5k | nNum /= 2; |
72 | 48.5k | nDen /= 2; |
73 | 48.5k | } |
74 | 3.66k | mnNumerator = nNum; |
75 | 3.66k | mnDenominator = nDen; |
76 | 3.66k | } |
77 | 44.7M | if ( mnDenominator == 0 ) |
78 | 10.8k | { |
79 | 10.8k | mbValid = false; |
80 | 10.8k | SAL_WARN( "tools.fraction", "'Fraction(" << nNum << ",0)' invalid fraction created" ); |
81 | 10.8k | return; |
82 | 10.8k | } |
83 | 44.7M | else if ((nDen == -1 && nNum == std::numeric_limits<sal_Int32>::min()) || |
84 | 44.7M | (nNum == -1 && nDen == std::numeric_limits<sal_Int32>::min())) |
85 | 24 | { |
86 | 24 | mbValid = false; |
87 | 24 | SAL_WARN("tools.fraction", "'Fraction(" << nNum << "," << nDen << ")' invalid fraction created"); |
88 | 24 | return; |
89 | 24 | } |
90 | 44.7M | } |
91 | | |
92 | | /** |
93 | | * only here to prevent passing of NaN |
94 | | */ |
95 | | Fraction::Fraction( double nNum, double nDen ) |
96 | 0 | : Fraction(sal_Int64(nNum), sal_Int64(nDen)) |
97 | 0 | {} |
98 | | |
99 | | Fraction::Fraction( double dVal ) |
100 | 203M | { |
101 | 203M | try |
102 | 203M | { |
103 | 203M | boost::rational<sal_Int32> v = rational_FromDouble( dVal ); |
104 | 203M | mnNumerator = v.numerator(); |
105 | 203M | mnDenominator = v.denominator(); |
106 | 203M | } |
107 | 203M | catch (const boost::bad_rational&) |
108 | 203M | { |
109 | 1.50k | mbValid = false; |
110 | 1.50k | SAL_WARN( "tools.fraction", "'Fraction(" << dVal << ")' invalid fraction created" ); |
111 | 1.50k | } |
112 | 203M | } |
113 | | |
114 | | Fraction::operator double() const |
115 | 3.19M | { |
116 | 3.19M | if (!mbValid) |
117 | 0 | { |
118 | 0 | SAL_WARN( "tools.fraction", "'double()' on invalid fraction" ); |
119 | 0 | return std::numeric_limits<double>::quiet_NaN(); |
120 | 0 | } |
121 | | |
122 | 3.19M | return boost::rational_cast<double>(toRational(mnNumerator, mnDenominator)); |
123 | 3.19M | } |
124 | | |
125 | | // This methods first validates both values. |
126 | | // If one of the arguments is invalid, the whole operation is invalid. |
127 | | // After computation detect if result overflows a sal_Int32 value |
128 | | // which cause the operation to be marked as invalid |
129 | | Fraction& Fraction::operator += ( const Fraction& rVal ) |
130 | 0 | { |
131 | 0 | if ( !rVal.mbValid ) |
132 | 0 | mbValid = false; |
133 | |
|
134 | 0 | if ( !mbValid ) |
135 | 0 | { |
136 | 0 | SAL_WARN( "tools.fraction", "'operator +=' with invalid fraction" ); |
137 | 0 | return *this; |
138 | 0 | } |
139 | | |
140 | 0 | boost::rational<sal_Int32> a = toRational(mnNumerator, mnDenominator); |
141 | 0 | a += toRational(rVal.mnNumerator, rVal.mnDenominator); |
142 | 0 | mnNumerator = a.numerator(); |
143 | 0 | mnDenominator = a.denominator(); |
144 | |
|
145 | 0 | return *this; |
146 | 0 | } |
147 | | |
148 | | Fraction& Fraction::operator -= ( const Fraction& rVal ) |
149 | 1.73k | { |
150 | 1.73k | if ( !rVal.mbValid ) |
151 | 0 | mbValid = false; |
152 | | |
153 | 1.73k | if ( !mbValid ) |
154 | 0 | { |
155 | 0 | SAL_WARN( "tools.fraction", "'operator -=' with invalid fraction" ); |
156 | 0 | return *this; |
157 | 0 | } |
158 | | |
159 | 1.73k | boost::rational<sal_Int32> a = toRational(mnNumerator, mnDenominator); |
160 | 1.73k | a -= toRational(rVal.mnNumerator, rVal.mnDenominator); |
161 | 1.73k | mnNumerator = a.numerator(); |
162 | 1.73k | mnDenominator = a.denominator(); |
163 | | |
164 | 1.73k | return *this; |
165 | 1.73k | } |
166 | | |
167 | | namespace |
168 | | { |
169 | | bool checked_multiply_by(boost::rational<sal_Int32>& i, const boost::rational<sal_Int32>& r) |
170 | 242M | { |
171 | | // Protect against self-modification |
172 | 242M | sal_Int32 num = r.numerator(); |
173 | 242M | sal_Int32 den = r.denominator(); |
174 | | |
175 | | // Fast-path if the number of bits in input is < the number of bits in the output, overflow cannot happen |
176 | | // This is considerably faster than repeated std::gcd() operations |
177 | 242M | if ((impl_NumberOfBits(std::abs(i.numerator())) + impl_NumberOfBits(std::abs(r.numerator()))) < 32 && |
178 | 240M | (impl_NumberOfBits(std::abs(i.denominator())) + impl_NumberOfBits(std::abs(r.denominator()))) < 32) |
179 | 240M | { |
180 | 240M | i *= r; |
181 | 240M | return false; |
182 | 240M | } |
183 | | |
184 | | // Avoid overflow and preserve normalization |
185 | 1.54M | sal_Int32 gcd1 = std::gcd(i.numerator(), den); |
186 | 1.54M | sal_Int32 gcd2 = std::gcd(num, i.denominator()); |
187 | | |
188 | 1.54M | if (!gcd1 || !gcd2) |
189 | 0 | return true; |
190 | | |
191 | 1.54M | bool fail = false; |
192 | 1.54M | fail |= o3tl::checked_multiply(i.numerator() / gcd1, num / gcd2, num); |
193 | 1.54M | fail |= o3tl::checked_multiply(i.denominator() / gcd2, den / gcd1, den); |
194 | | |
195 | 1.54M | if (!fail) |
196 | 49.6k | i.assign(num, den); |
197 | | |
198 | 1.54M | return fail; |
199 | 1.54M | } |
200 | | } |
201 | | |
202 | | Fraction& Fraction::operator *= ( const Fraction& rVal ) |
203 | 214M | { |
204 | 214M | if ( !rVal.mbValid ) |
205 | 19.4k | mbValid = false; |
206 | | |
207 | 214M | if ( !mbValid ) |
208 | 19.4k | { |
209 | 19.4k | SAL_WARN( "tools.fraction", "'operator *=' with invalid fraction" ); |
210 | 19.4k | return *this; |
211 | 19.4k | } |
212 | | |
213 | 214M | boost::rational<sal_Int32> a = toRational(mnNumerator, mnDenominator); |
214 | 214M | boost::rational<sal_Int32> b = toRational(rVal.mnNumerator, rVal.mnDenominator); |
215 | 214M | bool bFail = checked_multiply_by(a, b); |
216 | 214M | mnNumerator = a.numerator(); |
217 | 214M | mnDenominator = a.denominator(); |
218 | | |
219 | 214M | if (bFail) |
220 | 739 | { |
221 | 739 | mbValid = false; |
222 | 739 | } |
223 | | |
224 | 214M | return *this; |
225 | 214M | } |
226 | | |
227 | | Fraction& Fraction::operator /= ( const Fraction& rVal ) |
228 | 8.96k | { |
229 | 8.96k | if ( !rVal.mbValid ) |
230 | 4.92k | mbValid = false; |
231 | | |
232 | 8.96k | if ( !mbValid ) |
233 | 4.92k | { |
234 | 4.92k | SAL_WARN( "tools.fraction", "'operator /=' with invalid fraction" ); |
235 | 4.92k | return *this; |
236 | 4.92k | } |
237 | | |
238 | 4.03k | boost::rational<sal_Int32> a = toRational(mnNumerator, mnDenominator); |
239 | 4.03k | a /= toRational(rVal.mnNumerator, rVal.mnDenominator); |
240 | 4.03k | mnNumerator = a.numerator(); |
241 | 4.03k | mnDenominator = a.denominator(); |
242 | | |
243 | 4.03k | return *this; |
244 | 8.96k | } |
245 | | |
246 | | /** Inaccurate cancellation for a fraction. |
247 | | |
248 | | Clip both nominator and denominator to said number of bits. If |
249 | | either of those already have equal or less number of bits used, |
250 | | this method does nothing. |
251 | | |
252 | | @param nSignificantBits denotes, how many significant binary |
253 | | digits to maintain, in both nominator and denominator. |
254 | | |
255 | | @example ReduceInaccurate(8) has an error <1% [1/2^(8-1)] - the |
256 | | largest error occurs with the following pair of values: |
257 | | |
258 | | binary 1000000011111111111111111111111b/1000000000000000000000000000000b |
259 | | = 1082130431/1073741824 |
260 | | = approx. 1.007812499 |
261 | | |
262 | | A ReduceInaccurate(8) yields 1/1. |
263 | | */ |
264 | | void Fraction::ReduceInaccurate( unsigned nSignificantBits ) |
265 | 635 | { |
266 | 635 | if ( !mbValid ) |
267 | 0 | { |
268 | 0 | SAL_WARN( "tools.fraction", "'ReduceInaccurate' on invalid fraction" ); |
269 | 0 | return; |
270 | 0 | } |
271 | | |
272 | 635 | if ( !mnNumerator ) |
273 | 0 | return; |
274 | | |
275 | 635 | auto a = toRational(mnNumerator, mnDenominator); |
276 | 635 | rational_ReduceInaccurate(a, nSignificantBits); |
277 | 635 | mnNumerator = a.numerator(); |
278 | 635 | mnDenominator = a.denominator(); |
279 | 635 | } |
280 | | |
281 | | sal_Int32 Fraction::GetNumerator() const |
282 | 53.0M | { |
283 | 53.0M | if ( !mbValid ) |
284 | 2.37k | { |
285 | 2.37k | SAL_WARN( "tools.fraction", "'GetNumerator()' on invalid fraction" ); |
286 | 2.37k | return 0; |
287 | 2.37k | } |
288 | 53.0M | return mnNumerator; |
289 | 53.0M | } |
290 | | |
291 | | sal_Int32 Fraction::GetDenominator() const |
292 | 52.9M | { |
293 | 52.9M | if ( !mbValid ) |
294 | 1.81k | { |
295 | 1.81k | SAL_WARN( "tools.fraction", "'GetDenominator()' on invalid fraction" ); |
296 | 1.81k | return -1; |
297 | 1.81k | } |
298 | 52.9M | return mnDenominator; |
299 | 52.9M | } |
300 | | |
301 | | Fraction::operator sal_Int32() const |
302 | 214M | { |
303 | 214M | if ( !mbValid ) |
304 | 24.7k | { |
305 | 24.7k | SAL_WARN( "tools.fraction", "'operator sal_Int32()' on invalid fraction" ); |
306 | 24.7k | return 0; |
307 | 24.7k | } |
308 | 214M | return boost::rational_cast<sal_Int32>(toRational(mnNumerator, mnDenominator)); |
309 | 214M | } |
310 | | |
311 | | Fraction operator+( const Fraction& rVal1, const Fraction& rVal2 ) |
312 | 0 | { |
313 | 0 | Fraction aErg( rVal1 ); |
314 | 0 | aErg += rVal2; |
315 | 0 | return aErg; |
316 | 0 | } |
317 | | |
318 | | Fraction operator-( const Fraction& rVal1, const Fraction& rVal2 ) |
319 | 1.73k | { |
320 | 1.73k | Fraction aErg( rVal1 ); |
321 | 1.73k | aErg -= rVal2; |
322 | 1.73k | return aErg; |
323 | 1.73k | } |
324 | | |
325 | | Fraction operator*( const Fraction& rVal1, const Fraction& rVal2 ) |
326 | 214M | { |
327 | 214M | Fraction aErg( rVal1 ); |
328 | 214M | aErg *= rVal2; |
329 | 214M | return aErg; |
330 | 214M | } |
331 | | |
332 | | Fraction operator/( const Fraction& rVal1, const Fraction& rVal2 ) |
333 | 8.96k | { |
334 | 8.96k | Fraction aErg( rVal1 ); |
335 | 8.96k | aErg /= rVal2; |
336 | 8.96k | return aErg; |
337 | 8.96k | } |
338 | | |
339 | | bool operator !=( const Fraction& rVal1, const Fraction& rVal2 ) |
340 | 515k | { |
341 | 515k | return !(rVal1 == rVal2); |
342 | 515k | } |
343 | | |
344 | | bool operator <=( const Fraction& rVal1, const Fraction& rVal2 ) |
345 | 0 | { |
346 | 0 | return !(rVal1 > rVal2); |
347 | 0 | } |
348 | | |
349 | | bool operator >=( const Fraction& rVal1, const Fraction& rVal2 ) |
350 | 0 | { |
351 | 0 | return !(rVal1 < rVal2); |
352 | 0 | } |
353 | | |
354 | | bool operator == ( const Fraction& rVal1, const Fraction& rVal2 ) |
355 | 4.39M | { |
356 | 4.39M | if ( !rVal1.mbValid || !rVal2.mbValid ) |
357 | 4.93k | { |
358 | 4.93k | SAL_WARN( "tools.fraction", "'operator ==' with an invalid fraction" ); |
359 | 4.93k | return false; |
360 | 4.93k | } |
361 | | |
362 | 4.39M | return toRational(rVal1.mnNumerator, rVal1.mnDenominator) == toRational(rVal2.mnNumerator, rVal2.mnDenominator); |
363 | 4.39M | } |
364 | | |
365 | | bool operator < ( const Fraction& rVal1, const Fraction& rVal2 ) |
366 | 0 | { |
367 | 0 | if ( !rVal1.mbValid || !rVal2.mbValid ) |
368 | 0 | { |
369 | 0 | SAL_WARN( "tools.fraction", "'operator <' with an invalid fraction" ); |
370 | 0 | return false; |
371 | 0 | } |
372 | | |
373 | 0 | return toRational(rVal1.mnNumerator, rVal1.mnDenominator) < toRational(rVal2.mnNumerator, rVal2.mnDenominator); |
374 | 0 | } |
375 | | |
376 | | bool operator > ( const Fraction& rVal1, const Fraction& rVal2 ) |
377 | 0 | { |
378 | 0 | if ( !rVal1.mbValid || !rVal2.mbValid ) |
379 | 0 | { |
380 | 0 | SAL_WARN( "tools.fraction", "'operator >' with an invalid fraction" ); |
381 | 0 | return false; |
382 | 0 | } |
383 | | |
384 | 0 | return toRational(rVal1.mnNumerator, rVal1.mnDenominator) > toRational(rVal2.mnNumerator, rVal2.mnDenominator); |
385 | 0 | } |
386 | | |
387 | | // If dVal > LONG_MAX or dVal < LONG_MIN, the rational throws a boost::bad_rational. |
388 | | // Otherwise, dVal and denominator are multiplied by 10, until one of them |
389 | | // is larger than (LONG_MAX / 10). |
390 | | // |
391 | | // NOTE: here we use 'sal_Int32' due that only values in sal_Int32 range are valid. |
392 | | static boost::rational<sal_Int32> rational_FromDouble(double dVal) |
393 | 203M | { |
394 | 203M | if ( dVal > std::numeric_limits<sal_Int32>::max() || |
395 | 203M | dVal < std::numeric_limits<sal_Int32>::min() || |
396 | 203M | std::isnan(dVal) ) |
397 | 1.50k | throw boost::bad_rational(); |
398 | | |
399 | 203M | const sal_Int32 nMAX = std::numeric_limits<sal_Int32>::max() / 10; |
400 | 203M | sal_Int32 nDen = 1; |
401 | 1.78G | while ( std::abs( dVal ) < nMAX && nDen < nMAX ) { |
402 | 1.58G | dVal *= 10; |
403 | 1.58G | nDen *= 10; |
404 | 1.58G | } |
405 | 203M | return boost::rational<sal_Int32>( sal_Int32(dVal), nDen ); |
406 | 203M | } |
407 | | |
408 | | /** Inaccurate cancellation for a fraction. |
409 | | |
410 | | Clip both nominator and denominator to said number of bits. If |
411 | | either of those already have equal or less number of bits used, |
412 | | this method does nothing. |
413 | | |
414 | | @param nSignificantBits denotes, how many significant binary |
415 | | digits to maintain, in both nominator and denominator. |
416 | | |
417 | | @example ReduceInaccurate(8) has an error <1% [1/2^(8-1)] - the |
418 | | largest error occurs with the following pair of values: |
419 | | |
420 | | binary 1000000011111111111111111111111b/1000000000000000000000000000000b |
421 | | = 1082130431/1073741824 |
422 | | = approx. 1.007812499 |
423 | | |
424 | | A ReduceInaccurate(8) yields 1/1. |
425 | | */ |
426 | | static void rational_ReduceInaccurate(boost::rational<sal_Int32>& rRational, unsigned nSignificantBits) |
427 | 635 | { |
428 | 635 | if ( !rRational ) |
429 | 0 | return; |
430 | | |
431 | | // http://www.boost.org/doc/libs/release/libs/rational/rational.html#Internal%20representation |
432 | 635 | sal_Int32 nMul = rRational.numerator(); |
433 | 635 | if (nMul == std::numeric_limits<sal_Int32>::min()) |
434 | 0 | { |
435 | | // ofz#32973 Integer-overflow |
436 | 0 | return; |
437 | 0 | } |
438 | 635 | const bool bNeg = nMul < 0; |
439 | 635 | if (bNeg) |
440 | 77 | nMul = -nMul; |
441 | 635 | sal_Int32 nDiv = rRational.denominator(); |
442 | | |
443 | 635 | DBG_ASSERT(nSignificantBits<65, "More than 64 bit of significance is overkill!"); |
444 | | |
445 | | // How much bits can we lose? |
446 | 635 | const int nMulBitsToLose = std::max( ( impl_NumberOfBits( nMul ) - int( nSignificantBits ) ), 0 ); |
447 | 635 | const int nDivBitsToLose = std::max( ( impl_NumberOfBits( nDiv ) - int( nSignificantBits ) ), 0 ); |
448 | | |
449 | 635 | const int nToLose = std::min( nMulBitsToLose, nDivBitsToLose ); |
450 | | |
451 | | // Remove the bits |
452 | 635 | nMul >>= nToLose; |
453 | 635 | nDiv >>= nToLose; |
454 | | |
455 | 635 | if ( !nMul || !nDiv ) { |
456 | | // Return without reduction |
457 | 0 | OSL_FAIL( "Oops, we reduced too much..." ); |
458 | 0 | return; |
459 | 0 | } |
460 | | |
461 | 635 | rRational.assign( bNeg ? -nMul : nMul, nDiv ); |
462 | 635 | } |
463 | | |
464 | | size_t Fraction::GetHashValue() const |
465 | 26.0M | { |
466 | 26.0M | size_t hash = 0; |
467 | 26.0M | o3tl::hash_combine( hash, mnNumerator ); |
468 | 26.0M | o3tl::hash_combine( hash, mnDenominator ); |
469 | 26.0M | o3tl::hash_combine( hash, mbValid ); |
470 | 26.0M | return hash; |
471 | 26.0M | } |
472 | | |
473 | | Fraction Fraction::MakeFraction( tools::Long nN1, tools::Long nN2, tools::Long nD1, tools::Long nD2 ) |
474 | 26.0M | { |
475 | 26.0M | if( nD1 == 0 || nD2 == 0 ) //under these bad circumstances the following while loop will be endless |
476 | 0 | { |
477 | 0 | SAL_WARN("tools.fraction", "Invalid parameter for ImplMakeFraction"); |
478 | 0 | return Fraction( 1, 1 ); |
479 | 0 | } |
480 | | |
481 | 26.0M | tools::Long i = 1; |
482 | | |
483 | 26.0M | if ( nN1 < 0 ) { i = -i; nN1 = -nN1; } |
484 | 26.0M | if ( nN2 < 0 ) { i = -i; nN2 = -nN2; } |
485 | 26.0M | if ( nD1 < 0 ) { i = -i; nD1 = -nD1; } |
486 | 26.0M | if ( nD2 < 0 ) { i = -i; nD2 = -nD2; } |
487 | | // all positive; i sign |
488 | | |
489 | 26.0M | assert( nN1 >= std::numeric_limits<sal_Int32>::min() ); |
490 | 26.0M | assert( nN1 <= std::numeric_limits<sal_Int32>::max( )); |
491 | 26.0M | assert( nD1 >= std::numeric_limits<sal_Int32>::min() ); |
492 | 26.0M | assert( nD1 <= std::numeric_limits<sal_Int32>::max( )); |
493 | 26.0M | assert( nN2 >= std::numeric_limits<sal_Int32>::min() ); |
494 | 26.0M | assert( nN2 <= std::numeric_limits<sal_Int32>::max( )); |
495 | 26.0M | assert( nD2 >= std::numeric_limits<sal_Int32>::min() ); |
496 | 26.0M | assert( nD2 <= std::numeric_limits<sal_Int32>::max( )); |
497 | | |
498 | 26.0M | boost::rational<sal_Int32> a = toRational(i*nN1, nD1); |
499 | 26.0M | boost::rational<sal_Int32> b = toRational(nN2, nD2); |
500 | 26.0M | bool bFail = checked_multiply_by(a, b); |
501 | | |
502 | 27.5M | while ( bFail ) { |
503 | 1.49M | if ( nN1 > nN2 ) |
504 | 735k | nN1 = (nN1 + 1) / 2; |
505 | 757k | else |
506 | 757k | nN2 = (nN2 + 1) / 2; |
507 | 1.49M | if ( nD1 > nD2 ) |
508 | 211k | nD1 = (nD1 + 1) / 2; |
509 | 1.28M | else |
510 | 1.28M | nD2 = (nD2 + 1) / 2; |
511 | | |
512 | 1.49M | a = toRational(i*nN1, nD1); |
513 | 1.49M | b = toRational(nN2, nD2); |
514 | 1.49M | bFail = checked_multiply_by(a, b); |
515 | 1.49M | } |
516 | | |
517 | 26.0M | return Fraction(a.numerator(), a.denominator()); |
518 | 26.0M | } |
519 | | |
520 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |