math: add fenv test and new 'add' function in gen
[libc-test] / src / math / gen / mp.c
index 96cd33a..be2ed39 100644 (file)
@@ -123,7 +123,7 @@ static void genf(struct t *p, mpfr_t my, int t, int r)
 
        t = adjust(mr, my, t, r);
        p->y = mpfr_get_flt(mr, r);
-       p->e = eflags(isnan(p->x) || isnan(p->x2));
+       p->e = eflags(isnan(p->x) || isnan(p->x2) || isnan(p->x3));
        i = eulpf(p->y);
        if (!isfinite(p->y)) {
                p->dy = 0;
@@ -193,7 +193,7 @@ static void gend(struct t *p, mpfr_t my, int t, int r)
 
        t = adjust(mr, my, t, r);
        p->y = mpfr_get_d(mr, r);
-       p->e = eflags(isnan(p->x) || isnan(p->x2));
+       p->e = eflags(isnan(p->x) || isnan(p->x2) || isnan(p->x3));
        i = eulp(p->y);
        if (!isfinite(p->y)) {
                p->dy = 0;
@@ -268,7 +268,7 @@ static void genl(struct t *p, mpfr_t my, int t, int r)
 
        t = adjust(mr, my, t, r);
        p->y = mpfr_get_ld(mr, r);
-       p->e = eflags(isnan(p->x) || isnan(p->x2));
+       p->e = eflags(isnan(p->x) || isnan(p->x2) || isnan(p->x3));
        i = eulpl(p->y);
        if (!isfinite(p->y)) {
                p->dy = 0;
@@ -327,8 +327,8 @@ static int mpl2(struct t *p, int (*fmp)(mpfr_t, const mpfr_t, const mpfr_t, mpfr
 
        mpsetupl();
        mpfr_clear_flags();
-       mpfr_set_d(mx, p->x, MPFR_RNDN);
-       mpfr_set_d(mx2, p->x2, MPFR_RNDN);
+       mpfr_set_ld(mx, p->x, MPFR_RNDN);
+       mpfr_set_ld(mx2, p->x2, MPFR_RNDN);
        tn = fmp(my, mx, mx2, r);
        genl(p, my, tn, r);
        if ((p->e & INEXACT) && nextafterl(p->y, 0) == 0) {
@@ -350,6 +350,20 @@ static int wrap_lgamma(mpfr_t my, const mpfr_t mx, mpfr_rnd_t r)
 {
        return mpfr_lgamma(my, &mplgamma_sign, mx, r);
 }
+static long mpremquo_q;
+static int wrap_remquo(mpfr_t my, const mpfr_t mx, const mpfr_t mx2, mpfr_rnd_t r)
+{
+       return mpfr_remquo(my, &mpremquo_q, mx, mx2, r);
+}
+static int mpbessel_n;
+static int wrap_jn(mpfr_t my, const mpfr_t mx, mpfr_rnd_t r)
+{
+       return mpfr_jn(my, mpbessel_n, mx, r);
+}
+static int wrap_yn(mpfr_t my, const mpfr_t mx, mpfr_rnd_t r)
+{
+       return mpfr_yn(my, mpbessel_n, mx, r);
+}
 static int wrap_ceil(mpfr_t my, const mpfr_t mx, mpfr_rnd_t r)
 {
        return mpfr_ceil(my, mx);
@@ -377,6 +391,8 @@ static int wrap_pow10(mpfr_t my, const mpfr_t mx, mpfr_rnd_t r)
        return mpfr_ui_pow(my, 10, mx, r);
 }
 
+int mpadd(struct t *t) { return mpd2(t, mpfr_add); }
+
 int mpacos(struct t *t) { return mpd1(t, mpfr_acos); }
 int mpacosf(struct t *t) { return mpf1(t, mpfr_acos); }
 int mpacosl(struct t *t) { return mpl1(t, mpfr_acos); }
@@ -468,39 +484,116 @@ int mplogb(struct t *t)
 {
        MPFR_DECL_INIT(mx, 53);
 
-       mpfr_set_d(mx, t->x, MPFR_RNDN);
-       t->y = mpfr_get_exp(mx) + 1;
        t->dy = 0;
        t->e = 0;
+       if (t->x == 0) {
+               t->y = -INFINITY;
+               t->e |= DIVBYZERO;
+               return 0;
+       }
+       if (isinf(t->x)) {
+               t->y = INFINITY;
+               return 0;
+       }
+       if (isnan(t->x)) {
+               t->y = t->x;
+               return 0;
+       }
+       mpfr_set_d(mx, t->x, MPFR_RNDN);
+       t->y = mpfr_get_exp(mx) - 1;
        return 0;
 }
 int mplogbf(struct t *t)
 {
        MPFR_DECL_INIT(mx, 24);
 
-       mpfr_set_flt(mx, t->x, MPFR_RNDN);
-       t->y = mpfr_get_exp(mx) + 1;
        t->dy = 0;
        t->e = 0;
+       if (t->x == 0) {
+               t->y = -INFINITY;
+               t->e |= DIVBYZERO;
+               return 0;
+       }
+       if (isinf(t->x)) {
+               t->y = INFINITY;
+               return 0;
+       }
+       if (isnan(t->x)) {
+               t->y = t->x;
+               return 0;
+       }
+       mpfr_set_flt(mx, t->x, MPFR_RNDN);
+       t->y = mpfr_get_exp(mx) - 1;
        return 0;
 }
 int mplogbl(struct t *t)
 {
        MPFR_DECL_INIT(mx, 64);
 
-       mpfr_set_ld(mx, t->x, MPFR_RNDN);
-       t->y = mpfr_get_exp(mx) + 1;
        t->dy = 0;
        t->e = 0;
+       if (t->x == 0) {
+               t->y = -INFINITY;
+               t->e |= DIVBYZERO;
+               return 0;
+       }
+       if (isinf(t->x)) {
+               t->y = INFINITY;
+               return 0;
+       }
+       if (isnan(t->x)) {
+               t->y = t->x;
+               return 0;
+       }
+       mpfr_set_ld(mx, t->x, MPFR_RNDN);
+       t->y = mpfr_get_exp(mx) - 1;
+       return 0;
+}
+int mpnearbyint(struct t *t) { return mpd1(t, wrap_nearbyint) || (t->e&=~INEXACT, 0); }
+int mpnearbyintf(struct t *t) { return mpf1(t, wrap_nearbyint) || (t->e&=~INEXACT, 0); }
+int mpnearbyintl(struct t *t) { return mpl1(t, wrap_nearbyint) || (t->e&=~INEXACT, 0); }
+// TODO: hard to implement with mpfr
+int mpnextafter(struct t *t)
+{
+       feclearexcept(FE_ALL_EXCEPT);
+       t->y = nextafter(t->x, t->x2);
+       t->e = getexcept();
+       t->dy = 0;
+       return 0;
+}
+int mpnextafterf(struct t *t)
+{
+       feclearexcept(FE_ALL_EXCEPT);
+       t->y = nextafterf(t->x, t->x2);
+       t->e = getexcept();
+       t->dy = 0;
+       return 0;
+}
+int mpnextafterl(struct t *t)
+{
+       feclearexcept(FE_ALL_EXCEPT);
+       t->y = nextafterl(t->x, t->x2);
+       t->e = getexcept();
+       t->dy = 0;
+       return 0;
+}
+int mpnexttoward(struct t *t)
+{
+       feclearexcept(FE_ALL_EXCEPT);
+       t->y = nexttoward(t->x, t->x2);
+       t->e = getexcept();
+       t->dy = 0;
+       return 0;
+}
+int mpnexttowardf(struct t *t)
+{
+       feclearexcept(FE_ALL_EXCEPT);
+       t->y = nexttowardf(t->x, t->x2);
+       t->e = getexcept();
+       t->dy = 0;
        return 0;
 }
-int mpnearbyint(struct t *t) { return mpd1(t, wrap_nearbyint); }
-int mpnearbyintf(struct t *t) { return mpf1(t, wrap_nearbyint); }
-int mpnearbyintl(struct t *t) { return mpl1(t, wrap_nearbyint); }
-int mpnextafter(struct t *t) { return -1; }
-int mpnextafterf(struct t *t) { return -1; }
-int mpnextafterl(struct t *t) { return -1; }
-int mpnexttowardl(struct t *t) { return -1; }
+int mpnexttowardl(struct t *t) { return mpnextafterl(t); }
 int mppow(struct t *t) { return mpd2(t, mpfr_pow); }
 int mppowf(struct t *t) { return mpf2(t, mpfr_pow); }
 int mppowl(struct t *t) { return mpl2(t, mpfr_pow); }
@@ -528,6 +621,7 @@ int mptanl(struct t *t) { return mpl1(t, mpfr_tan); }
 int mptanh(struct t *t) { return mpd1(t, mpfr_tanh); }
 int mptanhf(struct t *t) { return mpf1(t, mpfr_tanh); }
 int mptanhl(struct t *t) { return mpl1(t, mpfr_tanh); }
+// TODO: tgamma(2) raises wrong flags
 int mptgamma(struct t *t) { return mpd1(t, mpfr_gamma); }
 int mptgammaf(struct t *t) { return mpf1(t, mpfr_gamma); }
 int mptgammal(struct t *t) { return mpl1(t, mpfr_gamma); }
@@ -538,8 +632,23 @@ int mpj0(struct t *t) { return mpd1(t, mpfr_j0); }
 int mpj1(struct t *t) { return mpd1(t, mpfr_j1); }
 int mpy0(struct t *t) { return mpd1(t, mpfr_y0); }
 int mpy1(struct t *t) { return mpd1(t, mpfr_y1); }
-int mpscalb(struct t *t) { return -1; }
-int mpscalbf(struct t *t) { return -1; }
+// TODO: non standard functions
+int mpscalb(struct t *t)
+{
+       setupfenv(t->r);
+       t->y = scalb(t->x, t->x2);
+       t->e = getexcept();
+       t->dy = 0; // wrong
+       return 0;
+}
+int mpscalbf(struct t *t)
+{
+       setupfenv(t->r);
+       t->y = scalbf(t->x, t->x2);
+       t->e = getexcept();
+       t->dy = 0; // wrong
+       return 0;
+}
 int mpj0f(struct t *t) { return mpf1(t, mpfr_j0); }
 int mpj0l(struct t *t) { return mpl1(t, mpfr_j0); }
 int mpj1f(struct t *t) { return mpf1(t, mpfr_j1); }
@@ -673,3 +782,246 @@ int mpscalblnl(struct t *t) { return mpldexpl(t); }
 int mplgamma_r(struct t *t) { return mplgamma(t); }
 int mplgammaf_r(struct t *t) { return mplgammaf(t); }
 int mplgammal_r(struct t *t) { return mplgammal(t); }
+
+int mpilogb(struct t *t)
+{
+       MPFR_DECL_INIT(mx, 53);
+
+       mpfr_set_d(mx, t->x, MPFR_RNDN);
+       t->i = mpfr_get_exp(mx) - 1;
+       t->e = 0;
+       if (isinf(t->x) || isnan(t->x) || t->x == 0)
+               t->e = INVALID;
+       return 0;
+}
+int mpilogbf(struct t *t)
+{
+       MPFR_DECL_INIT(mx, 24);
+
+       mpfr_set_flt(mx, t->x, MPFR_RNDN);
+       t->i = mpfr_get_exp(mx) - 1;
+       t->e = 0;
+       if (isinf(t->x) || isnan(t->x) || t->x == 0)
+               t->e = INVALID;
+       return 0;
+}
+int mpilogbl(struct t *t)
+{
+       MPFR_DECL_INIT(mx, 64);
+
+       mpfr_set_ld(mx, t->x, MPFR_RNDN);
+       t->i = mpfr_get_exp(mx) - 1;
+       t->e = 0;
+       if (isinf(t->x) || isnan(t->x) || t->x == 0)
+               t->e = INVALID;
+       return 0;
+}
+
+// TODO: ll* is hard to do with mpfr
+#define mp_f_i(n) \
+int mp##n(struct t *t) \
+{ \
+       setupfenv(t->r); \
+       t->i = n(t->x); \
+       t->e = getexcept(); \
+       return 0; \
+}
+
+mp_f_i(llrint)
+mp_f_i(llrintf)
+mp_f_i(llrintl)
+mp_f_i(lrint)
+mp_f_i(lrintf)
+mp_f_i(lrintl)
+mp_f_i(llround)
+mp_f_i(llroundf)
+mp_f_i(llroundl)
+mp_f_i(lround)
+mp_f_i(lroundf)
+mp_f_i(lroundl)
+
+int mpmodf(struct t *t)
+{
+       int e, r;
+
+       r = mpd1(t, wrap_trunc);
+       if (r)
+               return r;
+       t->y2 = t->y;
+       t->dy2 = t->dy;
+       e = t->e & ~INEXACT;
+       r = mpd1(t, mpfr_frac);
+       t->e |= e;
+       return r;
+}
+
+int mpmodff(struct t *t)
+{
+       int e, r;
+
+       r = mpf1(t, wrap_trunc);
+       if (r)
+               return r;
+       t->y2 = t->y;
+       t->dy2 = t->dy;
+       e = t->e & ~INEXACT;
+       r = mpf1(t, mpfr_frac);
+       t->e |= e;
+       return r;
+}
+
+int mpmodfl(struct t *t)
+{
+       int e, r;
+
+       r = mpl1(t, wrap_trunc);
+       if (r)
+               return r;
+       t->y2 = t->y;
+       t->dy2 = t->dy;
+       e = t->e & ~INEXACT;
+       r = mpl1(t, mpfr_frac);
+       t->e |= e;
+       return r;
+}
+
+int mpsincos(struct t *t)
+{
+       int e, r;
+
+       r = mpd1(t, mpfr_cos);
+       if (r)
+               return r;
+       t->y2 = t->y;
+       t->dy2 = t->dy;
+       e = t->e;
+       r = mpd1(t, mpfr_sin);
+       t->e |= e;
+       return r;
+}
+
+int mpsincosf(struct t *t)
+{
+       int e, r;
+
+       r = mpf1(t, mpfr_cos);
+       if (r)
+               return r;
+       t->y2 = t->y;
+       t->dy2 = t->dy;
+       e = t->e;
+       r = mpf1(t, mpfr_sin);
+       t->e |= e;
+       return r;
+}
+
+int mpsincosl(struct t *t)
+{
+       int e, r;
+
+       r = mpl1(t, mpfr_cos);
+       if (r)
+               return r;
+       t->y2 = t->y;
+       t->dy2 = t->dy;
+       e = t->e;
+       r = mpl1(t, mpfr_sin);
+       t->e |= e;
+       return r;
+}
+
+int mpremquo(struct t *t) { return mpd2(t, wrap_remquo) || (t->i = mpremquo_q, 0); }
+int mpremquof(struct t *t) { return mpf2(t, wrap_remquo) || (t->i = mpremquo_q, 0); }
+int mpremquol(struct t *t) { return mpl2(t, wrap_remquo) || (t->i = mpremquo_q, 0); }
+
+int mpfma(struct t *t)
+{
+       int tn;
+       int r = rmap(t->r);
+       MPFR_DECL_INIT(mx, 53);
+       MPFR_DECL_INIT(mx2, 53);
+       MPFR_DECL_INIT(mx3, 53);
+       MPFR_DECL_INIT(my, 128);
+
+       mpsetup();
+       mpfr_clear_flags();
+       mpfr_set_d(mx, t->x, MPFR_RNDN);
+       mpfr_set_d(mx2, t->x2, MPFR_RNDN);
+       mpfr_set_d(mx3, t->x3, MPFR_RNDN);
+       tn = mpfr_fma(my, mx, mx2, mx3, r);
+       gend(t, my, tn, r);
+       if ((t->e & INEXACT) && nextafter(t->y, 0) == 0) {
+               mpfr_set_emin(-(1<<20));
+               tn = mpfr_fma(my, mx, mx2, mx3, r);
+               mpfr_mul_2si(my, my, 1074, MPFR_RNDN);
+               t->dy = scalbnl(t->y, 1074) - mpfr_get_ld(my, r);
+               mpfr_set_emin(-1073);
+       }
+       return 0;
+}
+
+int mpfmaf(struct t *t)
+{
+       int tn;
+       int r = rmap(t->r);
+       MPFR_DECL_INIT(mx, 24);
+       MPFR_DECL_INIT(mx2, 24);
+       MPFR_DECL_INIT(mx3, 24);
+       MPFR_DECL_INIT(my, 128);
+
+       mpsetupf();
+       mpfr_clear_flags();
+       mpfr_set_flt(mx, t->x, MPFR_RNDN);
+       mpfr_set_flt(mx2, t->x2, MPFR_RNDN);
+       mpfr_set_flt(mx3, t->x3, MPFR_RNDN);
+       tn = mpfr_fma(my, mx, mx2, mx3, r);
+       genf(t, my, tn, r);
+       if ((t->e & INEXACT) && nextafterf(t->y, 0) == 0) {
+               mpfr_set_emin(-(1<<20));
+               tn = mpfr_fma(my, mx, mx2, mx3, r);
+               mpfr_mul_2si(my, my, 149, MPFR_RNDN);
+               t->dy = scalbnl(t->y, 149) - mpfr_get_ld(my, r);
+               mpfr_set_emin(-148);
+       }
+       return 0;
+}
+
+int mpfmal(struct t *t)
+{
+#if LDBL_MANT_DIG == 53
+       return mpfma(t);
+#elif LDBL_MANT_DIG == 64
+       int tn;
+       int r = rmap(t->r);
+       MPFR_DECL_INIT(mx, 64);
+       MPFR_DECL_INIT(mx2, 64);
+       MPFR_DECL_INIT(mx3, 64);
+       MPFR_DECL_INIT(my, 128);
+
+       mpsetupl();
+       mpfr_clear_flags();
+       mpfr_set_ld(mx, t->x, MPFR_RNDN);
+       mpfr_set_ld(mx2, t->x2, MPFR_RNDN);
+       mpfr_set_ld(mx3, t->x3, MPFR_RNDN);
+       tn = mpfr_fma(my, mx, mx2, mx3, r);
+       genl(t, my, tn, r);
+       if ((t->e & INEXACT) && nextafterl(t->y, 0) == 0) {
+               mpfr_set_emin(-(1<<20));
+               tn = mpfr_fma(my, mx, mx2, mx3, r);
+               mpfr_mul_2si(my, my, 16445, MPFR_RNDN);
+               t->dy = scalbnl(t->y, 16445) - mpfr_get_ld(my, r);
+               mpfr_set_emin(-16444);
+       }
+       return 0;
+#else
+       return -1;
+#endif
+}
+
+int mpjn(struct t *t) { mpbessel_n = t->i; return mpd1(t, wrap_jn); }
+int mpjnf(struct t *t) { mpbessel_n = t->i; return mpf1(t, wrap_jn); }
+int mpjnl(struct t *t) { mpbessel_n = t->i; return mpl1(t, wrap_jn); }
+int mpyn(struct t *t) { mpbessel_n = t->i; return mpd1(t, wrap_yn); }
+int mpynf(struct t *t) { mpbessel_n = t->i; return mpf1(t, wrap_yn); }
+int mpynl(struct t *t) { mpbessel_n = t->i; return mpl1(t, wrap_yn); }
+