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 
21 static 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 
67 ATF_TC(fe_round);
68 ATF_TC_HEAD(fe_round, tc)
69 {
70 	atf_tc_set_md_var(tc, "descr","Checking IEEE 754 rounding modes using lrint");
71 }
72 
73 ATF_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 
96 ATF_TC(fe_nearbyint);
97 ATF_TC_HEAD(fe_nearbyint, tc)
98 {
99 	atf_tc_set_md_var(tc, "descr","Checking IEEE 754 rounding modes using nearbyint");
100 }
101 
102 ATF_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 
125 static 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 
134 ATF_TC(fe_nextafter);
135 ATF_TC_HEAD(fe_nextafter, tc)
136 {
137 	atf_tc_set_md_var(tc, "descr", "Checking IEEE 754 rounding using nextafter()");
138 }
139 
140 ATF_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 
160 ATF_TC(fe_nexttoward);
161 ATF_TC_HEAD(fe_nexttoward, tc)
162 {
163 	atf_tc_set_md_var(tc, "descr", "Checking IEEE 754 rounding using nexttoward()");
164 }
165 
166 ATF_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 
186 ATF_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
197 ATF_TC(t_nofe_round);
198 
199 ATF_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 
205 ATF_TC_BODY(t_nofe_round, tc)
206 {
207 	atf_tc_skip("no fenv.h support on this architecture");
208 }
209 
210 ATF_TC(t_nofe_nearbyint);
211 
212 ATF_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 
218 ATF_TC_BODY(t_nofe_nearbyint, tc)
219 {
220 	atf_tc_skip("no fenv.h support on this architecture");
221 }
222 
223 ATF_TC(t_nofe_nextafter);
224 
225 ATF_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 
231 ATF_TC_BODY(t_nofe_nextafter, tc)
232 {
233 	atf_tc_skip("no fenv.h support on this architecture");
234 }
235 
236 ATF_TC(t_nofe_nexttoward);
237 
238 ATF_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 
244 ATF_TC_BODY(t_nofe_nexttoward, tc)
245 {
246 	atf_tc_skip("no fenv.h support on this architecture");
247 }
248 
249 ATF_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