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