check/gst/gstutils.c: Added more checks for the high precision uint64 cases.
authorWim Taymans <wim.taymans@gmail.com>
Fri, 25 Nov 2005 00:02:05 +0000 (00:02 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Fri, 25 Nov 2005 00:02:05 +0000 (00:02 +0000)
Original commit message from CVS:
* check/gst/gstutils.c: (GST_START_TEST), (gst_utils_suite):
Added more checks for the high precision uint64 cases.

* gst/gstutils.c: (gst_util_uint64_scale_int64),
(gst_util_uint64_scale), (gst_util_uint64_scale_int):
Implement high precission (guint64 * guint64) / guint64.

ChangeLog
check/gst/gstutils.c
gst/gstutils.c
tests/check/gst/gstutils.c

index ba0dd4f..ab1fb89 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2005-11-25  Wim Taymans  <wim@fluendo.com>
+
+       * check/gst/gstutils.c: (GST_START_TEST), (gst_utils_suite):
+       Added more checks for the high precision uint64 cases.
+
+       * gst/gstutils.c: (gst_util_uint64_scale_int64),
+       (gst_util_uint64_scale), (gst_util_uint64_scale_int):
+       Implement high precission (guint64 * guint64) / guint64.
+
 2005-11-24  Wim Taymans  <wim@fluendo.com>
 
        * gst/base/gstbasesrc.c: (gst_base_src_query):
index a6ca771..465d278 100644 (file)
@@ -192,12 +192,49 @@ GST_START_TEST (test_math_scale)
           G_MAXINT32) != G_MAXUINT64 - 100);
 
   /* overflow */
-  fail_if (gst_util_uint64_scale_int (G_MAXUINT64 - 1, 10, 1) != G_MAXUINT64);
-  fail_if (gst_util_uint64_scale_int (G_MAXUINT64 - 1, G_MAXINT32,
+  ASSERT_WARNING (gst_util_uint64_scale_int (G_MAXUINT64 - 1, 10,
+          1) != G_MAXUINT64);
+  ASSERT_WARNING (gst_util_uint64_scale_int (G_MAXUINT64 - 1, G_MAXINT32,
           1) != G_MAXUINT64);
 
 } GST_END_TEST;
 
+GST_START_TEST (test_math_scale_uint64)
+{
+  fail_if (gst_util_uint64_scale (1, 1, 1) != 1);
+
+  fail_if (gst_util_uint64_scale (10, 10, 1) != 100);
+  fail_if (gst_util_uint64_scale (10, 10, 2) != 50);
+
+  fail_if (gst_util_uint64_scale (0, 10, 2) != 0);
+  fail_if (gst_util_uint64_scale (0, 0, 2) != 0);
+
+  fail_if (gst_util_uint64_scale (G_MAXUINT32, 5, 1) != G_MAXUINT32 * 5LL);
+  fail_if (gst_util_uint64_scale (G_MAXUINT32, 10, 2) != G_MAXUINT32 * 5LL);
+
+  fail_if (gst_util_uint64_scale (G_MAXUINT32, 1, 5) != G_MAXUINT32 / 5LL);
+  fail_if (gst_util_uint64_scale (G_MAXUINT32, 2, 10) != G_MAXUINT32 / 5LL);
+
+  /* not quite overflow */
+  fail_if (gst_util_uint64_scale (G_MAXUINT64 - 1, 10, 10) != G_MAXUINT64 - 1);
+  fail_if (gst_util_uint64_scale (G_MAXUINT64 - 1, G_MAXUINT32,
+          G_MAXUINT32) != G_MAXUINT64 - 1);
+  fail_if (gst_util_uint64_scale (G_MAXUINT64 - 100, G_MAXUINT32,
+          G_MAXUINT32) != G_MAXUINT64 - 100);
+
+  fail_if (gst_util_uint64_scale (G_MAXUINT64 - 1, 10, 10) != G_MAXUINT64 - 1);
+  fail_if (gst_util_uint64_scale (G_MAXUINT64 - 1, G_MAXUINT64,
+          G_MAXUINT64) != G_MAXUINT64 - 1);
+  fail_if (gst_util_uint64_scale (G_MAXUINT64 - 100, G_MAXUINT64,
+          G_MAXUINT64) != G_MAXUINT64 - 100);
+
+  /* overflow */
+  ASSERT_WARNING (gst_util_uint64_scale (G_MAXUINT64 - 1, 10,
+          1) != G_MAXUINT64);
+  ASSERT_WARNING (gst_util_uint64_scale (G_MAXUINT64 - 1, G_MAXUINT64,
+          1) != G_MAXUINT64);
+
+} GST_END_TEST;
 Suite *
 gst_utils_suite (void)
 {
@@ -208,6 +245,7 @@ gst_utils_suite (void)
   tcase_add_test (tc_chain, test_buffer_probe_n_times);
   tcase_add_test (tc_chain, test_buffer_probe_once);
   tcase_add_test (tc_chain, test_math_scale);
+  tcase_add_test (tc_chain, test_math_scale_uint64);
   return s;
 }
 
index aaf00be..3a182c9 100644 (file)
@@ -356,6 +356,89 @@ typedef union
   } l;
 } GstUInt64;
 
