mirror of
https://mirror.skon.top/https://github.com/FFmpeg/FFmpeg
synced 2026-04-20 21:00:41 +08:00
tests/fate/libavutil: add FATE test for mathematics
Test the integer math utility functions: av_gcd, av_rescale, av_rescale_rnd (all rounding modes including PASS_MINMAX), av_rescale_q, av_compare_ts, av_compare_mod, av_rescale_delta, and av_add_stable. Includes large-value tests that exercise the 128-bit multiply path in av_rescale_rnd. av_bessel_i0 is not tested since it uses floating point math that is not bitexact across platforms. Coverage for libavutil/mathematics.c: 0.00% -> 82.03% Remaining uncovered lines are av_bessel_i0 (float, 23 lines) and one edge case fallback in av_rescale_delta.
This commit is contained in:
@@ -287,6 +287,7 @@ TESTPROGS = adler32 \
|
||||
lfg \
|
||||
lls \
|
||||
log \
|
||||
mathematics \
|
||||
md5 \
|
||||
murmur3 \
|
||||
opt \
|
||||
|
||||
175
libavutil/tests/mathematics.c
Normal file
175
libavutil/tests/mathematics.c
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* This file is part of FFmpeg.
|
||||
*
|
||||
* FFmpeg is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* FFmpeg is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with FFmpeg; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libavutil/avutil.h"
|
||||
#include "libavutil/macros.h"
|
||||
#include "libavutil/mathematics.h"
|
||||
#include "libavutil/rational.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int64_t last;
|
||||
|
||||
/* av_gcd */
|
||||
printf("Testing av_gcd()\n");
|
||||
static const struct { int64_t a, b, expected; } gcd_tests[] = {
|
||||
{ 0, 0, 0 },
|
||||
{ 1, 0, 1 },
|
||||
{ 0, 1, 1 },
|
||||
{ 6, 4, 2 },
|
||||
{ 12, 8, 4 },
|
||||
{ 17, 13, 1 },
|
||||
{ 100, 75, 25 },
|
||||
{ -6, 4, 2 },
|
||||
{ 6, -4, 2 },
|
||||
};
|
||||
for (int i = 0; i < FF_ARRAY_ELEMS(gcd_tests); i++)
|
||||
printf("gcd(%"PRId64", %"PRId64") = %"PRId64" %s\n",
|
||||
gcd_tests[i].a, gcd_tests[i].b,
|
||||
av_gcd(gcd_tests[i].a, gcd_tests[i].b),
|
||||
av_gcd(gcd_tests[i].a, gcd_tests[i].b) == gcd_tests[i].expected ? "OK" : "FAIL");
|
||||
|
||||
/* av_rescale */
|
||||
printf("\nTesting av_rescale()\n");
|
||||
printf("rescale(6, 3, 2) = %"PRId64"\n", av_rescale(6, 3, 2));
|
||||
printf("rescale(0, 3, 2) = %"PRId64"\n", av_rescale(0, 3, 2));
|
||||
printf("rescale(1, 1, 1) = %"PRId64"\n", av_rescale(1, 1, 1));
|
||||
printf("rescale(-6, 3, 2) = %"PRId64"\n", av_rescale(-6, 3, 2));
|
||||
printf("rescale(90000, 1, 90000) = %"PRId64"\n", av_rescale(90000, 1, 90000));
|
||||
|
||||
/* av_rescale_rnd with different rounding modes */
|
||||
printf("\nTesting av_rescale_rnd()\n");
|
||||
static const struct {
|
||||
int64_t a, b, c;
|
||||
enum AVRounding rnd;
|
||||
int64_t expected;
|
||||
} rnd_tests[] = {
|
||||
{ 7, 1, 2, AV_ROUND_ZERO, 3 },
|
||||
{ 7, 1, 2, AV_ROUND_INF, 4 },
|
||||
{ 7, 1, 2, AV_ROUND_DOWN, 3 },
|
||||
{ 7, 1, 2, AV_ROUND_UP, 4 },
|
||||
{ 7, 1, 2, AV_ROUND_NEAR_INF, 4 },
|
||||
{ -7, 1, 2, AV_ROUND_ZERO, -3 },
|
||||
{ -7, 1, 2, AV_ROUND_INF, -4 },
|
||||
{ -7, 1, 2, AV_ROUND_DOWN, -4 },
|
||||
{ -7, 1, 2, AV_ROUND_UP, -3 },
|
||||
{ 6, 1, 2, AV_ROUND_NEAR_INF, 3 },
|
||||
};
|
||||
for (int i = 0; i < FF_ARRAY_ELEMS(rnd_tests); i++) {
|
||||
int64_t r = av_rescale_rnd(rnd_tests[i].a, rnd_tests[i].b,
|
||||
rnd_tests[i].c, rnd_tests[i].rnd);
|
||||
printf("rescale_rnd(%"PRId64", %"PRId64", %"PRId64", %d) = %"PRId64" %s\n",
|
||||
rnd_tests[i].a, rnd_tests[i].b, rnd_tests[i].c,
|
||||
rnd_tests[i].rnd, r,
|
||||
r == rnd_tests[i].expected ? "OK" : "FAIL");
|
||||
}
|
||||
|
||||
/* AV_ROUND_PASS_MINMAX */
|
||||
printf("\nTesting AV_ROUND_PASS_MINMAX\n");
|
||||
printf("INT64_MIN passthrough: %s\n",
|
||||
av_rescale_rnd(INT64_MIN, 1, 2,
|
||||
AV_ROUND_UP | AV_ROUND_PASS_MINMAX) == INT64_MIN ? "OK" : "FAIL");
|
||||
printf("INT64_MAX passthrough: %s\n",
|
||||
av_rescale_rnd(INT64_MAX, 1, 2,
|
||||
AV_ROUND_UP | AV_ROUND_PASS_MINMAX) == INT64_MAX ? "OK" : "FAIL");
|
||||
printf("normal with PASS_MINMAX: %"PRId64"\n",
|
||||
av_rescale_rnd(3, 1, 2, AV_ROUND_UP | AV_ROUND_PASS_MINMAX));
|
||||
|
||||
/* large value rescale (exercises 128-bit multiply path) */
|
||||
printf("\nTesting large value rescale\n");
|
||||
printf("rescale(INT64_MAX/2, 2, 1) = %"PRId64"\n",
|
||||
av_rescale_rnd(INT64_MAX / 2, 2, 1, AV_ROUND_ZERO));
|
||||
printf("rescale(1000000007, 1000000009, 1000000007) = %"PRId64"\n",
|
||||
av_rescale(1000000007LL, 1000000009LL, 1000000007LL));
|
||||
/* b and c both > INT_MAX triggers 128-bit multiply */
|
||||
printf("rescale_rnd(10, INT_MAX+1, INT_MAX+1, ZERO) = %"PRId64"\n",
|
||||
av_rescale_rnd(10, (int64_t)INT32_MAX + 1, (int64_t)INT32_MAX + 1, AV_ROUND_ZERO));
|
||||
printf("rescale_rnd(7, 3000000000, 2000000000, NEAR_INF) = %"PRId64"\n",
|
||||
av_rescale_rnd(7, 3000000000LL, 2000000000LL, AV_ROUND_NEAR_INF));
|
||||
|
||||
/* av_rescale_q */
|
||||
printf("\nTesting av_rescale_q()\n");
|
||||
printf("rescale_q(90000, 1/90000, 1/1000) = %"PRId64"\n",
|
||||
av_rescale_q(90000, (AVRational){1, 90000}, (AVRational){1, 1000}));
|
||||
printf("rescale_q(48000, 1/48000, 1/44100) = %"PRId64"\n",
|
||||
av_rescale_q(48000, (AVRational){1, 48000}, (AVRational){1, 44100}));
|
||||
|
||||
/* av_compare_ts */
|
||||
printf("\nTesting av_compare_ts()\n");
|
||||
printf("compare(1, 1/1, 1, 1/1) = %d\n",
|
||||
av_compare_ts(1, (AVRational){1, 1}, 1, (AVRational){1, 1}));
|
||||
printf("compare(1, 1/1, 2, 1/1) = %d\n",
|
||||
av_compare_ts(1, (AVRational){1, 1}, 2, (AVRational){1, 1}));
|
||||
printf("compare(2, 1/1, 1, 1/1) = %d\n",
|
||||
av_compare_ts(2, (AVRational){1, 1}, 1, (AVRational){1, 1}));
|
||||
printf("compare(1, 1/1000, 1, 1/90000) = %d\n",
|
||||
av_compare_ts(1, (AVRational){1, 1000}, 1, (AVRational){1, 90000}));
|
||||
/* large values trigger rescale-based comparison path */
|
||||
printf("compare(INT64_MAX/2, 1/1, INT64_MAX/3, 1/1) = %d\n",
|
||||
av_compare_ts(INT64_MAX / 2, (AVRational){1, 1},
|
||||
INT64_MAX / 3, (AVRational){1, 1}));
|
||||
|
||||
/* av_compare_mod */
|
||||
printf("\nTesting av_compare_mod()\n");
|
||||
printf("compare_mod(3, 1, 16) = %"PRId64"\n", av_compare_mod(3, 1, 16));
|
||||
printf("compare_mod(1, 3, 16) = %"PRId64"\n", av_compare_mod(1, 3, 16));
|
||||
printf("compare_mod(5, 5, 16) = %"PRId64"\n", av_compare_mod(5, 5, 16));
|
||||
|
||||
/* av_rescale_delta */
|
||||
printf("\nTesting av_rescale_delta()\n");
|
||||
last = AV_NOPTS_VALUE;
|
||||
for (int i = 0; i < 4; i++)
|
||||
printf("delta step %d: %"PRId64"\n", i,
|
||||
av_rescale_delta((AVRational){1, 48000}, i * 1024,
|
||||
(AVRational){1, 48000}, 1024,
|
||||
&last, (AVRational){1, 44100}));
|
||||
/* trigger clip-based path: use different in_tb and fs_tb */
|
||||
last = AV_NOPTS_VALUE;
|
||||
for (int i = 0; i < 4; i++)
|
||||
printf("delta clip %d: %"PRId64"\n", i,
|
||||
av_rescale_delta((AVRational){1, 44100}, i * 940,
|
||||
(AVRational){1, 48000}, 1024,
|
||||
&last, (AVRational){1, 90000}));
|
||||
|
||||
/* av_add_stable */
|
||||
printf("\nTesting av_add_stable()\n");
|
||||
printf("add_stable(0, 1/1, 1/1000, 500) = %"PRId64"\n",
|
||||
av_add_stable((AVRational){1, 1}, 0, (AVRational){1, 1000}, 500));
|
||||
printf("add_stable(1000, 1/90000, 1/48000, 1024) = %"PRId64"\n",
|
||||
av_add_stable((AVRational){1, 90000}, 1000, (AVRational){1, 48000}, 1024));
|
||||
/* non-exact division path (m >= d, general case) */
|
||||
printf("add_stable(0, 1/48000, 1/90000, 90000) = %"PRId64"\n",
|
||||
av_add_stable((AVRational){1, 48000}, 0, (AVRational){1, 90000}, 90000));
|
||||
printf("add_stable(100, 1/1000, 1/90000, 3000) = %"PRId64"\n",
|
||||
av_add_stable((AVRational){1, 1000}, 100, (AVRational){1, 90000}, 3000));
|
||||
/* repeated addition: verify no rounding error accumulation */
|
||||
{
|
||||
int64_t ts = 0;
|
||||
for (int i = 0; i < 10000; i++)
|
||||
ts = av_add_stable((AVRational){1, 48000}, ts, (AVRational){1, 48000}, 1024);
|
||||
printf("add_stable 10000x1024 at 1/48000: %"PRId64" (expected %"PRId64") %s\n",
|
||||
ts, (int64_t)10000 * 1024,
|
||||
ts == (int64_t)10000 * 1024 ? "OK" : "FAIL");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -116,6 +116,10 @@ FATE_LIBAVUTIL += fate-lfg
|
||||
fate-lfg: libavutil/tests/lfg$(EXESUF)
|
||||
fate-lfg: CMD = run libavutil/tests/lfg$(EXESUF)
|
||||
|
||||
FATE_LIBAVUTIL += fate-mathematics
|
||||
fate-mathematics: libavutil/tests/mathematics$(EXESUF)
|
||||
fate-mathematics: CMD = run libavutil/tests/mathematics$(EXESUF)
|
||||
|
||||
FATE_LIBAVUTIL += fate-md5
|
||||
fate-md5: libavutil/tests/md5$(EXESUF)
|
||||
fate-md5: CMD = run libavutil/tests/md5$(EXESUF)
|
||||
|
||||
73
tests/ref/fate/mathematics
Normal file
73
tests/ref/fate/mathematics
Normal file
@@ -0,0 +1,73 @@
|
||||
Testing av_gcd()
|
||||
gcd(0, 0) = 0 OK
|
||||
gcd(1, 0) = 1 OK
|
||||
gcd(0, 1) = 1 OK
|
||||
gcd(6, 4) = 2 OK
|
||||
gcd(12, 8) = 4 OK
|
||||
gcd(17, 13) = 1 OK
|
||||
gcd(100, 75) = 25 OK
|
||||
gcd(-6, 4) = 2 OK
|
||||
gcd(6, -4) = 2 OK
|
||||
|
||||
Testing av_rescale()
|
||||
rescale(6, 3, 2) = 9
|
||||
rescale(0, 3, 2) = 0
|
||||
rescale(1, 1, 1) = 1
|
||||
rescale(-6, 3, 2) = -9
|
||||
rescale(90000, 1, 90000) = 1
|
||||
|
||||
Testing av_rescale_rnd()
|
||||
rescale_rnd(7, 1, 2, 0) = 3 OK
|
||||
rescale_rnd(7, 1, 2, 1) = 4 OK
|
||||
rescale_rnd(7, 1, 2, 2) = 3 OK
|
||||
rescale_rnd(7, 1, 2, 3) = 4 OK
|
||||
rescale_rnd(7, 1, 2, 5) = 4 OK
|
||||
rescale_rnd(-7, 1, 2, 0) = -3 OK
|
||||
rescale_rnd(-7, 1, 2, 1) = -4 OK
|
||||
rescale_rnd(-7, 1, 2, 2) = -4 OK
|
||||
rescale_rnd(-7, 1, 2, 3) = -3 OK
|
||||
rescale_rnd(6, 1, 2, 5) = 3 OK
|
||||
|
||||
Testing AV_ROUND_PASS_MINMAX
|
||||
INT64_MIN passthrough: OK
|
||||
INT64_MAX passthrough: OK
|
||||
normal with PASS_MINMAX: 2
|
||||
|
||||
Testing large value rescale
|
||||
rescale(INT64_MAX/2, 2, 1) = 9223372036854775806
|
||||
rescale(1000000007, 1000000009, 1000000007) = 1000000009
|
||||
rescale_rnd(10, INT_MAX+1, INT_MAX+1, ZERO) = 10
|
||||
rescale_rnd(7, 3000000000, 2000000000, NEAR_INF) = 11
|
||||
|
||||
Testing av_rescale_q()
|
||||
rescale_q(90000, 1/90000, 1/1000) = 1000
|
||||
rescale_q(48000, 1/48000, 1/44100) = 44100
|
||||
|
||||
Testing av_compare_ts()
|
||||
compare(1, 1/1, 1, 1/1) = 0
|
||||
compare(1, 1/1, 2, 1/1) = -1
|
||||
compare(2, 1/1, 1, 1/1) = 1
|
||||
compare(1, 1/1000, 1, 1/90000) = 1
|
||||
compare(INT64_MAX/2, 1/1, INT64_MAX/3, 1/1) = 1
|
||||
|
||||
Testing av_compare_mod()
|
||||
compare_mod(3, 1, 16) = 2
|
||||
compare_mod(1, 3, 16) = -2
|
||||
compare_mod(5, 5, 16) = 0
|
||||
|
||||
Testing av_rescale_delta()
|
||||
delta step 0: 0
|
||||
delta step 1: 941
|
||||
delta step 2: 1882
|
||||
delta step 3: 2822
|
||||
delta clip 0: 0
|
||||
delta clip 1: 1920
|
||||
delta clip 2: 3838
|
||||
delta clip 3: 5756
|
||||
|
||||
Testing av_add_stable()
|
||||
add_stable(0, 1/1, 1/1000, 500) = 0
|
||||
add_stable(1000, 1/90000, 1/48000, 1024) = 2920
|
||||
add_stable(0, 1/48000, 1/90000, 90000) = 48000
|
||||
add_stable(100, 1/1000, 1/90000, 3000) = 133
|
||||
add_stable 10000x1024 at 1/48000: 10240000 (expected 10240000) OK
|
||||
Reference in New Issue
Block a user