Skip to content

Commit 9fdb3b3

Browse files
Adding trigonometric and hyperbolic trigonometric tests (#1699)
* math_tests: rewrote test_atan() Rewrote the atan test so that analagous checks are symmetrically performed for all possible inputs: int, float, and double. The total number of tests has increased while reducing the total amount of code. * math_tests: inverse trig and inverse hyperbolic trig Tests were written for acos, acosh, asin, asinh, and atanh. * math: cos macro missing values::promote_int The cosine macro can't take an integer input without this fix. You can see that some of the other macros, like the sine macro, have this. * math_tests: trig and exponential Wrote tests for the trigonometric macros as well as the exponential macro. The hyperbolic trig macros use the exponential macro rather than any LLVM instrinsics (for now at least, LLVM 19 has the proper compiler intrinsics). * math: float comparison In the math module two macros were defined to assist in comparing floating-point numbers for a given tolerance. The trig, hyperbolic trig, and exponential tests in the math_tests module were updated to use these macros instead of direct comparison.
1 parent 1362aa6 commit 9fdb3b3

File tree

2 files changed

+283
-25
lines changed

2 files changed

+283
-25
lines changed

lib/std/math/math.c3

+23-1
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,28 @@ macro deg_to_rad(x) {
131131
*>
132132
macro abs(x) => $$abs(x);
133133

134+
<*
135+
@require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
136+
@require values::@is_int(y) || values::@is_float(y) "Expected an integer or floating point value"
137+
*>
138+
macro is_approx(x, y, eps)
139+
{
140+
if (x == y) return true;
141+
if (is_nan(x) || is_nan(y)) return false;
142+
return abs(x-y) <= eps;
143+
}
144+
145+
<*
146+
@require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
147+
@require values::@is_int(y) || values::@is_float(y) "Expected an integer or floating point value"
148+
*>
149+
macro is_approx_rel(x, y, eps)
150+
{
151+
if (x == y) return true;
152+
if (is_nan(x) || is_nan(y)) return false;
153+
return abs(x-y) <= eps * max(abs(x), abs(y));
154+
}
155+
134156
<*
135157
@require values::@is_int(x) `The input must be an integer`
136158
*>
@@ -290,7 +312,7 @@ macro copysign(mag, sgn) => $$copysign(values::promote_int_same(mag, sgn), ($typ
290312
<*
291313
@require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
292314
*>
293-
macro cos(x) => $$cos(x);
315+
macro cos(x) => $$cos(values::promote_int(x));
294316

295317
<*
296318
@require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`

test/unit/stdlib/math/math.c3

+260-24
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,151 @@ fn void! test_abs() @test
1616
assert(math::abs(yy) == double[<3>] { 1, 0.5, 1000 });
1717
}
1818

19+
fn void! test_acos() @test
20+
{
21+
int [<5>] in = { 231, -231, 1, 0, -1 };
22+
double [<3>] out = { 0., math::PI_2, math::PI };
23+
assert(@typeis(math::acos(in[0]), double));
24+
assert(@typeis(math::acos((float)in[0]), float));
25+
assert(@typeis(math::acos((double)in[0]), double));
26+
for (int i = 0; i < 2; i++)
27+
{
28+
double x = math::acos(in[i]);
29+
assert(math::is_nan(x), "acos(%d)=%f is not nan", in[i], x);
30+
float f = math::acos((float)in[i]);
31+
assert(math::is_nan(f), "acos(%f)=%f is not nan", in[i], f);
32+
x = math::acos((double)in[i]);
33+
assert(math::is_nan(x), "acos(%f)=%f is not nan", in[i], x);
34+
}
35+
for (int i = 2; i < 5; i++)
36+
{
37+
int ii = i-2;
38+
double x = math::acos(in[i]);
39+
assert(math::is_approx_rel(x, out[ii], 1e-12), "acos(%d)=%f is not equal to %f", in[i], x, out[ii]);
40+
float f = math::acos((float)in[i]);
41+
assert(math::is_approx_rel(f, (float)out[ii], 1e-6), "acos(%f)=%f is not equal to %f", in[i], f, out[ii]);
42+
x = math::acos((double)in[i]);
43+
assert(math::is_approx_rel(x, out[ii], 1e-12), "acos(%f)=%f is not equal to %f", in[i], x, out[ii]);
44+
}
45+
}
46+
47+
48+
fn void! test_acosh() @test
49+
{
50+
int [<5>] in = { 0, -1, 1, 2, 231 };
51+
double [<3>] out = { 0., 1.3169578969248166, 6.135560205979194 };
52+
assert(@typeis(math::acosh(in[0]), double));
53+
assert(@typeis(math::acosh((float)in[0]), float));
54+
assert(@typeis(math::acosh((double)in[0]), double));
55+
for (int i = 0; i < 2; i++)
56+
{
57+
assert(math::is_nan(math::acosh(in[i])), "acosh(%d)=%f is not nan", in[i]);
58+
assert(math::is_nan(math::acosh((float)in[i])), "acosh(%f) is not nan", in[i]);
59+
assert(math::is_nan(math::acosh((double)in[i])), "acosh(%f) is not nan", in[i]);
60+
}
61+
for (int i = 2; i < 5; i++)
62+
{
63+
int ii = i-2;
64+
double x = math::acosh(in[i]);
65+
assert(math::is_approx_rel(x, out[ii], 1e-12), "acosh(%d)=%f is not equal to %f", in[i], x, out[ii]);
66+
float f = math::acosh((float)in[i]);
67+
assert(math::is_approx_rel(f, (float)out[ii], 1e-6), "acosh(%f)=%f is not equal to %f", in[i], f, out[ii]);
68+
x = math::acosh((double)in[i]);
69+
assert(math::is_approx_rel(x, out[ii], 1e-12), "acosh(%f)=%f is not equal to %f", in[i], x, out[ii]);
70+
}
71+
}
72+
73+
fn void! test_asin() @test
74+
{
75+
int [<5>] in = { 231, -231, 1, 0, -1 };
76+
double [<3>] out = { math::PI_2, 0., -math::PI_2 };
77+
assert(@typeis(math::asin(in[0]), double));
78+
assert(@typeis(math::asin((float)in[0]), float));
79+
assert(@typeis(math::asin((double)in[0]), double));
80+
for (int i = 0; i < 2; i++)
81+
{
82+
assert(math::is_nan(math::asin(in[i])), "asin(%d)=%f is not nan", in[i]);
83+
assert(math::is_nan(math::asin((float)in[i])), "asin(%f) is not nan", in[i]);
84+
assert(math::is_nan(math::asin((double)in[i])), "asin(%f) is not nan", in[i]);
85+
}
86+
for (int i = 2; i < 5; i++)
87+
{
88+
int ii = i-2;
89+
double x = math::asin(in[i]);
90+
assert(math::is_approx_rel(x, out[ii], 1e-12), "asin(%d)=%f is not equal to %f", in[i], x, out[ii]);
91+
float f = math::asin((float)in[i]);
92+
assert(math::is_approx_rel(f, (float)out[ii], 1e-6), "asin(%f)=%f is not equal to %f", in[i], f, out[ii]);
93+
x = math::asin((double)in[i]);
94+
assert(math::is_approx_rel(x, out[ii], 1e-12), "asin(%f)=%f is not equal to %f", in[i], x, out[ii]);
95+
}
96+
}
97+
98+
fn void! test_asinh() @test
99+
{
100+
int [<5>] in = { 231, 1, 0, -1, -231 };
101+
double [<5>] out = { 6.135569576118435, 0.881373587019543, 0., -0.881373587019543, -6.135569576118435 };
102+
assert(@typeis(math::asinh(in[0]), double));
103+
assert(@typeis(math::asinh((float)in[0]), float));
104+
assert(@typeis(math::asinh((double)in[0]), double));
105+
for (int i = 0; i < 5; i++)
106+
{
107+
double x = math::asinh(in[i]);
108+
assert(math::is_approx_rel(x, out[i], 1e-12), "asinh(%d)=%f is not equal to %f", in[i], x, out[i]);
109+
float f = math::asinh((float)in[i]);
110+
assert(math::is_approx_rel(f, (float)out[i], 1e-6), "asinh(%f)=%f is not equal to %f", in[i], f, out[i]);
111+
x = math::asinh((double)in[i]);
112+
assert(math::is_approx_rel(x, out[i], 1e-12), "asinh(%f)=%f is not equal to %f", in[i], x, out[i]);
113+
}
114+
}
115+
19116
fn void! test_atan() @test
20117
{
21-
int x = 231;
22-
assert(math::atan(x) == 1.5664673495078372);
23-
x = 1;
24-
assert(math::atan(x) == 0.7853981633974483);
25-
x = 0;
26-
assert(math::atan(x) == 0.0);
27-
x = -1;
28-
assert(math::atan(x) == -0.7853981633974483);
29-
x = -231;
30-
assert(math::atan(x) == -1.5664673495078372);
31-
float f = 0;
32-
assert(math::atan(f) == 0.0f);
33-
f = 1;
34-
assert(math::atan(f) == 0.7853981633974483f);
35-
f = -1;
36-
assert(math::atan(f) == -0.7853981633974483f);
37-
assert(@typeis(math::atan(f), float));
38-
double d = 0;
39-
assert(math::atan(d) == 0.0);
40-
d = 1;
41-
assert(math::atan(d) == 0.7853981633974483);
42-
d = -1;
43-
assert(math::atan(d) == -0.7853981633974483);
44-
assert(@typeis(math::atan(d), double));
118+
int [<5>] in = { 231, 1, 0, -1, -231 };
119+
double [<5>] out = { 1.5664673495078372, math::PI_4, 0., -math::PI_4, -1.5664673495078372 };
120+
assert(@typeis(math::atan(in[0]), double));
121+
assert(@typeis(math::atan((float)in[0]), float));
122+
assert(@typeis(math::atan((double)in[0]), double));
123+
for (int i = 0; i < 5; i++)
124+
{
125+
double x = math::atan(in[i]);
126+
assert(math::is_approx_rel(x, out[i], 1e-12), "atan(%d)=%f is not equal to %f", in[i], x, out[i]);
127+
float f = math::atan((float)in[i]);
128+
assert(math::is_approx_rel(f, (float)out[i], 1e-6), "atan(%f)=%f is not equal to %f", in[i], f, out[i]);
129+
x = math::atan((double)in[i]);
130+
assert(math::is_approx_rel(x, out[i], 1e-12), "atan(%f)=%f is not equal to %f", in[i], x, out[i]);
131+
}
132+
}
133+
134+
fn void! test_atanh() @test
135+
{
136+
int [<4>] in = { 231, -231, 1, -1 };
137+
double [<2>] in2 = { 0.5, -0.5 };
138+
double [<2>] out = { 0.5493061443340548, -0.5493061443340548 };
139+
assert(@typeis(math::atanh(in[0]), double));
140+
assert(@typeis(math::atanh((float)in[0]), float));
141+
assert(@typeis(math::atanh((double)in[0]), double));
142+
for (int i = 0; i < 2; i++)
143+
{
144+
assert(math::is_nan(math::atanh(in[i])), "atanh(%d)=%f is not nan", in[i]);
145+
assert(math::is_nan(math::atanh((float)in[i])), "atanh(%f) is not nan", in[i]);
146+
assert(math::is_nan(math::atanh((double)in[i])), "atanh(%f) is not nan", in[i]);
147+
}
148+
for (int i = 2; i < 4; i++)
149+
{
150+
assert(math::is_inf(math::atanh(in[i])), "atanh(%d)=%f is not inf", in[i]);
151+
assert(math::is_inf(math::atanh((float)in[i])), "atanh(%f) is not inf", in[i]);
152+
assert(math::is_inf(math::atanh((double)in[i])), "atanh(%f) is not inf", in[i]);
153+
}
154+
assert(math::atanh(0) == 0., "atanh(%d) is not equal to %f", 0, 0.);
155+
assert(math::atanh(0.f) == 0.f, "atanh(%f) is not equal to %f", 0.f, 0.f);
156+
assert(math::atanh(0.) == 0., "atanh(%f) is not equal to %f", 0., 0.);
157+
for (int i = 0; i < 2; i++)
158+
{
159+
float f = math::atanh((float)in2[i]);
160+
assert(math::is_approx(f, (float)out[i], 1e-6), "atanh(%f)=%f is not equal to %f", in2[i], f, out[i]);
161+
double x = math::atanh((double)in2[i]);
162+
assert(math::is_approx(x, out[i], 1e-12), "atanh(%f)=%f is not equal to %f", in2[i], x, out[i]);
163+
}
45164
}
46165

47166
fn void! test_ceil() @test
@@ -68,6 +187,64 @@ fn void! test_ceil() @test
68187
assert(math::ceil(vec) == double[<5>] { -123, 124, 1, 0, 0 });
69188
}
70189

190+
fn void! test_cos() @test
191+
{
192+
int [<5>] in = { 231, 1, 0, -1, -231 };
193+
double [<5>] out = { 0.09280621889587707, 0.54030230586813972 , 1., 0.54030230586813972, 0.09280621889587707 };
194+
float [<2>] in2 = { math::PI, 0.f };
195+
float [<2>] out2 = { -1.f, 1.f };
196+
double [<2>] in3 = { math::PI, 0. };
197+
double [<2>] out3 = { -1., 1. };
198+
assert(@typeis(math::cos(in[0]), double));
199+
assert(@typeis(math::cos((float)in[0]), float));
200+
assert(@typeis(math::cos((double)in[0]), double));
201+
for (int i = 0; i < 5; i++)
202+
{
203+
double x = math::cos(in[i]);
204+
assert(math::is_approx_rel(x, out[i], 1e-12), "cos(%d)=%f is not equal to %f", in[i], x, out[i]);
205+
float f = math::cos((float)in[i]);
206+
assert(math::is_approx_rel(f, (float)out[i], 1e-6), "cos(%f)=%f is not equal to %f", in[i], f, out[i]);
207+
x = math::cos((double)in[i]);
208+
assert(math::is_approx_rel(x, out[i], 1e-12), "cos(%f)=%f is not equal to %f", in[i], x, out[i]);
209+
}
210+
float [<2>] vecf = math::cos(in2);
211+
double [<2>] vec = math::cos(in3);
212+
for (int i = 0; i < 2; i++)
213+
{
214+
assert(math::is_approx_rel(vecf[i], out2[i], 1e-6), "cos(%f)=%f is not equal to %f", in2[i], vecf[i], out2[i]);
215+
assert(math::is_approx_rel(vec[i], out3[i], 1e-12), "cos(%f)=%f is not equal to %f", in3[i], vec[i], out3[i]);
216+
}
217+
}
218+
219+
fn void! test_exp() @test
220+
{
221+
int [<5>] in = { 2, 1, 0, -1, -2 };
222+
double [<5>] out = { 7.38905609893065, math::E , 1., 0.36787944117144233, 0.1353352832366127 };
223+
float [<2>] in2 = { 1.f, 0.f };
224+
float [<2>] out2 = { math::E, 1.f };
225+
double [<2>] in3 = { 1., 0. };
226+
double [<2>] out3 = { math::E, 1. };
227+
assert(@typeis(math::exp(in[0]), double));
228+
assert(@typeis(math::exp((float)in[0]), float));
229+
assert(@typeis(math::exp((double)in[0]), double));
230+
for (int i = 0; i < 5; i++)
231+
{
232+
double x = math::exp(in[i]);
233+
assert(math::is_approx_rel(x, out[i], 1e-12), "exp(%d)=%f is not equal to %f", in[i], x, out[i]);
234+
float f = math::exp((float)in[i]);
235+
assert(math::is_approx_rel(f, (float)out[i], 1e-6), "exp(%f)=%f is not equal to %f", in[i], f, out[i]);
236+
x = math::exp((double)in[i]);
237+
assert(math::is_approx_rel(x, out[i], 1e-12), "exp(%f)=%f is not equal to %f", in[i], x, out[i]);
238+
}
239+
float [<2>] vecf = math::exp(in2);
240+
double [<2>] vec = math::exp(in3);
241+
for (int i = 0; i < 2; i++)
242+
{
243+
assert(math::is_approx_rel(vecf[i], out2[i], 1e-6), "exp(%f)=%f is not equal to %f", in2[i], vecf[i], out2[i]);
244+
assert(math::is_approx_rel(vec[i], out3[i], 1e-12), "exp(%f)=%f is not equal to %f", in3[i], vec[i], out3[i]);
245+
}
246+
}
247+
71248
fn void! test_floor() @test
72249
{
73250
double d = -123.1;
@@ -122,6 +299,65 @@ fn void! test_sign() @test
122299
assert(@typeis(math::sign(y), uint));
123300
}
124301

302+
303+
fn void! test_sin() @test
304+
{
305+
int [<5>] in = { 231, 1, 0, -1, -231 };
306+
double [<5>] out = { -0.99568418975810324, 0.84147098480789651 , 0., -0.84147098480789651, 0.99568418975810324 };
307+
float [<2>] in2 = { math::PI_2, -math::PI_2 };
308+
float [<2>] out2 = { 1.f, -1.f };
309+
double [<2>] in3 = { math::PI_2, -math::PI_2 };
310+
double [<2>] out3 = { 1., -1. };
311+
assert(@typeis(math::sin(in[0]), double));
312+
assert(@typeis(math::sin((float)in[0]), float));
313+
assert(@typeis(math::sin((double)in[0]), double));
314+
for (int i = 0; i < 5; i++)
315+
{
316+
double x = math::sin(in[i]);
317+
assert(math::is_approx_rel(x, out[i], 1e-12), "sin(%d)=%f is not equal to %f", in[i], x, out[i]);
318+
float f = math::sin((float)in[i]);
319+
assert(math::is_approx_rel(f, (float)out[i], 1e-6), "sin(%f)=%f is not equal to %f", in[i], f, out[i]);
320+
x = math::sin((double)in[i]);
321+
assert(math::is_approx_rel(x, out[i], 1e-12), "sin(%f)=%f is not equal to %f", in[i], x, out[i]);
322+
}
323+
float [<2>] vecf = math::sin(in2);
324+
double [<2>] vec = math::sin(in3);
325+
for (int i = 0; i < 2; i++)
326+
{
327+
assert(math::is_approx_rel(vecf[i], out2[i], 1e-6), "sin(%f)=%f is not equal to %f", in2[i], vecf[i], out2[i]);
328+
assert(math::is_approx_rel(vec[i], out3[i], 1e-12), "sin(%f)=%f is not equal to %f", in3[i], vec[i], out3[i]);
329+
}
330+
}
331+
332+
fn void! test_tan() @test
333+
{
334+
int [<5>] in = { 231, 1, 0, -1, -231 };
335+
double [<5>] out = { -10.7286365246191129, 1.5574077246549022 , 0., -1.5574077246549022, 10.7286365246191129 };
336+
float [<2>] in2 = { math::PI_4, -math::PI_4 };
337+
float [<2>] out2 = { 1.f, -1.f };
338+
double [<2>] in3 = { math::PI_4, -math::PI_4 };
339+
double [<2>] out3 = { 1., -1. };
340+
assert(@typeis(math::tan(in[0]), double));
341+
assert(@typeis(math::tan((float)in[0]), float));
342+
assert(@typeis(math::tan((double)in[0]), double));
343+
for (int i = 0; i < 5; i++)
344+
{
345+
double x = math::tan(in[i]);
346+
assert(math::is_approx_rel(x, out[i], 1e-12), "tan(%d)=%f is not equal to %f", in[i], x, out[i]);
347+
float f = math::tan((float)in[i]);
348+
assert(math::is_approx_rel(f, (float)out[i], 1e-6), "tan(%f)=%f is not equal to %f", in[i], f, out[i]);
349+
x = math::tan((double)in[i]);
350+
assert(math::is_approx_rel(x, out[i], 1e-12), "tan(%f)=%f is not equal to %f", in[i], x, out[i]);
351+
}
352+
float [<2>] vecf = math::tan(in2);
353+
double [<2>] vec = math::tan(in3);
354+
for (int i = 0; i < 2; i++)
355+
{
356+
assert(math::is_approx_rel(vecf[i], out2[i], 1e-6), "tan(%f)=%f is not equal to %f", in2[i], vecf[i], out2[i]);
357+
assert(math::is_approx_rel(vec[i], out3[i], 1e-12), "tan(%f)=%f is not equal to %f", in3[i], vec[i], out3[i]);
358+
}
359+
}
360+
125361
fn void! test_trunc() @test
126362
{
127363
double d = -123.9;

0 commit comments

Comments
 (0)