+static guint64
+gst_util_uint64_scale_int64 (guint64 val, guint64 num, guint64 denom)
+{
+  GstUInt64 a0, a1, b0, b1, c0, ct, c1, result;
+  GstUInt64 v, n, d;
+
+  /* prepare input */
+  v.ll = val;
+  n.ll = num;
+  d.ll = denom;
+
+  /* do 128 bits multiply 
+   *                   nh   nl
+   *                *  vh   vl
+   *                ----------
+   * a0 =              vl * nl
+   * a1 =         vl * nh
+   * b0 =         vh * nl
+   * b1 =  + vh * nh
+   *       -------------------
+   * c1,c0
+   */
+  a0.ll = (guint64) v.l.low * n.l.low;
+  a1.ll = (guint64) v.l.low * n.l.high;
+  b0.ll = (guint64) v.l.high * n.l.low;
+  b1.ll = (guint64) v.l.high * n.l.high;
+
+  /* and sum together with carry into 128 bits c1, c0 */
+  c0.l.low = a0.l.low;
+  ct.ll = (guint64) a0.l.high + a1.l.low + b0.l.low;
+  c0.l.high = ct.l.low;
+  c1.ll = (guint64) a1.l.high + b0.l.high + ct.l.high + b1.ll;
+
+  /* if high bits bigger than denom, we overflow */
+  if (c1.ll >= denom)
+    goto overflow;
+
+  /* and 128/64 bits division, result fits 64 bits */
+  if (denom <= G_MAXUINT32) {
+    guint32 den = (guint32) denom;
+
+    /* easy case, (c1,c0)128/(den)32 division */
+    c1.l.high %= den;
+    c1.l.high = c1.ll % den;
+    c1.l.low = c0.l.high;
+    c0.l.high = c1.ll % den;
+    result.l.high = c1.ll / den;
+    result.l.low = c0.ll / den;
+  } else {
+    gint i;
+    gint64 mask;
+
+    /* full 128/64 case, very slow... */
+    /* quotient is c1, c0 */
+    a0.ll = 0;                  /* remainder a0 */
+
+    /* This can be done faster, inspiration in Hacker's Delight p152 */
+    for (i = 0; i < 128; i++) {
+      /* shift 192 bits remainder:quotient, we only need to
+       * check the top bit since denom is only 64 bits. */
+      /* sign extend top bit into mask */
+      mask = ((gint32) a0.l.high) >> 31;
+      mask |= (a0.ll = (a0.ll << 1) | (c1.l.high >> 31));
+      c1.ll = (c1.ll << 1) | (c0.l.high >> 31);
+      c0.ll <<= 1;
+
+      /* if remainder >= denom or top bit was set */
+      if (mask >= denom) {
+        a0.ll -= denom;
+        c0.ll += 1;
+      }
+    }
+    result.ll = c0.ll;
+  }
+  return result.ll;
+
+overflow:
+  {
+    g_warning ("int64 scaling overflow");
+    return G_MAXUINT64;
+  }
+}
+
 /**
  * gst_util_uint64_scale:
  * @val: the number to scale
@@ -364,6 +447,8 @@ typedef union
  *
  * Scale @val by @num / @denom, trying to avoid overflows.
  *
+ * This function can potentially be very slow if denom > G_MAXUINT32.
+ *
  * Returns: @val * @num / @denom, trying to avoid overflows.
  */
 guint64
@@ -390,9 +475,8 @@ do_int32:
   return gst_util_uint64_scale_int (val, (gint) num, (gint) denom);
 
 do_int64:
