[droid] MathUtils: change #ifdef once again to cover all android builds. it actually...
[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 #ifdef __SSE2__
29 #include <emmintrin.h>
30 #endif
31
32 // use real compiler defines in here as we want to
33 // avoid including system.h or other magic includes.
34 // use 'gcc -dM -E - < /dev/null' or similar to find them.
35
36 #if defined(__ppc__) || \
37     defined(__powerpc__) || \
38    (defined(__APPLE__) && defined(__arm__) && defined(__llvm__)) || \
39    (defined(__ANDROID__) && defined(__arm__))
40   #define DISABLE_MATHUTILS_ASM_ROUND_INT
41 #endif
42
43 #if defined(__ppc__) || \
44     defined(__powerpc__) || \
45    (defined(__APPLE__) && defined(__llvm__)) || \
46    (defined(__ANDROID__) && defined(__arm__))
47   #define DISABLE_MATHUTILS_ASM_TRUNCATE_INT
48 #endif
49
50 /*! \brief Math utility class.
51  Note that the test() routine should return true for all implementations
52
53  See http://ldesoras.free.fr/doc/articles/rounding_en.pdf for an explanation
54  of the technique used on x86.
55  */
56 namespace MathUtils
57 {
58   // GCC does something stupid with optimization on release builds if we try
59   // to assert in these functions
60
61   /*! \brief Round to nearest integer.
62    This routine does fast rounding to the nearest integer.
63    In the case (k + 0.5 for any integer k) we round up to k+1, and in all other
64    instances we should return the nearest integer.
65    Thus, { -1.5, -0.5, 0.5, 1.5 } is rounded to { -1, 0, 1, 2 }.
66    It preserves the property that round(k) - round(k-1) = 1 for all doubles k.
67
68    Make sure MathUtils::test() returns true for each implementation.
69    \sa truncate_int, test
70   */
71   inline int round_int(double x)
72   {
73     assert(x > static_cast<double>(INT_MIN / 2) - 1.0);
74     assert(x < static_cast<double>(INT_MAX / 2) + 1.0);
75     const float round_to_nearest = 0.5f;
76     int i;
77
78 #if defined(DISABLE_MATHUTILS_ASM_ROUND_INT)
79     i = floor(x + round_to_nearest);
80
81 #elif defined(__arm__)
82     // From 'ARM-v7-M Architecture Reference Manual' page A7-569:
83     //  "The floating-point to integer operation (vcvt) [normally] uses the Round towards Zero rounding mode"
84     // Because of this...we must use some less-than-straightforward logic to perform this operation without
85     //  changing the rounding mode flags
86
87     /* The assembly below implements the following logic:
88      if (x < 0)
89        inc = -0.5f
90      else
91        inc = 0.5f
92      int_val = trunc(x+inc);
93      err = x - int_val;
94      if (err == 0.5f)
95        int_val++;
96      return int_val;
97     */
98
99     __asm__ __volatile__ (
100 #if defined(__ARM_PCS_VFP)
101       "fconstd d1,#%G[rnd_val]     \n\t" // Copy round_to_nearest into a working register (d1 = 0.5)
102 #else
103       "vmov.F64 d1,%[rnd_val]      \n\t"
104 #endif
105       "fcmpezd %P[value]           \n\t" // Check value against zero (value == 0?)
106       "fmstat                      \n\t" // Copy the floating-point status flags into the general-purpose status flags
107       "it mi                       \n\t"
108       "vnegmi.F64 d1, d1           \n\t" // if N-flag is set, negate round_to_nearest (if (value < 0) d1 = -1 * d1)
109       "vadd.F64 d1,%P[value],d1    \n\t" // Add round_to_nearest to value, store result in working register (d1 += value)
110       "vcvt.S32.F64 s3,d1          \n\t" // Truncate(round towards zero) (s3 = (int)d1)
111       "vmov %[result],s3           \n\t" // Store the integer result in a general-purpose register (result = s3)
112       "vcvt.F64.S32 d1,s3          \n\t" // Convert back to floating-point (d1 = (double)s3)
113       "vsub.F64 d1,%P[value],d1    \n\t" // Calculate the error (d1 = value - d1)
114 #if defined(__ARM_PCS_VFP)
115       "fconstd d2,#%G[rnd_val]     \n\t" // d2 = 0.5;
116 #else
117       "vmov.F64 d2,%[rnd_val]      \n\t"
118 #endif
119       "fcmped d1, d2               \n\t" // (d1 == 0.5?)
120       "fmstat                      \n\t" // Copy the floating-point status flags into the general-purpose status flags
121       "it eq                       \n\t"
122       "addeq %[result],#1          \n\t" // (if (d1 == d2) result++;)
123       : [result] "=r"(i)                                  // Outputs
124       : [rnd_val] "Dv" (round_to_nearest), [value] "w"(x) // Inputs
125       : "d1", "d2", "s3"                                  // Clobbers
126     );
127
128 #elif defined(__SSE2__)
129     const float round_dn_to_nearest = 0.4999999f;
130     i = (x > 0) ? _mm_cvttsd_si32(_mm_set_sd(x + round_to_nearest)) : _mm_cvttsd_si32(_mm_set_sd(x - round_dn_to_nearest));
131
132 #elif defined(_WIN32)
133     __asm
134     {
135       fld x
136       fadd st, st (0)
137       fadd round_to_nearest
138       fistp i
139       sar i, 1
140     }
141
142 #else
143     __asm__ __volatile__ (
144       "fadd %%st\n\t"
145       "fadd %%st(1)\n\t"
146       "fistpl %0\n\t"
147       "sarl $1, %0\n"
148       : "=m"(i) : "u"(round_to_nearest), "t"(x) : "st"
149     );
150
151 #endif
152
153     return i;
154   }
155
156   /*! \brief Truncate to nearest integer.
157    This routine does fast truncation to an integer.
158    It should simply drop the fractional portion of the floating point number.
159
160    Make sure MathUtils::test() returns true for each implementation.
161    \sa round_int, test
162   */
163   inline int truncate_int(double x)
164   {
165     assert(x > static_cast<double>(INT_MIN / 2) - 1.0);
166     assert(x < static_cast<double>(INT_MAX / 2) + 1.0);
167     int i;
168
169 #if defined(DISABLE_MATHUTILS_ASM_TRUNCATE_INT)
170     return i = (int)x;
171
172 #elif defined(__arm__)
173     __asm__ __volatile__ (
174       "vcvt.S32.F64 %[result],%P[value]   \n\t" // Truncate(round towards zero) and store the result
175       : [result] "=w"(i)                        // Outputs
176       : [value] "w"(x)                          // Inputs
177     );
178     return i;
179
180 #elif defined(_WIN32)
181     const float round_towards_m_i = -0.5f;
182     __asm
183     {
184       fld x
185       fadd st, st (0)
186       fabs
187       fadd round_towards_m_i
188       fistp i
189       sar i, 1
190     }
191
192 #else
193     const float round_towards_m_i = -0.5f;
194     __asm__ __volatile__ (
195       "fadd %%st\n\t"
196       "fabs\n\t"
197       "fadd %%st(1)\n\t"
198       "fistpl %0\n\t"
199       "sarl $1, %0\n"
200       : "=m"(i) : "u"(round_towards_m_i), "t"(x) : "st"
201     );
202 #endif
203     if (x < 0)
204       i = -i;
205     return (i);
206   }
207
208   inline int64_t abs(int64_t a)
209   {
210     return (a < 0) ? -a : a;
211   }
212
213   inline unsigned bitcount(unsigned v)
214   {
215     unsigned c = 0;
216     for (c = 0; v; c++)
217       v &= v - 1; // clear the least significant bit set
218     return c;
219   }
220
221   inline void hack()
222   {
223     // stupid hack to keep compiler from dropping these
224     // functions as unused
225     MathUtils::round_int(0.0);
226     MathUtils::truncate_int(0.0);
227     MathUtils::abs(0);
228   }
229
230 #if 0
231   /*! \brief test routine for round_int and truncate_int
232    Must return true on all platforms.
233    */
234   inline bool test()
235   {
236     for (int i = -8; i < 8; ++i)
237     {
238       double d = 0.25*i;
239       int r = (i < 0) ? (i - 1) / 4 : (i + 2) / 4;
240       int t = i / 4;
241       if (round_int(d) != r || truncate_int(d) != t)
242         return false;
243     }
244     return true;
245   }
246 #endif
247 } // namespace MathUtils
248