xref: /freebsd/contrib/netbsd-tests/lib/libm/t_fe_round.c (revision 6c18c40b29fd5ac66230af34726260ce2d47aecd)
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);
ATF_TC_HEAD(fe_round,tc)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 
ATF_TC_BODY(fe_round,tc)73 ATF_TC_BODY(fe_round, tc)
74 {
75 #if defined(__riscv)
76 	atf_tc_expect_fail("https://bugs.freebsd.org/290099");
77 #endif
78 	long int received;
79 
80 	for (unsigned int i = 0; i < __arraycount(values); i++) {
81 		fesetround(values[i].round_mode);
82 
83 		received = lrint(values[i].input);
84 		ATF_REQUIRE_MSG(
85 		    (labs(received - values[i].expected) < EPSILON),
86 		    "lrint rounding wrong, difference too large. "
87 		    "input: %f (index %d): got %ld, expected %ld",
88 		    values[i].input, i, received, values[i].expected);
89 
90 		/* Do we get the same rounding mode out? */
91 		ATF_REQUIRE_MSG(
92 		    (fegetround() == values[i].round_mode),
93 		    "Didn't get the same rounding mode out!. "
94 		    "(index %d) fed in %d rounding mode, got %d out",
95 		    i, values[i].round_mode, fegetround());
96 	}
97 }
98 
99 ATF_TC(fe_nearbyint);
ATF_TC_HEAD(fe_nearbyint,tc)100 ATF_TC_HEAD(fe_nearbyint, tc)
101 {
102 	atf_tc_set_md_var(tc, "descr","Checking IEEE 754 rounding modes using nearbyint");
103 }
104 
ATF_TC_BODY(fe_nearbyint,tc)105 ATF_TC_BODY(fe_nearbyint, tc)
106 {
107 	double received;
108 
109 	for (unsigned int i = 0; i < __arraycount(values); i++) {
110 		fesetround(values[i].round_mode);
111 
112 		received = nearbyint(values[i].input);
113 		ATF_CHECK_MSG(
114 		    (fabs(received - values[i].expected) < EPSILON),
115 		    "nearbyint rounding wrong, difference too large. "
116 		    "input: %f (index %d): got %f, expected %ld",
117 		    values[i].input, i, received, values[i].expected);
118 
119 		/* Do we get the same rounding mode out? */
120 		ATF_CHECK_MSG(
121 		    (fegetround() == values[i].round_mode),
122 		    "Didn't get the same rounding mode out! "
123 		    "(index %d) fed in %d rounding mode, got %d out",
124 		    i, values[i].round_mode, fegetround());
125 	}
126 }
127 
128 static const struct {
129 	double input;
130 	double toward;
131 	double expected;
132 } values2[] = {
133 	{ 10.0, 11.0, 10.0 },
134 	{ -5.0, -6.0, -5.0 },
135 };
136 
137 ATF_TC(fe_nextafter);
ATF_TC_HEAD(fe_nextafter,tc)138 ATF_TC_HEAD(fe_nextafter, tc)
139 {
140 	atf_tc_set_md_var(tc, "descr", "Checking IEEE 754 rounding using nextafter()");
141 }
142 
ATF_TC_BODY(fe_nextafter,tc)143 ATF_TC_BODY(fe_nextafter, tc)
144 {
145 	double received;
146 	int res;
147 
148 	for (unsigned int i = 0; i < __arraycount(values2); i++) {
149 		received = nextafter(values2[i].input, values2[i].toward);
150 		if (values2[i].input < values2[i].toward) {
151 			res = (received > values2[i].input);
152 		} else {
153 			res = (received < values2[i].input);
154 		}
155 		ATF_CHECK_MSG(
156 			res && (fabs(received - values2[i].expected) < EPSILON),
157 			"nextafter() rounding wrong, difference too large. "
158 			"input: %f (index %d): got %f, expected %f, res %d",
159 			values2[i].input, i, received, values2[i].expected, res);
160 	}
161 }
162 
163 ATF_TC(fe_nexttoward);
ATF_TC_HEAD(fe_nexttoward,tc)164 ATF_TC_HEAD(fe_nexttoward, tc)
165 {
166 	atf_tc_set_md_var(tc, "descr", "Checking IEEE 754 rounding using nexttoward()");
167 }
168 
ATF_TC_BODY(fe_nexttoward,tc)169 ATF_TC_BODY(fe_nexttoward, tc)
170 {
171 	double received;
172 	int res;
173 
174 	for (unsigned int i = 0; i < __arraycount(values2); i++) {
175 		received = nexttoward(values2[i].input, values2[i].toward);
176 		if (values2[i].input < values2[i].toward) {
177 			res = (received > values2[i].input);
178 		} else {
179 			res = (received < values2[i].input);
180 		}
181 		ATF_CHECK_MSG(
182 			res && (fabs(received - values2[i].expected) < EPSILON),
183 			"nexttoward() rounding wrong, difference too large. "
184 			"input: %f (index %d): got %f, expected %f, res %d",
185 			values2[i].input, i, received, values2[i].expected, res);
186 	}
187 }
188 
ATF_TP_ADD_TCS(tp)189 ATF_TP_ADD_TCS(tp)
190 {
191 
192 	ATF_TP_ADD_TC(tp, fe_round);
193 	ATF_TP_ADD_TC(tp, fe_nearbyint);
194 	ATF_TP_ADD_TC(tp, fe_nextafter);
195 	ATF_TP_ADD_TC(tp, fe_nexttoward);
196 
197 	return atf_no_error();
198 }
199 #else
200 ATF_TC(t_nofe_round);
201 
ATF_TC_HEAD(t_nofe_round,tc)202 ATF_TC_HEAD(t_nofe_round, tc)
203 {
204 	atf_tc_set_md_var(tc, "descr",
205 	    "dummy test case - no fenv.h support");
206 }
207 
ATF_TC_BODY(t_nofe_round,tc)208 ATF_TC_BODY(t_nofe_round, tc)
209 {
210 	atf_tc_skip("no fenv.h support on this architecture");
211 }
212 
213 ATF_TC(t_nofe_nearbyint);
214 
ATF_TC_HEAD(t_nofe_nearbyint,tc)215 ATF_TC_HEAD(t_nofe_nearbyint, tc)
216 {
217 	atf_tc_set_md_var(tc, "descr",
218 	    "dummy test case - no fenv.h support");
219 }
220 
ATF_TC_BODY(t_nofe_nearbyint,tc)221 ATF_TC_BODY(t_nofe_nearbyint, tc)
222 {
223 	atf_tc_skip("no fenv.h support on this architecture");
224 }
225 
226 ATF_TC(t_nofe_nextafter);
227 
ATF_TC_HEAD(t_nofe_nextafter,tc)228 ATF_TC_HEAD(t_nofe_nextafter, tc)
229 {
230 	atf_tc_set_md_var(tc, "descr",
231 	    "dummy test case - no fenv.h support");
232 }
233 
ATF_TC_BODY(t_nofe_nextafter,tc)234 ATF_TC_BODY(t_nofe_nextafter, tc)
235 {
236 	atf_tc_skip("no fenv.h support on this architecture");
237 }
238 
239 ATF_TC(t_nofe_nexttoward);
240 
ATF_TC_HEAD(t_nofe_nexttoward,tc)241 ATF_TC_HEAD(t_nofe_nexttoward, tc)
242 {
243 	atf_tc_set_md_var(tc, "descr",
244 	    "dummy test case - no fenv.h support");
245 }
246 
ATF_TC_BODY(t_nofe_nexttoward,tc)247 ATF_TC_BODY(t_nofe_nexttoward, tc)
248 {
249 	atf_tc_skip("no fenv.h support on this architecture");
250 }
251 
ATF_TP_ADD_TCS(tp)252 ATF_TP_ADD_TCS(tp)
253 {
254 	ATF_TP_ADD_TC(tp, t_nofe_round);
255 	ATF_TP_ADD_TC(tp, t_nofe_nearbyint);
256 	ATF_TP_ADD_TC(tp, t_nofe_nextafter);
257 	ATF_TP_ADD_TC(tp, t_nofe_nexttoward);
258 	return atf_no_error();
259 }
260 
261 #endif
262