root/tests/lib/libm/t_ldexp.c
/* $NetBSD: t_ldexp.c,v 1.17 2018/11/07 03:59:36 riastradh Exp $ */

/*-
 * Copyright (c) 2011 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jukka Ruohonen.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#include <sys/cdefs.h>
__RCSID("$NetBSD: t_ldexp.c,v 1.17 2018/11/07 03:59:36 riastradh Exp $");

#include <sys/param.h>

#include <atf-c.h>

#include <float.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <string.h>

#define SKIP    9999
#define FORMAT  "%23.23lg"

static const int exps[] = { 0, 1, -1, 100, -100 };

struct ldexp_test {
        double      x;
        int         exp1;
        int         exp2;
        const char *result;
};

struct ldexp_test ldexp_basic[] = {
        { 1.0,  5,      SKIP,   "                     32" },
        { 1.0,  1022,   SKIP,   "4.4942328371557897693233e+307" },
        { 1.0,  1023,   -1,     "4.4942328371557897693233e+307" },
        { 1.0,  1023,   SKIP,   "8.9884656743115795386465e+307" },
        { 1.0,  1022,   1,      "8.9884656743115795386465e+307" },
        { 1.0,  -1022,  2045,   "8.9884656743115795386465e+307" },
        { 1.0,  -5,     SKIP,   "                0.03125" },
        { 1.0,  -1021,  SKIP,   "4.4501477170144027661805e-308" },
        { 1.0,  -1022,  1,      "4.4501477170144027661805e-308" },
        { 1.0,  -1022,  SKIP,   "2.2250738585072013830902e-308" },
        { 1.0,  -1021,  -1,     "2.2250738585072013830902e-308" },
        { 1.0,  1023,   -2045,  "2.2250738585072013830902e-308" },
        { 1.0,  1023,   -1023,  "                      1" },
        { 1.0,  -1022,  1022,   "                      1" },
        { 0,    0,      0,      NULL }
};

struct ldexp_test ldexp_zero[] = {
        { 0.0,  -1,     SKIP,   "                      0" },
        { 0.0,  0,      SKIP,   "                      0" },
        { 0.0,  1,      SKIP,   "                      0" },
        { 0.0,  1024,   SKIP,   "                      0" },
        { 0.0,  1025,   SKIP,   "                      0" },
        { 0.0,  -1023,  SKIP,   "                      0" },
        { 0.0,  -1024,  SKIP,   "                      0" },
        { 0,    0,      0,      NULL }
};

struct ldexp_test ldexp_infinity[] = {
        { 1.0,  1024,   -1,     "                    inf" },
        { 1.0,  1024,   0,      "                    inf" },
        { 1.0,  1024,   1,      "                    inf" },
        { -1.0, 1024,   -1,     "                   -inf" },
        { -1.0, 1024,   0,      "                   -inf" },
        { -1.0, 1024,   1,      "                   -inf" },
        { 0,    0,      0,      NULL }
};

struct ldexp_test ldexp_overflow[] = {
        { 1.0,  1024,   SKIP,   "                    inf" },
        { 1.0,  1023,   1,      "                    inf" },
        { 1.0,  -1022,  2046,   "                    inf" },
        { 1.0,  1025,   SKIP,   "                    inf" },
        { 2.0,  INT_MAX,SKIP,   "                    inf" },
        { -1.0, 1024,   SKIP,   "                   -inf" },
        { -1.0, 1023,   1,      "                   -inf" },
        { -1.0, -1022,  2046,   "                   -inf" },
        { -1.0, 1025,   SKIP,   "                   -inf" },
        { -2.0, INT_MAX,SKIP,   "                   -inf" },
        { 0,    0,      0,      NULL }
};

struct ldexp_test ldexp_denormal[] = {
        { 1.0,  -1023,  SKIP,   "1.1125369292536006915451e-308" },
        { 1.0,  -1022,  -1,     "1.1125369292536006915451e-308" },
        { 1.0,  1023,   -2046,  "1.1125369292536006915451e-308" },
        { 1.0,  -1024,  SKIP,   "5.5626846462680034577256e-309" },
        { 1.0,  -1074,  SKIP,   "4.9406564584124654417657e-324" },
        { -1.0, -1023,  SKIP,   "-1.1125369292536006915451e-308" },
        { -1.0, -1022,  -1,     "-1.1125369292536006915451e-308" },
        { -1.0, 1023,   -2046,  "-1.1125369292536006915451e-308" },
        { -1.0, -1024,  SKIP,   "-5.5626846462680034577256e-309" },
        { -1.0, -1074,  SKIP,   "-4.9406564584124654417657e-324" },
        { 0,    0,      0,      NULL }
};

struct ldexp_test ldexp_underflow[] = {
        { 1.0,  -1075,  SKIP,   "                      0" },
        { 1.0,  -1074,  -1,     "                      0" },
        { 1.0,  1023,   -2098,  "                      0" },
        { 1.0,  -1076,  SKIP,   "                      0" },
        { -1.0, -1075,  SKIP,   "                     -0" },
        { -1.0, -1074,  -1,     "                     -0" },
        { -1.0, 1023,   -2098,  "                     -0" },
        { -1.0, -1076,  SKIP,   "                     -0" },
        { 0,    0,      0,      NULL }
};

struct ldexp_test ldexp_denormal_large[] = {
        { 1.0,  -1028,  1024,   "                 0.0625" },
        { 1.0,  -1028,  1025,   "                  0.125" },
        { 1.0,  -1028,  1026,   "                   0.25" },
        { 1.0,  -1028,  1027,   "                    0.5" },
        { 1.0,  -1028,  1028,   "                      1" },
        { 1.0,  -1028,  1029,   "                      2" },
        { 1.0,  -1028,  1030,   "                      4" },
        { 1.0,  -1028,  1040,   "                   4096" },
        { 1.0,  -1028,  1050,   "                4194304" },
        { 1.0,  -1028,  1060,   "             4294967296" },
        { 1.0,  -1028,  1100,   " 4722366482869645213696" },
        { 1.0,  -1028,  1200,   "5.9863107065073783529623e+51" },
        { 1.0,  -1028,  1300,   "7.5885503602567541832791e+81" },
        { 1.0,  -1028,  1400,   "9.6196304190416209014353e+111" },
        { 1.0,  -1028,  1500,   "1.2194330274671844653834e+142" },
        { 1.0,  -1028,  1600,   "1.5458150092069033378781e+172" },
        { 1.0,  -1028,  1700,   "1.9595533242629369747791e+202" },
        { 1.0,  -1028,  1800,   "2.4840289476811342962384e+232" },
        { 1.0,  -1028,  1900,   "3.1488807865122869393369e+262" },
        { 1.0,  -1028,  2000,   "3.9916806190694396233127e+292" },
        { 1.0,  -1028,  2046,   "2.808895523222368605827e+306" },
        { 1.0,  -1028,  2047,   "5.6177910464447372116541e+306" },
        { 1.0,  -1028,  2048,   "1.1235582092889474423308e+307" },
        { 1.0,  -1028,  2049,   "2.2471164185778948846616e+307" },
        { 1.0,  -1028,  2050,   "4.4942328371557897693233e+307" },
        { 1.0,  -1028,  2051,   "8.9884656743115795386465e+307" },
        { 0,    0,      0,      NULL }
};

static void
run_test(struct ldexp_test *table)
{
        char outbuf[64];
        size_t i;
        double v;

        for (i = 0; table->result != NULL; table++, i++) {

                v = ldexp(table->x, table->exp1);

                if (table->exp2 != SKIP)
                        v = ldexp(v, table->exp2);

                (void)snprintf(outbuf, sizeof(outbuf), FORMAT, v);

                ATF_CHECK_STREQ_MSG(table->result, outbuf,
                            "Entry %zu:\n\tExp: \"%s\"\n\tAct: \"%s\"",
                            i, table->result, outbuf);
        }
}

/*
 * ldexp(3)
 */
