1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
'''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()
|