summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorRüdiger Timm <rt@openoffice.org>2007-07-03 11:32:17 +0000
committerRüdiger Timm <rt@openoffice.org>2007-07-03 11:32:17 +0000
commit4d13576cc03c7d99bb392bafd78017a6b082da9b (patch)
tree7413c513db4d044310c233315086e7f8c6098992 /tools
parentb0d3ef00d4068809b775f4b799ac851b8ac01596 (diff)
INTEGRATION: CWS sixtyfour12 (1.6.88); FILE MERGED
2007/06/20 15:00:19 thb 1.6.88.4: #i78392# Due to popular demand, switched old function docs to English 2007/06/20 12:46:16 thb 1.6.88.3: #i78392# After checking for GEN:Const 2007/06/20 10:55:44 thb 1.6.88.2: #i78392# Added link to reference paper; asserting valid input range 2007/06/15 12:27:22 kendy 1.6.88.1: #i78392# Improve Fraction::ReduceInaccurate() to work on 64bit
Diffstat (limited to 'tools')
-rw-r--r--tools/source/generic/fract.cxx167
1 files changed, 111 insertions, 56 deletions
diff --git a/tools/source/generic/fract.cxx b/tools/source/generic/fract.cxx
index 040d6f15ce06..8222a5882096 100644
--- a/tools/source/generic/fract.cxx
+++ b/tools/source/generic/fract.cxx
@@ -4,9 +4,9 @@
*
* $RCSfile: fract.cxx,v $
*
- * $Revision: 1.7 $
+ * $Revision: 1.8 $
*
- * last change: $Author: hr $ $Date: 2007-06-27 22:13:27 $
+ * last change: $Author: rt $ $Date: 2007-07-03 12:32:17 $
*
* The Contents of this file are made available subject to
* the terms of GNU Lesser General Public License Version 2.1.
@@ -513,76 +513,131 @@ Fraction& Fraction::operator /= ( const Fraction& rVal )
|*
|* Beschreibung FRACT.SDW
|* Ersterstellung JOE 17.09.95
-|* Letzte Aenderung JOE 17.09.95
+|* Letzte Aenderung kendy 2007-06-13
|*
*************************************************************************/
-// Funktioniert z.Zt. nur fuer 32-Bit Werte !!!
-// Fehlerbehaftetes Kuerzen einer Fraction.
-// nSignificantBits gibt an, wieviele signifikante Binaerstellen
-// in Zaehler/Nenner mindestens erhalten bleiben sollen.
-// Beispiel: ReduceInaccurate(8) hat einen Fehler <1% [1/2^(8-1)]
-// dabei tritt der groesste Fehler bei folgendem Wertepaar auf:
-// Binaer 1000000011111111111111111111111b/1000000000000000000000000000000b
-// = 1082130431/1073741824
-// = ca. 1.007812499
-// Nach ReduceInaccurate( 8 ) wird daraus 1/1.
-void Fraction::ReduceInaccurate( unsigned nSignificantBits )
+// Similar to clz_table that can be googled
+const char nbits_table[32] =
{
- if ( !nNumerator || !nDenominator )
- return;
+ 32, 1, 23, 2, 29, 24, 14, 3,
+ 30, 27, 25, 18, 20, 15, 10, 4,
+ 31, 22, 28, 13, 26, 17, 19, 9,
+ 21, 12, 16, 8, 11, 7, 6, 5
+};
- // Zaehler und Nenner auf den Stack fuer schnelleren Zugriff
- unsigned long nMul;
- unsigned long nDiv;
- BOOL bNeg;
- if ( nNumerator >= 0 )
+static int impl_NumberOfBits( unsigned long nNum )
+{
+ // http://en.wikipedia.org/wiki/De_Bruijn_sequence
+ //
+ // background paper: Using de Bruijn Sequences to Index a 1 in a
+ // Computer Word (1998) Charles E. Leiserson,
+ // Harald Prokop, Keith H. Randall
+ // (e.g. http://citeseer.ist.psu.edu/leiserson98using.html)
+ const sal_uInt32 nDeBruijn = 0x7DCD629;
+
+ if ( nNum == 0 )
+ return 0;
+
+ // Get it to form like 0000001111111111b
+ nNum |= ( nNum >> 1 );
+ nNum |= ( nNum >> 2 );
+ nNum |= ( nNum >> 4 );
+ nNum |= ( nNum >> 8 );
+ nNum |= ( nNum >> 16 );
+
+ sal_uInt32 nNumber;
+ int nBonus = 0;
+
+#if SAL_TYPES_SIZEOFLONG == 4
+ nNumber = nNum;
+#elif SAL_TYPES_SIZEOFLONG == 8
+ nNum |= ( nNum >> 32 );
+
+ if ( nNum & 0x80000000 )
{
- nMul = (unsigned long)nNumerator;
- bNeg = FALSE;
+ nNumber = sal_uInt32( nNum >> 32 );
+ nBonus = 32;
+
+ if ( nNumber == 0 )
+ return 32;
}
else
- {
- nMul = (unsigned long)(-nNumerator);
- bNeg = TRUE;
- }
- nDiv=(unsigned long)nDenominator;
-
- unsigned long a=nMul; unsigned nMulZ=0; // Fuehrende Nullen zaehlen
- while (a<0x00800000) { nMulZ+=8; a<<=8; }
- while (a<0x80000000) { nMulZ++; a<<=1; }
- a=nDiv; unsigned nDivZ=0; // Fuehrende Nullen zaehlen
- while (a<0x00800000) { nDivZ+=8; a<<=8; }
- while (a<0x80000000) { nDivZ++; a<<=1; }
- // Anzahl der verwendeten Digits bestimmen
- // Auch hier gehe ich davon aus, dass es sich um 32Bit-Werte handelt
- int nMulDigits=(sizeof(long) * 8)-nMulZ;
- int nDivDigits=(sizeof(long) * 8)-nDivZ;
- // Nun bestimmen, wieviele Stellen hinten weg koennen
- // Hier koennte man das Ergebnis noch etwas optimieren...
- int nMulWeg=nMulDigits-nSignificantBits; if (nMulWeg<0) nMulWeg=0;
- int nDivWeg=nDivDigits-nSignificantBits; if (nDivWeg<0) nDivWeg=0;
- int nWeg=Min(nMulWeg,nDivWeg);
- nMul>>=nWeg;
- nDiv>>=nWeg;
+ nNumber = sal_uInt32( nNum & 0xFFFFFFFF );
+#else
+#error "Unknown size of long!"
+#endif
+
+ // De facto shift left of nDeBruijn using multiplication (nNumber
+ // is all ones from topmost bit, thus nDeBruijn + (nDeBruijn *
+ // nNumber) => nDeBruijn * (nNumber+1) clears all those bits to
+ // zero, sets the next bit to one, and thus effectively shift-left
+ // nDeBruijn by lg2(nNumber+1). This generates a distinct 5bit
+ // sequence in the msb for each distinct position of the last
+ // leading 0 bit - that's the property of a de Bruijn number.
+ nNumber = nDeBruijn + ( nDeBruijn * nNumber );
+
+ // 5-bit window indexes the result
+ return ( nbits_table[nNumber >> 27] ) + nBonus;
+}
+
+/** Inaccurate cancellation for a fraction.
+
+ Clip both nominator and denominator to said number of bits. If
+ either of those already have equal or less number of bits used,
+ this method does nothing.
+
+ @param nSignificantBits denotes, how many significant binary
+ digits to maintain, in both nominator and denominator.
+
+ @example ReduceInaccurate(8) has an error <1% [1/2^(8-1)] - the
+ largest error occurs with the following pair of values:
+
+ binary 1000000011111111111111111111111b/1000000000000000000000000000000b
+ = 1082130431/1073741824
+ = approx. 1.007812499
+
+ A ReduceInaccurate(8) yields 1/1.
+*/
+void Fraction::ReduceInaccurate( unsigned nSignificantBits )
+{
+ if ( !nNumerator || !nDenominator )
+ return;
+
+ // Count with unsigned longs only
+ const bool bNeg = ( nNumerator < 0 );
+ unsigned long nMul = (unsigned long)( bNeg? -nNumerator: nNumerator );
+ unsigned long nDiv = (unsigned long)( nDenominator );
+
+ DBG_ASSERT(nSignificantBits<65, "More than 64 bit of significance is overkill!");
+
+ // How much bits can we lose?
+ const int nMulBitsToLose = Max( ( impl_NumberOfBits( nMul ) - int( nSignificantBits ) ), 0 );
+ const int nDivBitsToLose = Max( ( impl_NumberOfBits( nDiv ) - int( nSignificantBits ) ), 0 );
+
+ const int nToLose = Min( nMulBitsToLose, nDivBitsToLose );
+
+ // Remove the bits
+ nMul >>= nToLose;
+ nDiv >>= nToLose;
+
if ( !nMul || !nDiv )
{
- DBG_ERROR( "Oups, beim kuerzen einer Fraction hat sich Joe verrechnet." );
+ // Return without reduction
+ DBG_ERROR( "Oops, we reduced too much..." );
return;
}
- // Nun noch kuerzen ueber GGT
- long n1=GetGGT( nMul, nDiv );
- if ( n1!=1 )
+ // Reduce
+ long n1 = GetGGT( nMul, nDiv );
+ if ( n1 != 1 )
{
- nMul/=n1;
- nDiv/=n1;
+ nMul /= n1;
+ nDiv /= n1;
}
- if ( !bNeg )
- nNumerator = (long)nMul;
- else
- nNumerator = -(long)nMul;
+
+ nNumerator = bNeg? -long( nMul ): long( nMul );
nDenominator = nDiv;
}