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