ATF_TC(ldexp_exp2);
ATF_TC_HEAD(ldexp_exp2, tc)
{
        atf_tc_set_md_var(tc, "descr", "Test ldexp(x, n) == x * exp2(n)");
}

ATF_TC_BODY(ldexp_exp2, tc)
{
        const double n[] = { 1, 2, 3, 10, 50, 100 };
        const double eps = DBL_EPSILON;
        const double x = 12.0;
        size_t i;

        for (i = 0; i < __arraycount(n); i++) {
                double y = ldexp(x, n[i]);

                if (!(fabs((y - (x * exp2(n[i])))/y) <= eps)) {
                        atf_tc_fail_nonfatal("ldexp(%.17g, %.17g) = %.17g "
                            "!= %.17g * exp2(%.17g) = %.17g",
                            x, n[i], y, x, n[i], (x * exp2(n[i])));
                }
        }
}

ATF_TC(ldexp_nan);
ATF_TC_HEAD(ldexp_nan, tc)
{
        atf_tc_set_md_var(tc, "descr", "Test ldexp(NaN) == NaN");
}

ATF_TC_BODY(ldexp_nan, tc)
{
        const double x = 0.0L / 0.0L;
        double y;
        size_t i;

        ATF_REQUIRE(isnan(x) != 0);

        for (i = 0; i < __arraycount(exps); i++) {
                y = ldexp(x, exps[i]);
                ATF_CHECK(isnan(y) != 0);
        }
}

ATF_TC(ldexp_inf_neg);
ATF_TC_HEAD(ldexp_inf_neg, tc)
{
        atf_tc_set_md_var(tc, "descr", "Test ldexp(-Inf) == -Inf");
}

ATF_TC_BODY(ldexp_inf_neg, tc)
{
        const double x = -1.0L / 0.0L;
        size_t i;

        for (i = 0; i < __arraycount(exps); i++)
                ATF_CHECK(ldexp(x, exps[i]) == x);
}

ATF_TC(ldexp_inf_pos);
ATF_TC_HEAD(ldexp_inf_pos, tc)
{
        atf_tc_set_md_var(tc, "descr", "Test ldexp(+Inf) == +Inf");
}

ATF_TC_BODY(ldexp_inf_pos, tc)
{
        const double x = 1.0L / 0.0L;
        size_t i;

        for (i = 0; i < __arraycount(exps); i++)
                ATF_CHECK(ldexp(x, exps[i]) == x);
}

ATF_TC(ldexp_zero_neg);
ATF_TC_HEAD(ldexp_zero_neg, tc)
{
        atf_tc_set_md_var(tc, "descr", "Test ldexp(-0.0) == -0.0");
}

ATF_TC_BODY(ldexp_zero_neg, tc)
{
        const double x = -0.0L;
        double y;
        size_t i;

        ATF_REQUIRE(signbit(x) != 0);

        for (i = 0; i < __arraycount(exps); i++) {
                y = ldexp(x, exps[i]);
                ATF_CHECK(x == y);
                ATF_CHECK(signbit(y) != 0);
        }
}

ATF_TC(ldexp_zero_pos);
ATF_TC_HEAD(ldexp_zero_pos, tc)
{
        atf_tc_set_md_var(tc, "descr", "Test ldexp(+0.0) == +0.0");
}

ATF_TC_BODY(ldexp_zero_pos, tc)
{
        const double x = 0.0L;
        double y;
        size_t i;

        ATF_REQUIRE(signbit(x) == 0);

        for (i = 0; i < __arraycount(exps); i++) {
                y = ldexp(x, exps[i]);
                ATF_CHECK(x == y);
                ATF_CHECK(signbit(y) == 0);
        }
}

/*
 * ldexpf(3)
 */

ATF_TC(ldexpf_exp2f);
ATF_TC_HEAD(ldexpf_exp2f, tc)
{
        atf_tc_set_md_var(tc, "descr", "Test ldexpf(x, n) == x * exp2f(n)");
}

ATF_TC_BODY(ldexpf_exp2f, tc)
{
        const float n[] = { 1, 2, 3, 10, 50, 100 };
        const float eps = FLT_EPSILON;
        const float x = 12.0;
        size_t i;

        for (i = 0; i < __arraycount(n); i++) {
                float y = ldexpf(x, n[i]);

                if (!(fabsf((y - (x * exp2f(n[i])))/y) <= eps)) {
                        atf_tc_fail_nonfatal("ldexpf(%.17g, %.17g) = %.17g "
                            "!= %.17g * exp2f(%.17g) = %.17g",
                            x, n[i], y, x, n[i], (x * exp2f(n[i])));
                }
        }
}

ATF_TC(ldexpf_nan);
ATF_TC_HEAD(ldexpf_nan, tc)
{
        atf_tc_set_md_var(tc, "descr", "Test ldexpf(NaN) == NaN");
}

ATF_TC_BODY(ldexpf_nan, tc)
{
        const float x = 0.0L / 0.0L;
        float y;
        size_t i;

        ATF_REQUIRE(isnan(x) != 0);

        for (i = 0; i < __arraycount(exps); i++) {
                y = ldexpf(x, exps[i]);
                ATF_CHECK(isnan(y) != 0);
        }
}

ATF_TC(ldexpf_inf_neg);
ATF_TC_HEAD(ldexpf_inf_neg, tc)
{
        atf_tc_set_md_var(tc, "descr", "Test ldexpf(-Inf) == -Inf");
}

