+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):
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)
{
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;
}
} 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
*
* 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
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);
}
/**
overflow:
{
+ g_warning ("int scaling overflow");
return G_MAXUINT64;
}
}
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)
{
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;
}