1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <tools/fract.hxx>
21 #include <tools/debug.hxx>
22 #include <tools/stream.hxx>
23 #include <o3tl/safeint.hxx>
24 #include <sal/log.hxx>
25 #include <osl/diagnose.h>
30 #include <boost/version.hpp>
31 #if BOOST_VERSION >= 106700
32 #include <boost/integer/common_factor_rt.hpp>
34 #include <boost/math/common_factor_rt.hpp>
36 #include <boost/rational.hpp>
38 static boost::rational
<sal_Int32
> rational_FromDouble(double dVal
);
40 static void rational_ReduceInaccurate(boost::rational
<sal_Int32
>& rRational
, unsigned nSignificantBits
);
42 static boost::rational
<sal_Int32
> toRational(sal_Int32 n
, sal_Int32 d
)
44 return boost::rational
<sal_Int32
>(n
, d
);
47 // Initialized by setting nNum as nominator and nDen as denominator
48 // Negative values in the denominator are invalid and cause the
49 // inversion of both nominator and denominator signs
50 // in order to return the correct value.
51 Fraction::Fraction( sal_Int64 nNum
, sal_Int64 nDen
) : mnNumerator(nNum
), mnDenominator(nDen
)
53 assert( nNum
>= std::numeric_limits
<sal_Int32
>::min() );
54 assert( nNum
<= std::numeric_limits
<sal_Int32
>::max( ));
55 assert( nDen
>= std::numeric_limits
<sal_Int32
>::min() );
56 assert( nDen
<= std::numeric_limits
<sal_Int32
>::max( ));
60 SAL_WARN( "tools.fraction", "'Fraction(" << nNum
<< ",0)' invalid fraction created" );
66 * only here to prevent passing of NaN
68 Fraction::Fraction( double nNum
, double nDen
) : mnNumerator(sal_Int64(nNum
)), mnDenominator(sal_Int64(nDen
))
70 assert( !std::isnan(nNum
) );
71 assert( !std::isnan(nDen
) );
72 assert( nNum
>= std::numeric_limits
<sal_Int32
>::min() );
73 assert( nNum
<= std::numeric_limits
<sal_Int32
>::max( ));
74 assert( nDen
>= std::numeric_limits
<sal_Int32
>::min() );
75 assert( nDen
<= std::numeric_limits
<sal_Int32
>::max( ));
79 SAL_WARN( "tools.fraction", "'Fraction(" << nNum
<< ",0)' invalid fraction created" );
84 Fraction::Fraction( double dVal
)
88 boost::rational
<sal_Int32
> v
= rational_FromDouble( dVal
);
89 mnNumerator
= v
.numerator();
90 mnDenominator
= v
.denominator();
92 catch (const boost::bad_rational
&)
95 SAL_WARN( "tools.fraction", "'Fraction(" << dVal
<< ")' invalid fraction created" );
99 Fraction::operator double() const
103 SAL_WARN( "tools.fraction", "'double()' on invalid fraction" );
107 // https://github.com/boostorg/boost/issues/335 when these are std::numeric_limits<sal_Int32>::min
108 if (mnNumerator
== mnDenominator
)
111 return boost::rational_cast
<double>(toRational(mnNumerator
, mnDenominator
));
114 // This methods first validates both values.
115 // If one of the arguments is invalid, the whole operation is invalid.
116 // After computation detect if result overflows a sal_Int32 value
117 // which cause the operation to be marked as invalid
118 Fraction
& Fraction::operator += ( const Fraction
& rVal
)
125 SAL_WARN( "tools.fraction", "'operator +=' with invalid fraction" );
129 boost::rational
<sal_Int32
> a
= toRational(mnNumerator
, mnDenominator
);
130 a
+= toRational(rVal
.mnNumerator
, rVal
.mnDenominator
);
131 mnNumerator
= a
.numerator();
132 mnDenominator
= a
.denominator();
137 Fraction
& Fraction::operator -= ( const Fraction
& rVal
)
144 SAL_WARN( "tools.fraction", "'operator -=' with invalid fraction" );
148 boost::rational
<sal_Int32
> a
= toRational(mnNumerator
, mnDenominator
);
149 a
-= toRational(rVal
.mnNumerator
, rVal
.mnDenominator
);
150 mnNumerator
= a
.numerator();
151 mnDenominator
= a
.denominator();
158 template<typename T
> bool checked_multiply_by(boost::rational
<T
>& i
, const boost::rational
<T
>& r
)
160 // Protect against self-modification
161 T num
= r
.numerator();
162 T den
= r
.denominator();
164 // Avoid overflow and preserve normalization
165 #if BOOST_VERSION >= 106700
166 T gcd1
= boost::integer::gcd(i
.numerator(), den
);
167 T gcd2
= boost::integer::gcd(num
, i
.denominator());
169 T gcd1
= boost::math::gcd(i
.numerator(), den
);
170 T gcd2
= boost::math::gcd(num
, i
.denominator());
174 fail
|= o3tl::checked_multiply(i
.numerator() / gcd1
, num
/ gcd2
, num
);
175 fail
|= o3tl::checked_multiply(i
.denominator() / gcd2
, den
/ gcd1
, den
);
184 Fraction
& Fraction::operator *= ( const Fraction
& rVal
)
191 SAL_WARN( "tools.fraction", "'operator *=' with invalid fraction" );
195 boost::rational
<sal_Int32
> a
= toRational(mnNumerator
, mnDenominator
);
196 boost::rational
<sal_Int32
> b
= toRational(rVal
.mnNumerator
, rVal
.mnDenominator
);
197 bool bFail
= checked_multiply_by(a
, b
);
198 mnNumerator
= a
.numerator();
199 mnDenominator
= a
.denominator();
209 Fraction
& Fraction::operator /= ( const Fraction
& rVal
)
216 SAL_WARN( "tools.fraction", "'operator /=' with invalid fraction" );
220 boost::rational
<sal_Int32
> a
= toRational(mnNumerator
, mnDenominator
);
221 a
/= toRational(rVal
.mnNumerator
, rVal
.mnDenominator
);
222 mnNumerator
= a
.numerator();
223 mnDenominator
= a
.denominator();
228 /** Inaccurate cancellation for a fraction.
230 Clip both nominator and denominator to said number of bits. If
231 either of those already have equal or less number of bits used,
232 this method does nothing.
234 @param nSignificantBits denotes, how many significant binary
235 digits to maintain, in both nominator and denominator.
237 @example ReduceInaccurate(8) has an error <1% [1/2^(8-1)] - the
238 largest error occurs with the following pair of values:
240 binary 1000000011111111111111111111111b/1000000000000000000000000000000b
241 = 1082130431/1073741824
242 = approx. 1.007812499
244 A ReduceInaccurate(8) yields 1/1.
246 void Fraction::ReduceInaccurate( unsigned nSignificantBits
)
250 SAL_WARN( "tools.fraction", "'ReduceInaccurate' on invalid fraction" );
257 auto a
= toRational(mnNumerator
, mnDenominator
);
258 rational_ReduceInaccurate(a
, nSignificantBits
);
259 mnNumerator
= a
.numerator();
260 mnDenominator
= a
.denominator();
263 sal_Int32
Fraction::GetNumerator() const
267 SAL_WARN( "tools.fraction", "'GetNumerator()' on invalid fraction" );
273 sal_Int32
Fraction::GetDenominator() const
277 SAL_WARN( "tools.fraction", "'GetDenominator()' on invalid fraction" );
280 return mnDenominator
;
283 Fraction::operator sal_Int32() const
287 SAL_WARN( "tools.fraction", "'operator sal_Int32()' on invalid fraction" );
290 return boost::rational_cast
<sal_Int32
>(toRational(mnNumerator
, mnDenominator
));
293 Fraction
operator+( const Fraction
& rVal1
, const Fraction
& rVal2
)
295 Fraction
aErg( rVal1
);
300 Fraction
operator-( const Fraction
& rVal1
, const Fraction
& rVal2
)
302 Fraction
aErg( rVal1
);
307 Fraction
operator*( const Fraction
& rVal1
, const Fraction
& rVal2
)
309 Fraction
aErg( rVal1
);
314 Fraction
operator/( const Fraction
& rVal1
, const Fraction
& rVal2
)
316 Fraction
aErg( rVal1
);
321 bool operator !=( const Fraction
& rVal1
, const Fraction
& rVal2
)
323 return !(rVal1
== rVal2
);
326 bool operator <=( const Fraction
& rVal1
, const Fraction
& rVal2
)
328 return !(rVal1
> rVal2
);
331 bool operator >=( const Fraction
& rVal1
, const Fraction
& rVal2
)
333 return !(rVal1
< rVal2
);
336 bool operator == ( const Fraction
& rVal1
, const Fraction
& rVal2
)
338 if ( !rVal1
.mbValid
|| !rVal2
.mbValid
)
340 SAL_WARN( "tools.fraction", "'operator ==' with an invalid fraction" );
344 return toRational(rVal1
.mnNumerator
, rVal1
.mnDenominator
) == toRational(rVal2
.mnNumerator
, rVal2
.mnDenominator
);
347 bool operator < ( const Fraction
& rVal1
, const Fraction
& rVal2
)
349 if ( !rVal1
.mbValid
|| !rVal2
.mbValid
)
351 SAL_WARN( "tools.fraction", "'operator <' with an invalid fraction" );
355 return toRational(rVal1
.mnNumerator
, rVal1
.mnDenominator
) < toRational(rVal2
.mnNumerator
, rVal2
.mnDenominator
);
358 bool operator > ( const Fraction
& rVal1
, const Fraction
& rVal2
)
360 if ( !rVal1
.mbValid
|| !rVal2
.mbValid
)
362 SAL_WARN( "tools.fraction", "'operator >' with an invalid fraction" );
366 return toRational(rVal1
.mnNumerator
, rVal1
.mnDenominator
) > toRational(rVal2
.mnNumerator
, rVal2
.mnDenominator
);
369 SvStream
& ReadFraction( SvStream
& rIStream
, Fraction
& rFract
)
371 sal_Int32
num(0), den(0);
372 rIStream
.ReadInt32( num
);
373 rIStream
.ReadInt32( den
);
376 SAL_WARN( "tools.fraction", "'ReadFraction()' read an invalid fraction" );
377 rFract
.mbValid
= false;
381 rFract
.mnNumerator
= num
;
382 rFract
.mnDenominator
= den
;
383 rFract
.mbValid
= true;
388 SvStream
& WriteFraction( SvStream
& rOStream
, const Fraction
& rFract
)
390 if ( !rFract
.mbValid
)
392 SAL_WARN( "tools.fraction", "'WriteFraction()' write an invalid fraction" );
393 rOStream
.WriteInt32( 0 );
394 rOStream
.WriteInt32( -1 );
396 rOStream
.WriteInt32( rFract
.mnNumerator
);
397 rOStream
.WriteInt32( rFract
.mnDenominator
);
402 // If dVal > LONG_MAX or dVal < LONG_MIN, the rational throws a boost::bad_rational.
403 // Otherwise, dVal and denominator are multiplied by 10, until one of them
404 // is larger than (LONG_MAX / 10).
406 // NOTE: here we use 'sal_Int32' due that only values in sal_Int32 range are valid.
407 static boost::rational
<sal_Int32
> rational_FromDouble(double dVal
)
409 if ( dVal
> std::numeric_limits
<sal_Int32
>::max() ||
410 dVal
< std::numeric_limits
<sal_Int32
>::min() ||
412 throw boost::bad_rational();
414 const sal_Int32 nMAX
= std::numeric_limits
<sal_Int32
>::max() / 10;
416 while ( std::abs( dVal
) < nMAX
&& nDen
< nMAX
) {
420 return boost::rational
<sal_Int32
>( sal_Int32(dVal
), nDen
);
423 // Similar to clz_table that can be googled
424 const char nbits_table
[32] =
426 32, 1, 23, 2, 29, 24, 14, 3,
427 30, 27, 25, 18, 20, 15, 10, 4,
428 31, 22, 28, 13, 26, 17, 19, 9,
429 21, 12, 16, 8, 11, 7, 6, 5
432 static int impl_NumberOfBits( sal_uInt32 nNum
)
434 // http://en.wikipedia.org/wiki/De_Bruijn_sequence
435 // background paper: Using de Bruijn Sequences to Index a 1 in a
436 // Computer Word (1998) Charles E. Leiserson,
437 // Harald Prokop, Keith H. Randall
438 // (e.g. http://citeseer.ist.psu.edu/leiserson98using.html)
439 const sal_uInt32 nDeBruijn
= 0x7DCD629;
444 // Get it to form like 0000001111111111b
445 nNum
|= ( nNum
>> 1 );
446 nNum
|= ( nNum
>> 2 );
447 nNum
|= ( nNum
>> 4 );
448 nNum
|= ( nNum
>> 8 );
449 nNum
|= ( nNum
>> 16 );
457 // De facto shift left of nDeBruijn using multiplication (nNumber
458 // is all ones from topmost bit, thus nDeBruijn + (nDeBruijn *
459 // nNumber) => nDeBruijn * (nNumber+1) clears all those bits to
460 // zero, sets the next bit to one, and thus effectively shift-left
461 // nDeBruijn by lg2(nNumber+1). This generates a distinct 5bit
462 // sequence in the msb for each distinct position of the last
463 // leading 0 bit - that's the property of a de Bruijn number.
464 nNumber
= nDeBruijn
+ ( nDeBruijn
* nNumber
);
466 // 5-bit window indexes the result
467 return ( nbits_table
[nNumber
>> 27] ) + nBonus
;
470 /** Inaccurate cancellation for a fraction.
472 Clip both nominator and denominator to said number of bits. If
473 either of those already have equal or less number of bits used,
474 this method does nothing.
476 @param nSignificantBits denotes, how many significant binary
477 digits to maintain, in both nominator and denominator.
479 @example ReduceInaccurate(8) has an error <1% [1/2^(8-1)] - the
480 largest error occurs with the following pair of values:
482 binary 1000000011111111111111111111111b/1000000000000000000000000000000b
483 = 1082130431/1073741824
484 = approx. 1.007812499
486 A ReduceInaccurate(8) yields 1/1.
488 static void rational_ReduceInaccurate(boost::rational
<sal_Int32
>& rRational
, unsigned nSignificantBits
)
493 // http://www.boost.org/doc/libs/release/libs/rational/rational.html#Internal%20representation
494 const bool bNeg
= ( rRational
.numerator() < 0 );
495 sal_Int32 nMul
= bNeg
? -rRational
.numerator(): rRational
.numerator();
496 sal_Int32 nDiv
= rRational
.denominator();
498 DBG_ASSERT(nSignificantBits
<65, "More than 64 bit of significance is overkill!");
500 // How much bits can we lose?
501 const int nMulBitsToLose
= std::max( ( impl_NumberOfBits( nMul
) - int( nSignificantBits
) ), 0 );
502 const int nDivBitsToLose
= std::max( ( impl_NumberOfBits( nDiv
) - int( nSignificantBits
) ), 0 );
504 const int nToLose
= std::min( nMulBitsToLose
, nDivBitsToLose
);
510 if ( !nMul
|| !nDiv
) {
511 // Return without reduction
512 OSL_FAIL( "Oops, we reduced too much..." );
516 rRational
.assign( bNeg
? -nMul
: nMul
, nDiv
);
519 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */