1/* $NetBSD: t_scalbn.c,v 1.16 2018/11/07 03:59:36 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.16 2018/11/07 03:59:36 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};
52static struct 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		    fabs((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#ifndef __HAVE_LONG_DOUBLE
380	atf_tc_skip("Requires long double support");
381#else
382	const struct testcase *tests = test_vals;
383	const size_t tcnt = __arraycount(test_vals);
384	size_t i;
385	long double rv;
386
387	for (i = 0; i < tcnt; i++) {
388		errno = 0;
389		rv = scalbnl(tests[i].inval, tests[i].exp);
390		ATF_CHECK_EQ_MSG(errno, tests[i].error,
391		    "test %zu: errno %d instead of %d", i, errno,
392		    tests[i].error);
393		/* scalbn is always exact except for underflow or overflow.  */
394		ATF_CHECK_MSG(rv == (long double)tests[i].result,
395		    "test %zu: return value %.35Lg instead of %.35Lg"
396		    " (error %.35Lg)",
397		    i, rv, (long double)tests[i].result,
398		    fabsl(((long double)tests[i].result - rv)/tests[i].result));
399	}
400#endif
401}
402
403ATF_TC(scalbnl_nan);
404ATF_TC_HEAD(scalbnl_nan, tc)
405{
406	atf_tc_set_md_var(tc, "descr", "Test scalbnl(NaN, n) == NaN");
407}
408
409ATF_TC_BODY(scalbnl_nan, tc)
410{
411#ifndef __HAVE_LONG_DOUBLE
412	atf_tc_skip("Requires long double support");
413#else
414	const long double x = 0.0L / 0.0L;
415	long double y;
416	size_t i;
417
418	if (isnan(x) == 0) {
419		atf_tc_expect_fail("PR lib/45362");
420		atf_tc_fail("(0.0L / 0.0L) != NaN");
421	}
422
423	for (i = 0; i < __arraycount(exps); i++) {
424		y = scalbnl(x, exps[i]);
425		ATF_CHECK(isnan(y) != 0);
426	}
427#endif
428}
429
430ATF_TC(scalbnl_inf_neg);
431ATF_TC_HEAD(scalbnl_inf_neg, tc)
432{
433	atf_tc_set_md_var(tc, "descr", "Test scalbnl(-Inf, n) == -Inf");
434}
435
436ATF_TC_BODY(scalbnl_inf_neg, tc)
437{
438#ifndef __HAVE_LONG_DOUBLE
439	atf_tc_skip("Requires long double support");
440#else
441	const long double x = -1.0L / 0.0L;
442	size_t i;
443
444	for (i = 0; i < __arraycount(exps); i++)
445		ATF_CHECK(scalbnl(x, exps[i]) == x);
446#endif
447}
448
449ATF_TC(scalbnl_inf_pos);
450ATF_TC_HEAD(scalbnl_inf_pos, tc)
451{
452	atf_tc_set_md_var(tc, "descr", "Test scalbnl(+Inf, n) == +Inf");
453}
454
455ATF_TC_BODY(scalbnl_inf_pos, tc)
456{
457#ifndef __HAVE_LONG_DOUBLE
458	atf_tc_skip("Requires long double support");
459#else
460	const long double x = 1.0L / 0.0L;
461	size_t i;
462
463	for (i = 0; i < __arraycount(exps); i++)
464		ATF_CHECK(scalbnl(x, exps[i]) == x);
465#endif
466}
467
468ATF_TC(scalbnl_zero_neg);
469ATF_TC_HEAD(scalbnl_zero_neg, tc)
470{
471	atf_tc_set_md_var(tc, "descr", "Test scalbnl(-0.0, n) == -0.0");
472}
473
474ATF_TC_BODY(scalbnl_zero_neg, tc)
475{
476#ifndef __HAVE_LONG_DOUBLE
477	atf_tc_skip("Requires long double support");
478#else
479	const long double x = -0.0L;
480	long double y;
481	size_t i;
482
483	ATF_REQUIRE(signbit(x) != 0);
484
485	for (i = 0; i < __arraycount(exps); i++) {
486		y = scalbnl(x, exps[i]);
487		ATF_CHECK(x == y);
488		ATF_CHECK(signbit(y) != 0);
489	}
490#endif
491}
492
493ATF_TC(scalbnl_zero_pos);
494ATF_TC_HEAD(scalbnl_zero_pos, tc)
495{
496	atf_tc_set_md_var(tc, "descr", "Test scalbnl(+0.0, n) == +0.0");
497}
498
499ATF_TC_BODY(scalbnl_zero_pos, tc)
500{
501#ifndef __HAVE_LONG_DOUBLE
502	atf_tc_skip("Requires long double support");
503#else
504	const long double x = 0.0L;
505	long double y;
506	size_t i;
507
508	ATF_REQUIRE(signbit(x) == 0);
509
510	for (i = 0; i < __arraycount(exps); i++) {
511		y = scalbnl(x, exps[i]);
512		ATF_CHECK(x == y);
513		ATF_CHECK(signbit(y) == 0);
514	}
515#endif
516}
517
518ATF_TP_ADD_TCS(tp)
519{
520
521	ATF_TP_ADD_TC(tp, scalbn_val);
522	ATF_TP_ADD_TC(tp, scalbn_nan);
523	ATF_TP_ADD_TC(tp, scalbn_inf_neg);
524	ATF_TP_ADD_TC(tp, scalbn_inf_pos);
525	ATF_TP_ADD_TC(tp, scalbn_ldexp);
526	ATF_TP_ADD_TC(tp, scalbn_zero_neg);
527	ATF_TP_ADD_TC(tp, scalbn_zero_pos);
528
529	ATF_TP_ADD_TC(tp, scalbnf_val);
530	ATF_TP_ADD_TC(tp, scalbnf_nan);
531	ATF_TP_ADD_TC(tp, scalbnf_inf_neg);
532	ATF_TP_ADD_TC(tp, scalbnf_inf_pos);
533	ATF_TP_ADD_TC(tp, scalbnf_ldexpf);
534	ATF_TP_ADD_TC(tp, scalbnf_zero_neg);
535	ATF_TP_ADD_TC(tp, scalbnf_zero_pos);
536
537	ATF_TP_ADD_TC(tp, scalbnl_val);
538	ATF_TP_ADD_TC(tp, scalbnl_nan);
539	ATF_TP_ADD_TC(tp, scalbnl_inf_neg);
540	ATF_TP_ADD_TC(tp, scalbnl_inf_pos);
541/*	ATF_TP_ADD_TC(tp, scalbnl_ldexp);	*/
542	ATF_TP_ADD_TC(tp, scalbnl_zero_neg);
543	ATF_TP_ADD_TC(tp, scalbnl_zero_pos);
544
545	return atf_no_error();
546}
547