Support dvb-box on openembedded build tree.
[vuplus_xbmc] / xbmc / utils / MathUtils.h
index 4d6faec..9a6fb09 100644 (file)
@@ -1,7 +1,7 @@
 #pragma once
 /*
- *      Copyright (C) 2005-2008 Team XBMC
- *      http://www.xbmc.org
+ *      Copyright (C) 2005-2013 Team XBMC
+ *      http://xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -14,9 +14,8 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *  http://www.gnu.org/copyleft/gpl.html
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  */
 
 #include <climits>
 #include <cmath>
 
+#ifdef __SSE2__
+#include <emmintrin.h>
+#endif
+
+// use real compiler defines in here as we want to
+// avoid including system.h or other magic includes.
+// use 'gcc -dM -E - < /dev/null' or similar to find them.
+
+#if defined(__ppc__) || \
+    defined(__powerpc__) || \
+   (defined(TARGET_DARWIN_IOS) && defined(__llvm__)) || \
+   (defined(TARGET_ANDROID) && defined(__arm__)) || \
+    defined(TARGET_RASPBERRY_PI) || defined(TARGET_DVBBOX) // oskwon
+  #define DISABLE_MATHUTILS_ASM_ROUND_INT
+#endif
+
+#if defined(__ppc__) || \
+    defined(__powerpc__) || \
+   (defined(TARGET_DARWIN) && defined(__llvm__)) || \
+   (defined(TARGET_ANDROID) && defined(__arm__)) || \
+    defined(TARGET_RASPBERRY_PI) || defined(TARGET_DVBBOX) // oskwon
+  #define DISABLE_MATHUTILS_ASM_TRUNCATE_INT
+#endif
+
+/*! \brief Math utility class.
+ Note that the test() routine should return true for all implementations
+
+ See http://ldesoras.free.fr/doc/articles/rounding_en.pdf for an explanation
+ of the technique used on x86.
+ */
 namespace MathUtils
 {
   // GCC does something stupid with optimization on release builds if we try
   // to assert in these functions
-  inline int round_int (double x)
+
+  /*! \brief Round to nearest integer.
+   This routine does fast rounding to the nearest integer.
+   In the case (k + 0.5 for any integer k) we round up to k+1, and in all other
+   instances we should return the nearest integer.
+   Thus, { -1.5, -0.5, 0.5, 1.5 } is rounded to { -1, 0, 1, 2 }.
+   It preserves the property that round(k) - round(k-1) = 1 for all doubles k.
+
+   Make sure MathUtils::test() returns true for each implementation.
+   \sa truncate_int, test
+  */
+  inline int round_int(double x)
   {
     assert(x > static_cast<double>(INT_MIN / 2) - 1.0);
-    assert(x < static_cast <double>(INT_MAX / 2) + 1.0);
+    assert(x < static_cast<double>(INT_MAX / 2) + 1.0);
     const float round_to_nearest = 0.5f;
     int i;
-    
-#ifndef _LINUX
-    __asm
-    {
-      fld x
-      fadd st, st (0)
-      fadd round_to_nearest
-      fistp i
-      sar i, 1
-    }
-#else
-#if defined(__powerpc__) || defined(__ppc__)
+
+#if defined(DISABLE_MATHUTILS_ASM_ROUND_INT)
     i = floor(x + round_to_nearest);
+
 #elif defined(__arm__)
+    // From 'ARM-v7-M Architecture Reference Manual' page A7-569:
+    //  "The floating-point to integer operation (vcvt) [normally] uses the Round towards Zero rounding mode"
+    // Because of this...we must use some less-than-straightforward logic to perform this operation without
+    //  changing the rounding mode flags
+
+    /* The assembly below implements the following logic:
+     if (x < 0)
+       inc = -0.5f
+     else
+       inc = 0.5f
+     int_val = trunc(x+inc);
+     err = x - int_val;
+     if (err == 0.5f)
+       int_val++;
+     return int_val;
+    */
+
     __asm__ __volatile__ (
-                          "vmov.F64 d1,%[rnd_val]             \n\t" // Copy round_to_nearest into a working register 
-                          "vadd.F64 %P[value],%P[value],d1    \n\t" // Add round_to_nearest to value
-                          "vcvt.S32.F64 %[result],%P[value]   \n\t" // Truncate(round towards zero) and store the result
-                          : [result] "=w"(i), [value] "+w"(x)  // Outputs
-                          : [rnd_val] "Dv" (round_to_nearest)  // Inputs
-                          : "d1");                             // Clobbers
+#if defined(__ARM_PCS_VFP)
+      "fconstd d1,#%G[rnd_val]     \n\t" // Copy round_to_nearest into a working register (d1 = 0.5)
 #else
-    __asm__ __volatile__ (
-                          "fadd %%st\n\t"
-                          "fadd %%st(1)\n\t"
-                          "fistpl %0\n\t"
-                          "sarl $1, %0\n"
-                          : "=m"(i) : "u"(round_to_nearest), "t"(x) : "st"
-                          );
+      "vmov.F64 d1,%[rnd_val]      \n\t"
 #endif
+      "fcmpezd %P[value]           \n\t" // Check value against zero (value == 0?)
+      "fmstat                      \n\t" // Copy the floating-point status flags into the general-purpose status flags
+      "it mi                       \n\t"
+      "vnegmi.F64 d1, d1           \n\t" // if N-flag is set, negate round_to_nearest (if (value < 0) d1 = -1 * d1)
+      "vadd.F64 d1,%P[value],d1    \n\t" // Add round_to_nearest to value, store result in working register (d1 += value)
+      "vcvt.S32.F64 s3,d1          \n\t" // Truncate(round towards zero) (s3 = (int)d1)
+      "vmov %[result],s3           \n\t" // Store the integer result in a general-purpose register (result = s3)
+      "vcvt.F64.S32 d1,s3          \n\t" // Convert back to floating-point (d1 = (double)s3)
+      "vsub.F64 d1,%P[value],d1    \n\t" // Calculate the error (d1 = value - d1)
+#if defined(__ARM_PCS_VFP)
+      "fconstd d2,#%G[rnd_val]     \n\t" // d2 = 0.5;
+#else
+      "vmov.F64 d2,%[rnd_val]      \n\t"
 #endif
-    return (i);
-  }
-  
-  inline int ceil_int (double x)
-  {
-    assert(x > static_cast<double>(INT_MIN / 2) - 1.0);
-    assert(x < static_cast <double>(INT_MAX / 2) + 1.0);
-    
-#if !defined(__powerpc__) && !defined(__ppc__) && !defined(__arm__)
-    const float round_towards_p_i = -0.5f;
-#endif
-    int i;
-    
-#ifndef _LINUX
+      "fcmped d1, d2               \n\t" // (d1 == 0.5?)
+      "fmstat                      \n\t" // Copy the floating-point status flags into the general-purpose status flags
+      "it eq                       \n\t"
+      "addeq %[result],#1          \n\t" // (if (d1 == d2) result++;)
+      : [result] "=r"(i)                                  // Outputs
+      : [rnd_val] "Dv" (round_to_nearest), [value] "w"(x) // Inputs
+      : "d1", "d2", "s3"                                  // Clobbers
+    );
+
+#elif defined(__SSE2__)
+    const float round_dn_to_nearest = 0.4999999f;
+    i = (x > 0) ? _mm_cvttsd_si32(_mm_set_sd(x + round_to_nearest)) : _mm_cvttsd_si32(_mm_set_sd(x - round_dn_to_nearest));
+
+#elif defined(TARGET_WINDOWS)
     __asm
     {
       fld x
       fadd st, st (0)
-      fsubr round_towards_p_i
+      fadd round_to_nearest
       fistp i
       sar i, 1
     }
-#else
-#if defined(__powerpc__) || defined(__ppc__) || defined(__arm__)
-    return (int)ceil(x);
+
 #else
     __asm__ __volatile__ (
-                          "fadd %%st\n\t"
-                          "fsubr %%st(1)\n\t"
-                          "fistpl %0\n\t"
-                          "sarl $1, %0\n"
-                          : "=m"(i) : "u"(round_towards_p_i), "t"(x) : "st"
-                          );
-#endif
+      "fadd %%st\n\t"
+      "fadd %%st(1)\n\t"
+      "fistpl %0\n\t"
+      "sarl $1, %0\n"
+      : "=m"(i) : "u"(round_to_nearest), "t"(x) : "st"
+    );
+
 #endif