-  /* implement me with fixed point, if you care */
-  return gst_gdouble_to_guint64 (gst_guint64_to_gdouble (val) *
-      ((gst_guint64_to_gdouble (num)) / gst_guint64_to_gdouble (denom)));
+  /* to the more heavy implementations... */
+  return gst_util_uint64_scale_int64 (val, num, denom);
 }
 
 /**
@@ -443,6 +527,7 @@ gst_util_uint64_scale_int (guint64 val, gint num, gint denom)
 
 overflow:
   {
+    g_warning ("int scaling overflow");
     return G_MAXUINT64;
   }
 }
index a6ca771..465d278 100644 (file)
@@ -192,12 +192,49 @@ GST_START_TEST (test_math_scale)
           G_MAXINT32) != G_MAXUINT64 - 100);
 
   /* overflow */
-  fail_if (gst_util_uint64_scale_int (G_MAXUINT64 - 1, 10, 1) != G_MAXUINT64);
-  fail_if (gst_util_uint64_scale_int (G_MAXUINT64 - 1, G_MAXINT32,
+  ASSERT_WARNING (gst_util_uint64_scale_int (G_MAXUINT64 - 1, 10,
+          1) != G_MAXUINT64);
+  ASSERT_WARNING (gst_util_uint64_scale_int (G_MAXUINT64 - 1, G_MAXINT32,
           1) != G_MAXUINT64);
 
 } GST_END_TEST;
 
+GST_START_TEST (test_math_scale_uint64)
+{
+  fail_if (gst_util_uint64_scale (1, 1, 1) != 1);
+
+  fail_if (gst_util_uint64_scale (10, 10, 1) != 100);
+  fail_if (gst_util_uint64_scale (10, 10, 2) != 50);
+
+  fail_if (gst_util_uint64_scale (0, 10, 2) != 0);
+  fail_if (gst_util_uint64_scale (0, 0, 2) != 0);
+
+  fail_if (gst_util_uint64_scale (G_MAXUINT32, 5, 1) != G_MAXUINT32 * 5LL);
+  fail_if (gst_util_uint64_scale (G_MAXUINT32, 10, 2) != G_MAXUINT32 * 5LL);
+
+  fail_if (gst_util_uint64_scale (G_MAXUINT32, 1, 5) != G_MAXUINT32 / 5LL);
+  fail_if (gst_util_uint64_scale (G_MAXUINT32, 2, 10) != G_MAXUINT32 / 5LL);
+
+  /* not quite overflow */
+  fail_if (gst_util_uint64_scale (G_MAXUINT64 - 1, 10, 10) != G_MAXUINT64 - 1);
+  fail_if (gst_util_uint64_scale (G_MAXUINT64 - 1, G_MAXUINT32,
+          G_MAXUINT32) != G_MAXUINT64 - 1);
+  fail_if (gst_util_uint64_scale (G_MAXUINT64 - 100, G_MAXUINT32,
+          G_MAXUINT32) != G_MAXUINT64 - 100);
+
+  fail_if (gst_util_uint64_scale (G_MAXUINT64 - 1, 10, 10) != G_MAXUINT64 - 1);
+  fail_if (gst_util_uint64_scale (G_MAXUINT64 - 1, G_MAXUINT64,
+          G_MAXUINT64) != G_MAXUINT64 - 1);
+  fail_if (gst_util_uint64_scale (G_MAXUINT64 - 100, G_MAXUINT64,
+          G_MAXUINT64) != G_MAXUINT64 - 100);
+
+  /* overflow */
+  ASSERT_WARNING (gst_util_uint64_scale (G_MAXUINT64 - 1, 10,
+          1) != G_MAXUINT64);
+  ASSERT_WARNING (gst_util_uint64_scale (G_MAXUINT64 - 1, G_MAXUINT64,
+          1) != G_MAXUINT64);
+
+} GST_END_TEST;
 Suite *
 gst_utils_suite (void)
 {
@@ -208,6 +245,7 @@ gst_utils_suite (void)
   tcase_add_test (tc_chain, test_buffer_probe_n_times);
   tcase_add_test (tc_chain, test_buffer_probe_once);
   tcase_add_test (tc_chain, test_math_scale);
+  tcase_add_test (tc_chain, test_math_scale_uint64);
   return s;
 }