1/*
2 * Written by Maya Rashish <maya@NetBSD.org>
3 * Public domain.
4 *
5 * Testing IEEE-754 rounding modes (and lrint)
6 */
7
8#include <atf-c.h>
9#include <fenv.h>
10#ifdef __HAVE_FENV
11#include <math.h>
12#include <stdio.h>
13#include <stdlib.h>
14
15/*#pragma STDC FENV_ACCESS ON gcc?? */
16
17#define INT 9223L
18
19#define EPSILON 0.001
20
21static const struct {
22	int round_mode;
23	double input;
24	long int expected;
25} values[] = {
26	{ FE_DOWNWARD,		3.7,		3},
27	{ FE_DOWNWARD,		-3.7,		-4},
28	{ FE_DOWNWARD,		+0,		0},
29	{ FE_DOWNWARD,		-INT-0.01,	-INT-1},
30	{ FE_DOWNWARD,		+INT-0.01,	INT-1},
31	{ FE_DOWNWARD,		-INT+0.01,	-INT},
32	{ FE_DOWNWARD,		+INT+0.01,	INT},
33#if 0 /* cpu bugs? */
34	{ FE_DOWNWARD,		-0,		-1},
35
36	{ FE_UPWARD,		+0,		1},
37#endif
38	{ FE_UPWARD,		-0,		0},
39	{ FE_UPWARD,		-123.7,		-123},
40	{ FE_UPWARD,		123.999,	124},
41	{ FE_UPWARD,		-INT-0.01,	-INT},
42	{ FE_UPWARD,		+INT-0.01,	INT},
43	{ FE_UPWARD,		-INT+0.01,	-INT+1},
44	{ FE_UPWARD,		+INT+0.01,	INT+1},
45
46	{ FE_TOWARDZERO,	1.99,		1},
47	{ FE_TOWARDZERO,	-1.99,		-1},
48	{ FE_TOWARDZERO,	0.2,		0},
49	{ FE_TOWARDZERO,	INT+0.01,	INT},
50	{ FE_TOWARDZERO,	INT-0.01,	INT - 1},
51	{ FE_TOWARDZERO,	-INT+0.01,	-INT + 1},
52	{ FE_TOWARDZERO,	+0,		0},
53	{ FE_TOWARDZERO,	-0,		0},
54
55	{ FE_TONEAREST,		-INT-0.01,	-INT},
56	{ FE_TONEAREST,		+INT-0.01,	INT},
57	{ FE_TONEAREST,		-INT+0.01,	-INT},
58	{ FE_TONEAREST,		+INT+0.01,	INT},
59	{ FE_TONEAREST,		-INT-0.501,	-INT-1},
60	{ FE_TONEAREST,		+INT-0.501,	INT-1},
61	{ FE_TONEAREST,		-INT+0.501,	-INT+1},
62	{ FE_TONEAREST,		+INT+0.501,	INT+1},
63	{ FE_TONEAREST,		+0,		0},
64	{ FE_TONEAREST,		-0,		0},
65};
66
67ATF_TC(fe_round);
68ATF_TC_HEAD(fe_round, tc)
69{
70	atf_tc_set_md_var(tc, "descr","Checking IEEE 754 rounding modes using lrint");
71}
72
73ATF_TC_BODY(fe_round, tc)
74{
75	long int received;
76
77	for (unsigned int i = 0; i < __arraycount(values); i++) {
78		fesetround(values[i].round_mode);
79
80		received = lrint(values[i].input);
81		ATF_CHECK_MSG(
82		    (labs(received - values[i].expected) < EPSILON),
83		    "lrint rounding wrong, difference too large\n"
84		    "input: %f (index %d): got %ld, expected %ld\n",
85		    values[i].input, i, received, values[i].expected);
86
87		/* Do we get the same rounding mode out? */
88		ATF_CHECK_MSG(
89		    (fegetround() == values[i].round_mode),
90		    "Didn't get the same rounding mode out!\n"
91		    "(index %d) fed in %d rounding mode, got %d out\n",
92		    i, values[i].round_mode, fegetround());
93	}
94}
95
96ATF_TC(fe_nearbyint);
97ATF_TC_HEAD(fe_nearbyint, tc)
98{
99	atf_tc_set_md_var(tc, "descr","Checking IEEE 754 rounding modes using nearbyint");
100}
101
102ATF_TC_BODY(fe_nearbyint, tc)
103{
104	double received;
105
106	for (unsigned int i = 0; i < __arraycount(values); i++) {
107		fesetround(values[i].round_mode);
108
109		received = nearbyint(values[i].input);
110		ATF_CHECK_MSG(
111		    (fabs(received - values[i].expected) < EPSILON),
112		    "nearbyint rounding wrong, difference too large\n"
113		    "input: %f (index %d): got %f, expected %ld\n",
114		    values[i].input, i, received, values[i].expected);
115
116		/* Do we get the same rounding mode out? */
117		ATF_CHECK_MSG(
118		    (fegetround() == values[i].round_mode),
119		    "Didn't get the same rounding mode out!\n"
120		    "(index %d) fed in %d rounding mode, got %d out\n",
121		    i, values[i].round_mode, fegetround());
122	}
123}
124
125static const struct {
126	double input;
127	double toward;
128	double expected;
129} values2[] = {
130	{ 10.0, 11.0, 10.0 },
131	{ -5.0, -6.0, -5.0 },
132};
133
134ATF_TC(fe_nextafter);
135ATF_TC_HEAD(fe_nextafter, tc)
136{
137	atf_tc_set_md_var(tc, "descr", "Checking IEEE 754 rounding using nextafter()");
138}
139
140ATF_TC_BODY(fe_nextafter, tc)
141{
142	double received;
143	int res;
144
145	for (unsigned int i = 0; i < __arraycount(values2); i++) {
146		received = nextafter(values2[i].input, values2[i].toward);
147		if (values2[i].input < values2[i].toward) {
148			res = (received > values2[i].input);
149		} else {
150			res = (received < values2[i].input);
151		}
152		ATF_CHECK_MSG(
153			res && (fabs(received - values2[i].expected) < EPSILON),
154			"nextafter() rounding wrong, difference too large\n"
155			"input: %f (index %d): got %f, expected %f, res %d\n",
156			values2[i].input, i, received, values2[i].expected, res);
157	}
158}
159
160ATF_TC(fe_nexttoward);
161ATF_TC_HEAD(fe_nexttoward, tc)
162{
163	atf_tc_set_md_var(tc, "descr", "Checking IEEE 754 rounding using nexttoward()");
164}
165
166ATF_TC_BODY(fe_nexttoward, tc)
167{
168	double received;
169	int res;
170
171	for (unsigned int i = 0; i < __arraycount(values2); i++) {
172		received = nexttoward(values2[i].input, values2[i].toward);
173		if (values2[i].input < values2[i].toward) {
174			res = (received > values2[i].input);
175		} else {
176			res = (received < values2[i].input);
177		}
178		ATF_CHECK_MSG(
179			res && (fabs(received - values2[i].expected) < EPSILON),
180			"nexttoward() rounding wrong, difference too large\n"
181			"input: %f (index %d): got %f, expected %f, res %d\n",
182			values2[i].input, i, received, values2[i].expected, res);
183	}
184}
185
186ATF_TP_ADD_TCS(tp)
187{
188
189	ATF_TP_ADD_TC(tp, fe_round);
190	ATF_TP_ADD_TC(tp, fe_nearbyint);
191	ATF_TP_ADD_TC(tp, fe_nextafter);
192	ATF_TP_ADD_TC(tp, fe_nexttoward);
193
194	return atf_no_error();
195}
196#else
197ATF_TC(t_nofe_round);
198
199ATF_TC_HEAD(t_nofe_round, tc)
200{
201	atf_tc_set_md_var(tc, "descr",
202	    "dummy test case - no fenv.h support");
203}
204
205ATF_TC_BODY(t_nofe_round, tc)
206{
207	atf_tc_skip("no fenv.h support on this architecture");
208}
209
210ATF_TC(t_nofe_nearbyint);
211
212ATF_TC_HEAD(t_nofe_nearbyint, tc)
213{
214	atf_tc_set_md_var(tc, "descr",
215	    "dummy test case - no fenv.h support");
216}
217
218ATF_TC_BODY(t_nofe_nearbyint, tc)
219{
220	atf_tc_skip("no fenv.h support on this architecture");
221}
222
223ATF_TC(t_nofe_nextafter);
224
225ATF_TC_HEAD(t_nofe_nextafter, tc)
226{
227	atf_tc_set_md_var(tc, "descr",
228	    "dummy test case - no fenv.h support");
229}
230
231ATF_TC_BODY(t_nofe_nextafter, tc)
232{
233	atf_tc_skip("no fenv.h support on this architecture");
234}
235
236ATF_TC(t_nofe_nexttoward);
237
238ATF_TC_HEAD(t_nofe_nexttoward, tc)
239{
240	atf_tc_set_md_var(tc, "descr",
241	    "dummy test case - no fenv.h support");
242}
243
244ATF_TC_BODY(t_nofe_nexttoward, tc)
245{
246	atf_tc_skip("no fenv.h support on this architecture");
247}
248
249ATF_TP_ADD_TCS(tp)
250{
251	ATF_TP_ADD_TC(tp, t_nofe_round);
252	ATF_TP_ADD_TC(tp, t_nofe_nearbyint);
253	ATF_TP_ADD_TC(tp, t_nofe_nextafter);
254	ATF_TP_ADD_TC(tp, t_nofe_nexttoward);
255	return atf_no_error();
256}
257
258#endif
259