-    return (-i);
+
+    return i;
   }
-  
+
+  /*! \brief Truncate to nearest integer.
+   This routine does fast truncation to an integer.
+   It should simply drop the fractional portion of the floating point number.
+
+   Make sure MathUtils::test() returns true for each implementation.
+   \sa round_int, test
+  */
   inline int truncate_int(double x)
   {
     assert(x > static_cast<double>(INT_MIN / 2) - 1.0);
-    assert(x < static_cast <double>(INT_MAX / 2) + 1.0);
-    
-#if !defined(__powerpc__) && !defined(__ppc__) && !defined(__arm__)
-    const float round_towards_m_i = -0.5f;
-#endif
+    assert(x < static_cast<double>(INT_MAX / 2) + 1.0);
     int i;
-    
-#ifndef _LINUX
+
+#if defined(DISABLE_MATHUTILS_ASM_TRUNCATE_INT)
+    return i = (int)x;
+
+#elif defined(__arm__)
+    __asm__ __volatile__ (
+      "vcvt.S32.F64 %[result],%P[value]   \n\t" // Truncate(round towards zero) and store the result
+      : [result] "=w"(i)                        // Outputs
+      : [value] "w"(x)                          // Inputs
+    );
+    return i;
+
+#elif defined(TARGET_WINDOWS)
+    const float round_towards_m_i = -0.5f;
     __asm
     {
       fld x
@@ -124,38 +189,61 @@ namespace MathUtils
       fistp i
       sar i, 1
     }
+
 #else
-#if defined(__powerpc__) || defined(__ppc__) || defined(__arm__)
-    return (int)x;
-#else
+    const float round_towards_m_i = -0.5f;
     __asm__ __volatile__ (
-                          "fadd %%st\n\t"
-                          "fabs\n\t"
-                          "fadd %%st(1)\n\t"
-                          "fistpl %0\n\t"
-                          "sarl $1, %0\n"
-                          : "=m"(i) : "u"(round_towards_m_i), "t"(x) : "st"
-                          );
-#endif
+      "fadd %%st\n\t"
+      "fabs\n\t"
+      "fadd %%st(1)\n\t"
+      "fistpl %0\n\t"
+      "sarl $1, %0\n"
+      : "=m"(i) : "u"(round_towards_m_i), "t"(x) : "st"
+    );
 #endif
     if (x < 0)
       i = -i;
     return (i);
   }
-  
+
   inline int64_t abs(int64_t a)
   {
     return (a < 0) ? -a : a;
   }
-  
+
+  inline unsigned bitcount(unsigned v)
+  {
+    unsigned c = 0;
+    for (c = 0; v; c++)
+      v &= v - 1; // clear the least significant bit set
+    return c;
+  }
+
   inline void hack()
   {
     // stupid hack to keep compiler from dropping these
     // functions as unused
     MathUtils::round_int(0.0);
     MathUtils::truncate_int(0.0);
-    MathUtils::ceil_int(0.0);
     MathUtils::abs(0);
   }
+
+#if 0
+  /*! \brief test routine for round_int and truncate_int
+   Must return true on all platforms.
+   */
+  inline bool test()
+  {
+    for (int i = -8; i < 8; ++i)
+    {
+      double d = 0.25*i;
+      int r = (i < 0) ? (i - 1) / 4 : (i + 2) / 4;
+      int t = i / 4;
+      if (round_int(d) != r || truncate_int(d) != t)
+        return false;
+    }
+    return true;
+  }
+#endif
 } // namespace MathUtils