check/gst/gstutils.c: Added more checks for the high precision uint64 cases.

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.
This commit is contained in:
Wim Taymans 2005-11-25 00:02:05 +00:00
parent d0c276b717
commit fb7302fc41
4 changed files with 177 additions and 7 deletions

View file

@ -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):

View 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;
}

View 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;
}
}

View 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;
}