add doxy to MathUtils explaining what round_int and truncate_int do, and a test routi...
[vuplus_xbmc] / xbmc / utils / MathUtils.h
1 #pragma once
2 /*
3  *      Copyright (C) 2005-2008 Team XBMC
4  *      http://www.xbmc.org
5  *
6  *  This Program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2, or (at your option)
9  *  any later version.
10  *
11  *  This Program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with XBMC; see the file COPYING.  If not, write to
18  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19  *  http://www.gnu.org/copyleft/gpl.html
20  *
21  */
22
23 #include <stdint.h>
24 #include <cassert>
25 #include <climits>
26 #include <cmath>
27
28 /*! \brief Math utility class.
29  Note that the test() routine should return true for all implementations
30  */
31 namespace MathUtils
32 {
33   // GCC does something stupid with optimization on release builds if we try
34   // to assert in these functions
35
36   /*! \brief Round to nearest integer.
37    This routine does fast rounding to the nearest integer.
38    In the case (k + 0.5 for any integer k) we round up to k+1, and in all other
39    instances we should return the nearest integer.
40    Thus, { -1.5, -0.5, 0.5, 1.5 } is rounded to { -1, 0, 1, 2 }.
41    It preserves the property that round(k) - round(k-1) = 1 for all doubles k.
42
43    Make sure MathUtils::test() returns true for each implementation.
44    \sa truncate_int, test
45    */
46   inline int round_int (double x)
47   {
48     assert(x > static_cast<double>(INT_MIN / 2) - 1.0);
49     assert(x < static_cast <double>(INT_MAX / 2) + 1.0);
50     const float round_to_nearest = 0.5f;
51     int i;
52     
53 #ifndef _LINUX
54     __asm
55     {
56       fld x
57       fadd st, st (0)
58       fadd round_to_nearest
59       fistp i
60       sar i, 1
61     }
62 #else
63 #if defined(__powerpc__) || defined(__ppc__)
64     i = floor(x + round_to_nearest);
65 #elif defined(__arm__)
66     //BIG FIXME here (still has issues with rounding -0.5 to zero and not -1)
67     //the asm codes below do the following - trunc(x+0.5)
68     //this isn't correct for negativ x - values - for example 
69     //-1 gets rounded to zero because trunc(-1+0.5) == 0
70     //this is a dirty hack until someone fixes this propably in asm
71     //i've created a trac ticket for this #11767
72     //this hacks decrements the x by 1 if it is negativ
73     // so for -1 it would be trunc(-2+0.5) - which would be correct -1 then ...
74     x = x < 0 ? x-1 : x;
75
76     __asm__ __volatile__ (
77                           "vmov.F64 d1,%[rnd_val]             \n\t" // Copy round_to_nearest into a working register
78                           "vadd.F64 %P[value],%P[value],d1    \n\t" // Add round_to_nearest to value
79                           "vcvt.S32.F64 %[result],%P[value]   \n\t" // Truncate(round towards zero) and store the result
80                           : [result] "=w"(i), [value] "+w"(x)  // Outputs
81                           : [rnd_val] "Dv" (round_to_nearest)  // Inputs
82                           : "d1");                             // Clobbers
83 #else
84     __asm__ __volatile__ (
85                           "fadd %%st\n\t"
86                           "fadd %%st(1)\n\t"
87                           "fistpl %0\n\t"
88                           "sarl $1, %0\n"
89                           : "=m"(i) : "u"(round_to_nearest), "t"(x) : "st"
90                           );
91 #endif
92 #endif
93     return (i);
94   }
95
96   /*! \brief Truncate to nearest integer.
97    This routine does fast truncation to an integer.
98    It should simply drop the fractional portion of the floating point number.
99
100    Make sure MathUtils::test() returns true for each implementation.
101    \sa round_int, test
102    */
103   inline int truncate_int(double x)
104   {
105     assert(x > static_cast<double>(INT_MIN / 2) - 1.0);
106     assert(x < static_cast <double>(INT_MAX / 2) + 1.0);
107
108 #if !defined(__powerpc__) && !defined(__ppc__) && !defined(__arm__)
109     const float round_towards_m_i = -0.5f;
110 #endif
111     int i;
112
113 #ifndef _LINUX
114     __asm
115     {
116       fld x
117       fadd st, st (0)
118       fabs
119       fadd round_towards_m_i
120       fistp i
121       sar i, 1
122     }
123 #else
124 #if defined(__powerpc__) || defined(__ppc__) || defined(__arm__)
125     return (int)x;
126 #else
127     __asm__ __volatile__ (
128                           "fadd %%st\n\t"
129                           "fabs\n\t"
130                           "fadd %%st(1)\n\t"
131                           "fistpl %0\n\t"
132                           "sarl $1, %0\n"
133                           : "=m"(i) : "u"(round_towards_m_i), "t"(x) : "st"
134                           );
135 #endif
136 #endif
137     if (x < 0)
138       i = -i;
139     return (i);
140   }
141
142   inline int64_t abs(int64_t a)
143   {
144     return (a < 0) ? -a : a;
145   }
146
147   inline void hack()
148   {
149     // stupid hack to keep compiler from dropping these
150     // functions as unused
151     MathUtils::round_int(0.0);
152     MathUtils::truncate_int(0.0);
153     MathUtils::abs(0);
154   }
155
156 #if 0
157   /*! \brief test routine for round_int and truncate_int
158    Must return true on all platforms.
159    */
160   inline bool test()
161   {
162     for (int i = -8; i < 8; ++i)
163     {
164       double d = 0.25*i;
165       int r = (i < 0) ? (i - 1) / 4 : (i + 2) / 4;
166       int t = i / 4;
167       if (round_int(d) != r || truncate_int(d) != t)
168         return false;
169     }
170     return true;
171   }
172 #endif
173 } // namespace MathUtils
174