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