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