1/* $NetBSD: t_scalbn.c,v 1.18 2024/06/09 16:53:12 riastradh Exp $ */
2
3/*-
4 * Copyright (c) 2011 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jukka Ruohonen.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31#include <sys/cdefs.h>
32__RCSID("$NetBSD: t_scalbn.c,v 1.18 2024/06/09 16:53:12 riastradh Exp $");
33
34#include <math.h>
35#include <limits.h>
36#include <float.h>
37#include <errno.h>
38#include <fenv.h>
39
40#include <atf-c.h>
41
42static const int exps[] = { 0, 1, -1, 100, -100 };
43
44/* tests here do not require specific precision, so we just use double */
45struct testcase {
46	int exp;
47	double inval;
48	double result;
49	int error;
50	int except;
51};
52struct testcase test_vals[] = {
53	{ 0,		1.00085,	1.00085,	0, 0 },
54	{ 0,		0.99755,	0.99755,	0, 0 },
55	{ 0,		-1.00085,	-1.00085,	0, 0 },
56	{ 0,		-0.99755,	-0.99755,	0, 0 },
57	{ 1,		1.00085,	2.0* 1.00085,	0, 0 },
58	{ 1,		0.99755,	2.0* 0.99755,	0, 0 },
59	{ 1,		-1.00085,	2.0* -1.00085,	0, 0 },
60	{ 1,		-0.99755,	2.0* -0.99755,	0, 0 },
61
62	/*
63	 * We could add more corner test cases here, but we would have to
64	 * add some ifdefs for the exact format and use a reliable
65	 * generator program - bail for now and only do trivial stuff above.
66	 */
67};
68
69/*
70 * scalbn(3)
71 */
72ATF_TC(scalbn_val);
73ATF_TC_HEAD(scalbn_val, tc)
74{
75	atf_tc_set_md_var(tc, "descr", "Test scalbn() for a few values");
76}
77
78ATF_TC_BODY(scalbn_val, tc)
79{
80	const struct testcase *tests = test_vals;
81	const size_t tcnt = __arraycount(test_vals);
82	size_t i;
83	double rv;
84
85	for (i = 0; i < tcnt; i++) {
86		errno = 0;
87#ifndef __vax__
88		feclearexcept(FE_ALL_EXCEPT);
89#endif
90		rv = scalbn(tests[i].inval, tests[i].exp);
91		ATF_CHECK_EQ_MSG(errno, tests[i].error,
92		    "test %zu: errno %d instead of %d", i, errno,
93		    tests[i].error);
94#ifndef __vax__
95		ATF_CHECK_EQ_MSG(errno, tests[i].error,
96		    "test %zu: fetestexcept %d instead of %d", i,
97		    fetestexcept(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW),
98		    tests[i].except);
99#endif
100		/* scalbn is always exact except for underflow or overflow.  */
101		ATF_CHECK_MSG(rv == tests[i].result,
102		    "test %zu: return value %.17g instead of %.17g"
103		    " (error %.17g)",
104		    i, rv, tests[i].result,
105		    fabs((tests[i].result - rv)/tests[i].result));
106	}
107}
108
109ATF_TC(scalbn_nan);
110ATF_TC_HEAD(scalbn_nan, tc)
111{
112	atf_tc_set_md_var(tc, "descr", "Test scalbn(NaN, n) == NaN");
113}
114
115ATF_TC_BODY(scalbn_nan, tc)
116{
117	const double x = 0.0L / 0.0L;
118	double y;
119	size_t i;
120
121	ATF_REQUIRE(isnan(x) != 0);
122
123	for (i = 0; i < __arraycount(exps); i++) {
124		y = scalbn(x, exps[i]);
125		ATF_CHECK(isnan(y) != 0);
126	}
127}
128
129ATF_TC(scalbn_inf_neg);
130ATF_TC_HEAD(scalbn_inf_neg, tc)
131{
132	atf_tc_set_md_var(tc, "descr", "Test scalbn(-Inf, n) == -Inf");
133}
134
135ATF_TC_BODY(scalbn_inf_neg, tc)
136{
137	const double x = -1.0L / 0.0L;
138	size_t i;
139
140	for (i = 0; i < __arraycount(exps); i++)
141		ATF_CHECK(scalbn(x, exps[i]) == x);
142}
143
144ATF_TC(scalbn_inf_pos);
145ATF_TC_HEAD(scalbn_inf_pos, tc)
146{
147	atf_tc_set_md_var(tc, "descr", "Test scalbn(+Inf, n) == +Inf");
148}
149
150ATF_TC_BODY(scalbn_inf_pos, tc)
151{
152	const double x = 1.0L / 0.0L;
153	size_t i;
154
155	for (i = 0; i < __arraycount(exps); i++)
156		ATF_CHECK(scalbn(x, exps[i]) == x);
157}
158
159ATF_TC(scalbn_ldexp);
160ATF_TC_HEAD(scalbn_ldexp, tc)
161{
162	atf_tc_set_md_var(tc, "descr", "Test scalbn(x, n) == ldexp(x, n)");
163}
164
165ATF_TC_BODY(scalbn_ldexp, tc)
166{
167#if FLT_RADIX == 2
168	const double x = 2.91288191221812821;
169	double y;
170	size_t i;
171
172	for (i = 0; i < __arraycount(exps); i++) {
173		y = scalbn(x, exps[i]);
174		ATF_CHECK_MSG(y == ldexp(x, exps[i]), "test %zu: exponent=%d, "
175		    "y=%g, expected %g (diff: %g)", i, exps[i], y,
176		    ldexp(x, exps[i]), y - ldexp(x, exps[i]));
177	}
178#endif
179}
180
181ATF_TC(scalbn_zero_neg);
182ATF_TC_HEAD(scalbn_zero_neg, tc)
183{
184	atf_tc_set_md_var(tc, "descr", "Test scalbn(-0.0, n) == -0.0");
185}
186
187ATF_TC_BODY(scalbn_zero_neg, tc)
188{
189	const double x = -0.0L;
190	double y;
191	size_t i;
192
193	ATF_REQUIRE(signbit(x) != 0);
194
195	for (i = 0; i < __arraycount(exps); i++) {
196		y = scalbn(x, exps[i]);
197		ATF_CHECK(x == y);
198		ATF_CHECK(signbit(y) != 0);
199	}
200}
201
202ATF_TC(scalbn_zero_pos);
203ATF_TC_HEAD(scalbn_zero_pos, tc)
204{
205	atf_tc_set_md_var(tc, "descr", "Test scalbn(+0.0, n) == +0.0");
206}
207
208ATF_TC_BODY(scalbn_zero_pos, tc)
209{
210	const double x = 0.0L;
211	double y;
212	size_t i;
213
214	ATF_REQUIRE(signbit(x) == 0);
215
216	for (i = 0; i < __arraycount(exps); i++) {
217		y = scalbn(x, exps[i]);
218		ATF_CHECK(x == y);
219		ATF_CHECK(signbit(y) == 0);
220	}
221}
222
223/*
224 * scalbnf(3)
225 */
226ATF_TC(scalbnf_val);
227ATF_TC_HEAD(scalbnf_val, tc)
228{
229	atf_tc_set_md_var(tc, "descr", "Test scalbnf() for a few values");
230}
231
232ATF_TC_BODY(scalbnf_val, tc)
233{
234	const struct testcase *tests = test_vals;
235	const size_t tcnt = __arraycount(test_vals);
236	size_t i;
237	double rv;
238
239	for (i = 0; i < tcnt; i++) {
240		errno = 0;
241		rv = scalbnf(tests[i].inval, tests[i].exp);
242		ATF_CHECK_EQ_MSG(errno, tests[i].error,
243		    "test %zu: errno %d instead of %d", i, errno,
244		    tests[i].error);
245		/* scalbn is always exact except for underflow or overflow.  */
246		ATF_CHECK_MSG(rv == (float)tests[i].result,
247		    "test %zu: return value %.8g instead of %.8g"
248		    " (error %.8g)",
249		    i, rv, tests[i].result,
250		    fabsf((tests[i].result - rv)/tests[i].result));
251	}
252}
253
254ATF_TC(scalbnf_nan);
255ATF_TC_HEAD(scalbnf_nan, tc)
256{
257	atf_tc_set_md_var(tc, "descr", "Test scalbnf(NaN, n) == NaN");
258}
259
260ATF_TC_BODY(scalbnf_nan, tc)
261{
262	const float x = 0.0L / 0.0L;
263	float y;
264	size_t i;
265
266	ATF_REQUIRE(isnan(x) != 0);
267
268	for (i = 0; i < __arraycount(exps); i++) {
269		y = scalbnf(x, exps[i]);
270		ATF_CHECK(isnan(y) != 0);
271	}
272}
273
274ATF_TC(scalbnf_inf_neg);
275ATF_TC_HEAD(scalbnf_inf_neg, tc)
276{
277	atf_tc_set_md_var(tc, "descr", "Test scalbnf(-Inf, n) == -Inf");
278}
279
280ATF_TC_BODY(scalbnf_inf_neg, tc)
281{
282	const float x = -1.0L / 0.0L;
283	size_t i;
284
285	for (i = 0; i < __arraycount(exps); i++)
286		ATF_CHECK(scalbnf(x, exps[i]) == x);
287}
288
289ATF_TC(scalbnf_inf_pos);
290ATF_TC_HEAD(scalbnf_inf_pos, tc)
291{
292	atf_tc_set_md_var(tc, "descr", "Test scalbnf(+Inf, n) == +Inf");
293}
294
295ATF_TC_BODY(scalbnf_inf_pos, tc)
296{
297	const float x = 1.0L / 0.0L;
298	size_t i;
299
300	for (i = 0; i < __arraycount(exps); i++)
301		ATF_CHECK(scalbnf(x, exps[i]) == x);
302}
303
304ATF_TC(scalbnf_ldexpf);
305ATF_TC_HEAD(scalbnf_ldexpf, tc)
306{
307	atf_tc_set_md_var(tc, "descr", "Test scalbnf(x, n) == ldexpf(x, n)");
308}
309
310ATF_TC_BODY(scalbnf_ldexpf, tc)
311{
312#if FLT_RADIX == 2
313	const float x = 2.91288191221812821;
314	float y;
315	size_t i;
316
317	for (i = 0; i < __arraycount(exps); i++) {
318		y = scalbnf(x, exps[i]);
319		ATF_CHECK_MSG(y == ldexpf(x, exps[i]),
320		    "test %zu: exponent=%d, y=%g ldexpf returns %g (diff: %g)",
321		    i, exps[i], y, ldexpf(x, exps[i]), y-ldexpf(x, exps[i]));
322	}
323#endif
324}
325
326ATF_TC(scalbnf_zero_neg);
327ATF_TC_HEAD(scalbnf_zero_neg, tc)
328{
329	atf_tc_set_md_var(tc, "descr", "Test scalbnf(-0.0, n) == -0.0");
330}
331
332ATF_TC_BODY(scalbnf_zero_neg, tc)
333{
334	const float x = -0.0L;
335	float y;
336	size_t i;
337
338	ATF_REQUIRE(signbit(x) != 0);
339
340	for (i = 0; i < __arraycount(exps); i++) {
341		y = scalbnf(x, exps[i]);
342		ATF_CHECK(x == y);
343		ATF_CHECK(signbit(y) != 0);
344	}
345}
346
347ATF_TC(scalbnf_zero_pos);
348ATF_TC_HEAD(scalbnf_zero_pos, tc)
349{
350	atf_tc_set_md_var(tc, "descr", "Test scalbnf(+0.0, n) == +0.0");
351}
352
353ATF_TC_BODY(scalbnf_zero_pos, tc)
354{
355	const float x = 0.0L;
356	float y;
357	size_t i;
358
359	ATF_REQUIRE(signbit(x) == 0);
360
361	for (i = 0; i < __arraycount(exps); i++) {
362		y = scalbnf(x, exps[i]);
363		ATF_CHECK(x == y);
364		ATF_CHECK(signbit(y) == 0);
365	}
366}
367
368/*
369 * scalbnl(3)
370 */
371ATF_TC(scalbnl_val);
372ATF_TC_HEAD(scalbnl_val, tc)
373{
374	atf_tc_set_md_var(tc, "descr", "Test scalbnl() for a few values");
375}
376
377ATF_TC_BODY(scalbnl_val, tc)
378{
379	const struct testcase *tests = test_vals;
380	const size_t tcnt = __arraycount(test_vals);
381	size_t i;
382	long double rv;
383
384	for (i = 0; i < tcnt; i++) {
385		errno = 0;
386		rv = scalbnl(tests[i].inval, tests[i].exp);
387		ATF_CHECK_EQ_MSG(errno, tests[i].error,
388		    "test %zu: errno %d instead of %d", i, errno,
389		    tests[i].error);
390		/* scalbn is always exact except for underflow or overflow.  */
391		ATF_CHECK_MSG(rv == (long double)tests[i].result,
392		    "test %zu: return value %.35Lg instead of %.35Lg"
393		    " (error %.35Lg)",
394		    i, rv, (long double)tests[i].result,
395		    fabsl(((long double)tests[i].result - rv)/tests[i].result));
396	}
397}
398
399ATF_TC(scalbnl_nan);
400ATF_TC_HEAD(scalbnl_nan, tc)
401{
402	atf_tc_set_md_var(tc, "descr", "Test scalbnl(NaN, n) == NaN");
403}
404
405ATF_TC_BODY(scalbnl_nan, tc)
406{
407	const long double x = 0.0L / 0.0L;
408	long double y;
409	size_t i;
410
411	ATF_CHECK_MSG(isnan(x), "x=%La", x);
412
413	for (i = 0; i < __arraycount(exps); i++) {
414		y = scalbnl(x, exps[i]);
415		ATF_CHECK_MSG(isnan(y), "y=%La", y);
416	}
417}
418
419ATF_TC(scalbnl_inf_neg);
420ATF_TC_HEAD(scalbnl_inf_neg, tc)
421{
422	atf_tc_set_md_var(tc, "descr", "Test scalbnl(-Inf, n) == -Inf");
423}
424
425ATF_TC_BODY(scalbnl_inf_neg, tc)
426{
427	const long double x = -1.0L / 0.0L;
428	size_t i;
429
430	for (i = 0; i < __arraycount(exps); i++)
431		ATF_CHECK(scalbnl(x, exps[i]) == x);
432}
433
434ATF_TC(scalbnl_inf_pos);
435ATF_TC_HEAD(scalbnl_inf_pos, tc)
436{
437	atf_tc_set_md_var(tc, "descr", "Test scalbnl(+Inf, n) == +Inf");
438}
439
440ATF_TC_BODY(scalbnl_inf_pos, tc)
441{
442	const long double x = 1.0L / 0.0L;
443	size_t i;
444
445	for (i = 0; i < __arraycount(exps); i++)
446		ATF_CHECK(scalbnl(x, exps[i]) == x);
447}
448
449ATF_TC(scalbnl_zero_neg);
450ATF_TC_HEAD(scalbnl_zero_neg, tc)
451{
452	atf_tc_set_md_var(tc, "descr", "Test scalbnl(-0.0, n) == -0.0");
453}
454
455ATF_TC_BODY(scalbnl_zero_neg, tc)
456{
457	const long double x = -0.0L;
458	long double y;
459	size_t i;
460
461	ATF_REQUIRE(signbit(x) != 0);
462
463	for (i = 0; i < __arraycount(exps); i++) {
464		y = scalbnl(x, exps[i]);
465		ATF_CHECK(x == y);
466		ATF_CHECK(signbit(y) != 0);
467	}
468}
469
470ATF_TC(scalbnl_zero_pos);
471ATF_TC_HEAD(scalbnl_zero_pos, tc)
472{
473	atf_tc_set_md_var(tc, "descr", "Test scalbnl(+0.0, n) == +0.0");
474}
475
476ATF_TC_BODY(scalbnl_zero_pos, tc)
477{
478	const long double x = 0.0L;
479	long double y;
480	size_t i;
481
482	ATF_REQUIRE(signbit(x) == 0);
483
484	for (i = 0; i < __arraycount(exps); i++) {
485		y = scalbnl(x, exps[i]);
486		ATF_CHECK(x == y);
487		ATF_CHECK(signbit(y) == 0);
488	}
489}
490
491ATF_TP_ADD_TCS(tp)
492{
493
494	ATF_TP_ADD_TC(tp, scalbn_val);
495	ATF_TP_ADD_TC(tp, scalbn_nan);
496	ATF_TP_ADD_TC(tp, scalbn_inf_neg);
497	ATF_TP_ADD_TC(tp, scalbn_inf_pos);
498	ATF_TP_ADD_TC(tp, scalbn_ldexp);
499	ATF_TP_ADD_TC(tp, scalbn_zero_neg);
500	ATF_TP_ADD_TC(tp, scalbn_zero_pos);
501
502	ATF_TP_ADD_TC(tp, scalbnf_val);
503	ATF_TP_ADD_TC(tp, scalbnf_nan);
504	ATF_TP_ADD_TC(tp, scalbnf_inf_neg);
505	ATF_TP_ADD_TC(tp, scalbnf_inf_pos);
506	ATF_TP_ADD_TC(tp, scalbnf_ldexpf);
507	ATF_TP_ADD_TC(tp, scalbnf_zero_neg);
508	ATF_TP_ADD_TC(tp, scalbnf_zero_pos);
509
510	ATF_TP_ADD_TC(tp, scalbnl_val);
511	ATF_TP_ADD_TC(tp, scalbnl_nan);
512	ATF_TP_ADD_TC(tp, scalbnl_inf_neg);
513	ATF_TP_ADD_TC(tp, scalbnl_inf_pos);
514/*	ATF_TP_ADD_TC(tp, scalbnl_ldexp);	*/
515	ATF_TP_ADD_TC(tp, scalbnl_zero_neg);
516	ATF_TP_ADD_TC(tp, scalbnl_zero_pos);
517
518	return atf_no_error();
519}
520