libfixmath/benchmarks/generate_testcases.py

141 lines
3.4 KiB
Python

'''This script precalculates the correct solutions for a set of test numbers,
and writes them to testcases.c. This is aimed for running the tests on-target,
therefore it doesn't test all the cases or use floating point math, but
instead generates a ~10k binary.
The tests are chosen randomly, so there is quite good chance to eventually
catch most errors. Because the list is not regenerated automatically, the
functioning of the benchmark application is still deterministic and easy
to debug.
'''
import math
import random
import struct
# Fix16 scaling factor
scale = 65536.
# Fix16 overflow indicator
overflow = -2**31
def f16_to_float(val):
return val / scale
def float_to_f16(val):
val = int(round(val * scale))
if val >= 2**31 or val < -2**31:
val = overflow
return val
def to_ui32(val):
return struct.unpack('I', struct.pack('i', val))[0]
testcases = [
# Small numbers
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
-1, -2, -3, -4, -5, -6, -7, -8, -9, -10,
# Integer numbers
0x10000, -0x10000, 0x20000, -0x20000, 0x30000, -0x30000,
0x40000, -0x40000, 0x50000, -0x50000, 0x60000, -0x60000,
# Fractions (1/2, 1/4, 1/8)
0x8000, -0x8000, 0x4000, -0x4000, 0x2000, -0x2000,
# Problematic carry
0xFFFF, -0xFFFF, 0x1FFFF, -0x1FFFF, 0x3FFFF, -0x3FFFF,
# Smallest and largest values
0x7FFFFFFF, -0x80000000
]
for i in range(10):
# Large random numbers
testcases.append(random.randint(-0x80000000, 0x7FFFFFFF))
# Small random numbers
testcases.append(random.randint(-100000, 100000))
# Tiny random numbers
testcases.append(random.randint(-200, 200))
out = open("testcases.c", "w")
out.write('''
/* Automatically generated testcases for fix16 operations
* See generate_testcases.py for the generator.
*/
#include <fix16.h>
typedef struct {
// Input
fix16_t a;
// Correct output
fix16_t sqrt;
fix16_t exp;
} fix16_1op_testcase;
typedef struct {
// Inputs
fix16_t a;
fix16_t b;
// Correct output
fix16_t add;
fix16_t sub;
fix16_t mul;
fix16_t div;
} fix16_2op_testcase;
#define TESTCASES1_COUNT (sizeof(testcases1)/sizeof(testcases1[0]))
#define TESTCASES2_COUNT (sizeof(testcases2)/sizeof(testcases2[0]))
''')
# Write testcases for 1-operand functions
out.write('static const fix16_1op_testcase testcases1[] = {\n')
for i in range(10):
a = random.choice(testcases)
if a >= 0:
sqrt = float_to_f16(math.sqrt(f16_to_float(a)))
else:
sqrt = 0
try:
exp = float_to_f16(math.exp(f16_to_float(a)))
except OverflowError:
exp = 0x7FFFFFFF
out.write(' {0x%08x, 0x%08x, 0x%08x}, // %d\n'
% (to_ui32(a), to_ui32(sqrt), to_ui32(exp), i))
out.write('};\n\n')
# Write testcases for 2-operand functions
out.write('static const fix16_2op_testcase testcases2[] = {\n')
for i in range(50):
a = random.choice(testcases)
b = random.choice(testcases)
add = float_to_f16(f16_to_float(a) + f16_to_float(b))
sub = float_to_f16(f16_to_float(a) - f16_to_float(b))
mul = float_to_f16(f16_to_float(a) * f16_to_float(b))
if b != 0:
div = float_to_f16(f16_to_float(a) / f16_to_float(b))
else:
div = 0
out.write(' {0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x}, // %d\n'
% (to_ui32(a), to_ui32(b), to_ui32(add), to_ui32(sub), to_ui32(mul), to_ui32(div), i))
out.write('};\n\n')
out.close()