ATF_TC_BODY(ldexpf_inf_neg, tc)
{
        const float x = -1.0L / 0.0L;
        size_t i;

        for (i = 0; i < __arraycount(exps); i++)
                ATF_CHECK(ldexpf(x, exps[i]) == x);
}

ATF_TC(ldexpf_inf_pos);
ATF_TC_HEAD(ldexpf_inf_pos, tc)
{
        atf_tc_set_md_var(tc, "descr", "Test ldexpf(+Inf) == +Inf");
}

ATF_TC_BODY(ldexpf_inf_pos, tc)
{
        const float x = 1.0L / 0.0L;
        size_t i;

        for (i = 0; i < __arraycount(exps); i++)
                ATF_CHECK(ldexpf(x, exps[i]) == x);
}

ATF_TC(ldexpf_zero_neg);
ATF_TC_HEAD(ldexpf_zero_neg, tc)
{
        atf_tc_set_md_var(tc, "descr", "Test ldexpf(-0.0) == -0.0");
}

ATF_TC_BODY(ldexpf_zero_neg, tc)
{
        const float x = -0.0L;
        float y;
        size_t i;

        ATF_REQUIRE(signbit(x) != 0);

        for (i = 0; i < __arraycount(exps); i++) {
                y = ldexpf(x, exps[i]);
                ATF_CHECK(x == y);
                ATF_CHECK(signbit(y) != 0);
        }
}

ATF_TC(ldexpf_zero_pos);
ATF_TC_HEAD(ldexpf_zero_pos, tc)
{
        atf_tc_set_md_var(tc, "descr", "Test ldexpf(+0.0) == +0.0");
}

ATF_TC_BODY(ldexpf_zero_pos, tc)
{
        const float x = 0.0L;
        float y;
        size_t i;

        ATF_REQUIRE(signbit(x) == 0);

        for (i = 0; i < __arraycount(exps); i++) {
                y = ldexpf(x, exps[i]);
                ATF_CHECK(x == y);
                ATF_CHECK(signbit(y) == 0);
        }
}

#define TEST(name, desc)                                                \
        ATF_TC(name);                                                   \
        ATF_TC_HEAD(name, tc)                                           \
        {                                                               \
                                                                        \
                atf_tc_set_md_var(tc, "descr",                          \
                    "Test ldexp(3) for " ___STRING(desc));              \
        }                                                               \
        ATF_TC_BODY(name, tc)                                           \
        {                                                               \
                if (strcmp("vax", MACHINE_ARCH) == 0)                   \
                        atf_tc_skip("Test not valid for " MACHINE_ARCH); \
                run_test(name);                                         \
        }

TEST(ldexp_basic, basics)
TEST(ldexp_zero, zero)
TEST(ldexp_infinity, infinity)
TEST(ldexp_overflow, overflow)
TEST(ldexp_denormal, denormal)
TEST(ldexp_denormal_large, large)
TEST(ldexp_underflow, underflow)

ATF_TP_ADD_TCS(tp)
{

        ATF_TP_ADD_TC(tp, ldexp_basic);
        ATF_TP_ADD_TC(tp, ldexp_zero);
        ATF_TP_ADD_TC(tp, ldexp_infinity);
        ATF_TP_ADD_TC(tp, ldexp_overflow);
        ATF_TP_ADD_TC(tp, ldexp_denormal);
        ATF_TP_ADD_TC(tp, ldexp_underflow);
        ATF_TP_ADD_TC(tp, ldexp_denormal_large);

        ATF_TP_ADD_TC(tp, ldexp_exp2);
        ATF_TP_ADD_TC(tp, ldexp_nan);
        ATF_TP_ADD_TC(tp, ldexp_inf_neg);
        ATF_TP_ADD_TC(tp, ldexp_inf_pos);
        ATF_TP_ADD_TC(tp, ldexp_zero_neg);
        ATF_TP_ADD_TC(tp, ldexp_zero_pos);

        ATF_TP_ADD_TC(tp, ldexpf_exp2f);
        ATF_TP_ADD_TC(tp, ldexpf_nan);
        ATF_TP_ADD_TC(tp, ldexpf_inf_neg);
        ATF_TP_ADD_TC(tp, ldexpf_inf_pos);
        ATF_TP_ADD_TC(tp, ldexpf_zero_neg);
        ATF_TP_ADD_TC(tp, ldexpf_zero_pos);

        return atf_no_error();
}