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